]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - android-sdk/kernel-video.git/commitdiff
gc320: OMAP4: adding Vivante Corp 2/3D gcxxx driver files
authorDavid Sin <davidsin@ti.com>
Thu, 17 Jan 2013 16:00:33 +0000 (18:00 +0200)
committerPraneeth Bajjuri <praneeth@ti.com>
Fri, 12 Jul 2013 22:45:13 +0000 (17:45 -0500)
Driver files to enable Vivante Corp 2/3D gcxxx hardware.

Change-Id: Ic7593e603a2b078879aa7a99f6dcf8074ed6b47d
Signed-off-by: Alexei Shlychkov <x0177296@ti.com>
Signed-off-by: David Sin <davidsin@ti.com>
Signed-off-by: Volodymyr Mieshkov <volodymyr.mieshkov@ti.com>
Signed-off-by: Sundar Raman <sunds@ti.com>
39 files changed:
drivers/misc/gcx/gcbv/Kconfig [new file with mode: 0644]
drivers/misc/gcx/gcbv/Makefile [new file with mode: 0644]
drivers/misc/gcx/gcbv/gcblit.c [new file with mode: 0644]
drivers/misc/gcx/gcbv/gcbuffer.c [new file with mode: 0644]
drivers/misc/gcx/gcbv/gcbv.c [new file with mode: 0644]
drivers/misc/gcx/gcbv/gcbv.h [new file with mode: 0644]
drivers/misc/gcx/gcbv/gcbvdebug.c [new file with mode: 0644]
drivers/misc/gcx/gcbv/gcbvdebug.h [new file with mode: 0644]
drivers/misc/gcx/gcbv/gcfill.c [new file with mode: 0644]
drivers/misc/gcx/gcbv/gcfilter.c [new file with mode: 0644]
drivers/misc/gcx/gcbv/gcmain.c [new file with mode: 0644]
drivers/misc/gcx/gcbv/gcmain.h [new file with mode: 0644]
drivers/misc/gcx/gcbv/gcmap.c [new file with mode: 0644]
drivers/misc/gcx/gcbv/gcparser.c [new file with mode: 0644]
drivers/misc/gcx/gccore/Kconfig [new file with mode: 0644]
drivers/misc/gcx/gccore/Makefile [new file with mode: 0644]
drivers/misc/gcx/gccore/gcdbglog.c [new file with mode: 0644]
drivers/misc/gcx/gccore/gcdebug.c [new file with mode: 0644]
drivers/misc/gcx/gccore/gcmain.c [new file with mode: 0644]
drivers/misc/gcx/gccore/gcmain.h [new file with mode: 0644]
drivers/misc/gcx/gccore/gcmem.c [new file with mode: 0644]
drivers/misc/gcx/gccore/gcmem.h [new file with mode: 0644]
drivers/misc/gcx/gccore/gcmmu.c [new file with mode: 0644]
drivers/misc/gcx/gccore/gcmmu.h [new file with mode: 0644]
drivers/misc/gcx/gccore/gcqueue.c [new file with mode: 0644]
drivers/misc/gcx/gccore/gcqueue.h [new file with mode: 0644]
drivers/misc/gcx/gcioctl/Kconfig [new file with mode: 0644]
drivers/misc/gcx/gcioctl/Makefile [new file with mode: 0644]
drivers/misc/gcx/gcioctl/gcif.c [new file with mode: 0644]
drivers/misc/gcx/gcioctl/gcif.h [new file with mode: 0644]
drivers/misc/gcx/gcioctl/version.h [new file with mode: 0644]
include/linux/gcbv-iface.h [new file with mode: 0644]
include/linux/gccore.h [new file with mode: 0644]
include/linux/gcdbglog.h [new file with mode: 0644]
include/linux/gcdebug.h [new file with mode: 0644]
include/linux/gcerror.h [new file with mode: 0644]
include/linux/gcioctl.h [new file with mode: 0644]
include/linux/gcreg.h [new file with mode: 0644]
include/linux/gcx.h [new file with mode: 0644]

diff --git a/drivers/misc/gcx/gcbv/Kconfig b/drivers/misc/gcx/gcbv/Kconfig
new file mode 100644 (file)
index 0000000..4ab8ebe
--- /dev/null
@@ -0,0 +1,6 @@
+config GCBV
+       tristate "Vivante BLTsville library"
+       default y
+       depends on GCCORE
+       help
+           Vivante BLTsville library.
diff --git a/drivers/misc/gcx/gcbv/Makefile b/drivers/misc/gcx/gcbv/Makefile
new file mode 100644 (file)
index 0000000..f25080c
--- /dev/null
@@ -0,0 +1,12 @@
+obj-$(CONFIG_GCBV) += gcbv2d.o
+
+gcbv2d-y := \
+       gcmain.o \
+       gcbv.o \
+       gcparser.o \
+       gcmap.o \
+       gcbuffer.o \
+       gcfill.o \
+       gcblit.o \
+       gcfilter.o \
+       gcbvdebug.o
diff --git a/drivers/misc/gcx/gcbv/gcblit.c b/drivers/misc/gcx/gcbv/gcblit.c
new file mode 100644 (file)
index 0000000..54d493b
--- /dev/null
@@ -0,0 +1,783 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Vivante Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "gcbv.h"
+
+#define GCZONE_NONE            0
+#define GCZONE_ALL             (~0U)
+#define GCZONE_BLEND           (1 << 0)
+#define GCZONE_SURF            (1 << 1)
+#define GCZONE_BLIT            (1 << 2)
+
+GCDBG_FILTERDEF(blit, GCZONE_NONE,
+               "blend",
+               "surf",
+               "blit")
+
+
+static enum bverror do_blit_end(struct bvbltparams *bvbltparams,
+                               struct gcbatch *batch)
+{
+       enum bverror bverror;
+       struct gcblit *gcblit;
+       struct gcmobltconfig *gcmobltconfig;
+       struct gcmostartde *gcmostartde;
+
+       GCENTER(GCZONE_BLIT);
+
+       /* Get a shortcut to the operation specific data. */
+       gcblit = &batch->op.blit;
+
+       GCDBG(GCZONE_BLIT, "finalizing the blit, scrcount = %d\n",
+             gcblit->srccount);
+
+       /***********************************************************************
+        * Configure the operation.
+        */
+
+       /* Allocate command buffer. */
+       bverror = claim_buffer(bvbltparams, batch,
+                              sizeof(struct gcmobltconfig),
+                              (void **) &gcmobltconfig);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /* Configure multi-source control. */
+       gcmobltconfig->multisource_ldst = gcmobltconfig_multisource_ldst;
+       gcmobltconfig->multisource.raw = 0;
+       gcmobltconfig->multisource.reg.srccount = gcblit->srccount - 1;
+
+       GCDBG(GCZONE_BLIT, "blockenable = %d\n", gcblit->blockenable);
+       if (gcblit->blockenable) {
+               gcmobltconfig->multisource.reg.horblock
+                       = GCREG_DE_MULTI_SOURCE_HORIZONTAL_BLOCK_PIXEL16;
+               gcmobltconfig->multisource.reg.verblock
+                       = GCREG_DE_MULTI_SOURCE_VERTICAL_BLOCK_LINE64;
+       } else {
+               gcmobltconfig->multisource.reg.horblock
+                       = GCREG_DE_MULTI_SOURCE_HORIZONTAL_BLOCK_PIXEL128;
+               gcmobltconfig->multisource.reg.verblock
+                       = GCREG_DE_MULTI_SOURCE_VERTICAL_BLOCK_LINE1;
+       }
+
+       /* Set destination configuration. */
+       GCDBG(GCZONE_BLIT, "  swizzle code = %d\n", gcblit->swizzle);
+       GCDBG(GCZONE_BLIT, "  format code = %d\n", gcblit->format);
+
+       gcmobltconfig->dstconfig_ldst = gcmobltconfig_dstconfig_ldst;
+       gcmobltconfig->dstconfig.raw = 0;
+       gcmobltconfig->dstconfig.reg.swizzle = gcblit->swizzle;
+       gcmobltconfig->dstconfig.reg.format = gcblit->format;
+       gcmobltconfig->dstconfig.reg.endian = gcblit->endian;
+       gcmobltconfig->dstconfig.reg.command = gcblit->multisrc
+               ? GCREG_DEST_CONFIG_COMMAND_MULTI_SOURCE_BLT
+               : GCREG_DEST_CONFIG_COMMAND_BIT_BLT;
+
+       /***********************************************************************
+        * Start the operation.
+        */
+
+       /* Allocate command buffer. */
+       bverror = claim_buffer(bvbltparams, batch,
+                              sizeof(struct gcmostartde),
+                              (void **) &gcmostartde);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /* Set START_DE command. */
+       gcmostartde->startde.cmd.fld = gcfldstartde;
+
+       /* Set destination rectangle. */
+       gcmostartde->rect.left = gcblit->dstrect.left;
+       gcmostartde->rect.top = gcblit->dstrect.top;
+       gcmostartde->rect.right = gcblit->dstrect.right;
+       gcmostartde->rect.bottom = gcblit->dstrect.bottom;
+
+       GCDBG(GCZONE_BLIT, "dstrect = (%d,%d)-(%d,%d)\n",
+             gcmostartde->rect.left, gcmostartde->rect.top,
+             gcmostartde->rect.right, gcmostartde->rect.bottom);
+
+       /* Reset the finalizer. */
+       batch->batchend = do_end;
+
+       gcbv_debug_blt(gcblit->srccount,
+                      abs(gcblit->dstrect.right - gcblit->dstrect.left),
+                      abs(gcblit->dstrect.bottom - gcblit->dstrect.top));
+
+exit:
+       GCEXITARG(GCZONE_BLIT, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+enum bverror do_blit(struct bvbltparams *bvbltparams,
+                    struct gcbatch *batch,
+                    struct gcsurface *srcinfo)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gccontext *gccontext = get_context();
+
+       struct gcmosrc0 *gcmosrc0;
+       struct gcmosrc *gcmosrc;
+       struct gcblit *gcblit;
+
+       unsigned int index;
+       struct bvbuffmap *dstmap = NULL;
+       struct bvbuffmap *srcmap = NULL;
+
+       struct gcsurface *dstinfo;
+       int dstshiftX, dstshiftY;
+       int dstpixalign, dstbyteshift;
+       int dstoffsetX, dstoffsetY;
+
+       int adjust, srcshiftX, srcshiftY;
+       unsigned int physwidth, physheight;
+       bool orthogonal;
+       bool multisrc;
+       unsigned int batchfinalize;
+
+       int srcleftedge, srctopedge;
+       int dstleftedge, dsttopedge;
+
+       struct gcrect *srcorig, *srcclip, *srcadj;
+       struct gcrect *dstorig, *dstclip, *dstadj;
+
+       GCENTER(GCZONE_BLIT);
+
+       /* 3-plane source not supported. */
+       if ((srcinfo->format.type == BVFMT_YUV) &&
+           (srcinfo->format.cs.yuv.planecount == 3)) {
+               BVSETBLTERROR((srcinfo->index == 0)
+                                       ? BVERR_SRC1GEOM_FORMAT
+                                       : BVERR_SRC2GEOM_FORMAT,
+                             "unsupported source%d format.",
+                             srcinfo->index + 1);
+               goto exit;
+       }
+
+       /* Zero-fill for source is not supported. */
+       if (srcinfo->format.zerofill) {
+               BVSETBLTERROR((srcinfo->index == 0)
+                                       ? BVERR_SRC1GEOM_FORMAT
+                                       : BVERR_SRC2GEOM_FORMAT,
+                             "0 filling is not supported.");
+               goto exit;
+       }
+
+       /***********************************************************************
+        * Parse destination.
+        */
+
+       /* Get a shortcut to the destination surface. */
+       dstinfo = &batch->dstinfo;
+
+       /* Get destination rectangle shortcuts. */
+       dstorig = &dstinfo->rect.orig;
+       dstclip = &dstinfo->rect.clip;
+       dstadj = &dstinfo->rect.adj;
+
+       /* Parse destination parameters. */
+       bverror = parse_destination(bvbltparams, batch);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /* Ignore the blit if destination rectangle is empty. */
+       if (null_rect(dstclip)) {
+               GCDBG(GCZONE_SURF, "empty destination rectangle.\n");
+               goto exit;
+       }
+
+       /* Adjust surface angles if necessary. */
+       adjust_angle(srcinfo, dstinfo);
+
+
+       /***********************************************************************
+        * Determine source surface alignment offset.
+        */
+
+       /* Assume multi-source is possible. */
+       multisrc = true;
+
+       /* Assume no additional shift is necessary. */
+       srcshiftX = 0;
+       srcshiftY = 0;
+
+       /* Get source rectangle shortcuts. */
+       srcorig = &srcinfo->rect.orig;
+       srcclip = &srcinfo->rect.clip;
+       srcadj = &srcinfo->rect.adj;
+
+       /* Determine whether the source and the destination are orthogonal
+        * to each other. */
+       orthogonal = (srcinfo->angle % 2) != (dstinfo->angle % 2);
+
+       /* Compute clipped source rectangle. */
+       srcclip->left   = srcorig->left   + batch->clipdelta.left;
+       srcclip->top    = srcorig->top    + batch->clipdelta.top;
+       srcclip->right  = srcorig->right  + batch->clipdelta.right;
+       srcclip->bottom = srcorig->bottom + batch->clipdelta.bottom;
+       GCPRINT_RECT(GCZONE_SURF, "clipped source", srcclip);
+
+       /* Validate the source rectangle. */
+       if (!valid_rect(srcinfo, srcclip)) {
+               BVSETBLTERROR((srcinfo->index == 0)
+                                       ? BVERR_SRC1RECT
+                                       : BVERR_SRC2RECT,
+                             "invalid source rectangle.");
+               goto exit;
+       }
+
+       /* Ignore the blit if source rectangle is empty. */
+       if (null_rect(srcclip)) {
+               GCDBG(GCZONE_SURF, "empty source rectangle.\n");
+               goto exit;
+       }
+
+       /* Determine source and destination physical origin. */
+       switch (srcinfo->angle) {
+       case ROT_ANGLE_0:
+               srcleftedge = srcclip->left;
+               srctopedge  = srcclip->top;
+               dstleftedge = dstadj->left;
+               dsttopedge  = dstadj->top;
+               break;
+
+       case ROT_ANGLE_90:
+               srcleftedge = srcclip->top;
+               srctopedge  = srcinfo->width - srcclip->left;
+               dstleftedge = dstadj->top;
+               dsttopedge  = dstinfo->adjwidth - dstadj->left;
+               break;
+
+       case ROT_ANGLE_180:
+               srcleftedge = srcinfo->width - srcclip->left;
+               srctopedge  = srcinfo->height - srcclip->top;
+               dstleftedge = dstinfo->adjwidth - dstadj->left;
+               dsttopedge  = dstinfo->adjheight - dstadj->top;
+               break;
+
+       case ROT_ANGLE_270:
+               srcleftedge = srcinfo->height - srcclip->top;
+               srctopedge  = srcclip->left;
+               dstleftedge = dstinfo->adjheight - dstadj->top;
+               dsttopedge  = dstadj->left;
+               break;
+
+       default:
+               srcleftedge = 0;
+               srctopedge  = 0;
+               dstleftedge = 0;
+               dsttopedge  = 0;
+       }
+
+       /* Compute the source surface shift. */
+       srcinfo->xpixalign = srcleftedge - dstleftedge;
+       srcinfo->ypixalign = srctopedge  - dsttopedge;
+
+       /* Compute the source surface offset in bytes. */
+       srcinfo->bytealign1
+               = srcinfo->ypixalign * (int) srcinfo->stride1
+               + srcinfo->xpixalign * (int) srcinfo->format.bitspp / 8;
+
+       GCDBG(GCZONE_SURF, "source surface %d:\n", srcinfo->index + 1);
+       GCDBG(GCZONE_SURF, "  surface offset (pixels) = %d,%d\n",
+             srcinfo->xpixalign, srcinfo->ypixalign);
+       GCDBG(GCZONE_SURF, "  surface offset (bytes) = 0x%08X\n",
+             srcinfo->bytealign1);
+
+       /* Compute the source offset in pixels needed to compensate
+        * for the surface base address misalignment if any. */
+       adjust = get_pixel_offset(srcinfo, srcinfo->bytealign1);
+
+       /* Account for the newly created misalignment if any. */
+       srcinfo->bytealign1 += adjust * (int) srcinfo->format.bitspp / 8;
+       srcinfo->xpixalign += adjust;
+       srcshiftX += adjust;
+
+       GCDBG(GCZONE_SURF, "  horizontal alignment adjustment (pixels) = %d\n",
+             adjust);
+       GCDBG(GCZONE_SURF, "  adjusted surface offset (pixels) = %d,%d\n",
+             srcinfo->xpixalign, srcinfo->ypixalign);
+       GCDBG(GCZONE_SURF, "  additional surface offset (pixels) = %d,%d\n",
+             srcshiftX, srcshiftY);
+       GCDBG(GCZONE_SURF, "  adjusted surface offset (bytes) = 0x%08X\n",
+             srcinfo->bytealign1);
+
+       /* Compute U/V plane offsets. */
+       if ((srcinfo->format.type == BVFMT_YUV) &&
+           (srcinfo->format.cs.yuv.planecount > 1))
+               set_computeyuv(srcinfo, srcinfo->xpixalign, srcinfo->ypixalign);
+
+       /* Set precomputed destination adjustments based on the destination
+        * base address misalignment only. */
+       dstshiftX = dstinfo->xpixalign;
+       dstshiftY = dstinfo->ypixalign;
+
+       /* Apply source adjustemnts. */
+       if (srcinfo->angle == dstinfo->angle) {
+               dstshiftX += srcshiftX;
+               dstshiftY += srcshiftY;
+       } else if (((srcinfo->angle + 3) % 4) == dstinfo->angle) {
+               dstshiftY += srcshiftX;
+       } else if (((srcinfo->angle + 1) % 4) == dstinfo->angle) {
+               dstshiftX += srcshiftY;
+       }
+
+       /* Compute the destination surface offset in bytes. */
+       dstbyteshift = dstshiftY * (int) dstinfo->stride1
+                    + dstshiftX * (int) dstinfo->format.bitspp / 8;
+
+       /* Compute the destination offset in pixels needed to compensate
+        * for the surface base address misalignment if any. If dstpixalign
+        * comes out anything other than zero, multisource blit cannot be
+        * performed. */
+       dstpixalign = get_pixel_offset(dstinfo, dstbyteshift);
+       if (dstpixalign != 0) {
+               GCDBG(GCZONE_SURF,
+                     "  disabling multi-source, "
+                     "destination needs to be realigned again.\n");
+               multisrc = false;
+       }
+
+       GCDBG(GCZONE_SURF, "destination surface:\n");
+       GCDBG(GCZONE_SURF, "  surface offset (pixels) = %d,%d\n",
+             dstshiftX, dstshiftY);
+       GCDBG(GCZONE_SURF, "  surface offset (bytes) = 0x%08X\n",
+             dstbyteshift);
+       GCDBG(GCZONE_SURF, "  realignment = %d\n",
+             dstpixalign);
+
+       if (multisrc) {
+               GCDBG(GCZONE_SURF, "multi-source enabled.\n");
+
+               /* Source origin is not used in multi-source setup. */
+               srcadj->left = 0;
+               srcadj->top = 0;
+
+               /* Set new surface shift. */
+               if (dstinfo->bytealign1 != dstbyteshift) {
+                       GCDBG(GCZONE_SURF,
+                             "destination alignment changed.\n");
+                       dstinfo->bytealign1 = dstbyteshift;
+                       dstinfo->surfdirty = true;
+               }
+
+               /* Adjust the destination to match the source geometry. */
+               switch (srcinfo->angle) {
+               case ROT_ANGLE_0:
+                       /* Adjust the destination horizontally. */
+                       dstoffsetX = srcshiftX;
+                       dstoffsetY = srcshiftY;
+
+                       /* Apply the source alignment. */
+                       if ((dstinfo->angle % 2) == 0) {
+                               physwidth  = dstinfo->physwidth  - srcshiftX;
+                               physheight = dstinfo->physheight - srcshiftY;
+                       } else {
+                               physwidth  = dstinfo->physwidth  - srcshiftY;
+                               physheight = dstinfo->physheight - srcshiftX;
+                       }
+                       break;
+
+               case ROT_ANGLE_90:
+                       /* Adjust the destination vertically. */
+                       dstoffsetX = srcshiftY;
+                       dstoffsetY = srcshiftX;
+
+                       /* Apply the source alignment. */
+                       if ((dstinfo->angle % 2) == 0) {
+                               physwidth  = dstinfo->physwidth  - srcshiftY;
+                               physheight = dstinfo->physheight - srcshiftX;
+                       } else {
+                               physwidth  = dstinfo->physwidth  - srcshiftX;
+                               physheight = dstinfo->physheight - srcshiftY;
+                       }
+                       break;
+
+               case ROT_ANGLE_180:
+                       /* No adjustment necessary. */
+                       dstoffsetX = 0;
+                       dstoffsetY = 0;
+
+                       /* Apply the source alignment. */
+                       if ((dstinfo->angle % 2) == 0) {
+                               physwidth  = dstinfo->physwidth  - srcshiftX;
+                               physheight = dstinfo->physheight - srcshiftY;
+                       } else {
+                               physwidth  = dstinfo->physwidth  - srcshiftY;
+                               physheight = dstinfo->physheight - srcshiftX;
+                       }
+                       break;
+
+               case ROT_ANGLE_270:
+                       /* No adjustment necessary. */
+                       dstoffsetX = 0;
+                       dstoffsetY = 0;
+
+                       /* Apply the source alignment. */
+                       if ((dstinfo->angle % 2) == 0) {
+                               physwidth  = dstinfo->physwidth  - srcshiftY;
+                               physheight = dstinfo->physheight - srcshiftX;
+                       } else {
+                               physwidth  = dstinfo->physwidth  - srcshiftX;
+                               physheight = dstinfo->physheight - srcshiftY;
+                       }
+                       break;
+
+               default:
+                       physwidth = 0;
+                       physheight = 0;
+                       dstoffsetX = 0;
+                       dstoffsetY = 0;
+               }
+
+               /* Source geometry is now the same as the destination. */
+               if (orthogonal) {
+                       srcinfo->physwidth  = physheight;
+                       srcinfo->physheight = physwidth;
+               } else {
+                       srcinfo->physwidth  = physwidth;
+                       srcinfo->physheight = physheight;
+               }
+
+               /* Set new surface size. */
+               if ((physwidth  != dstinfo->physwidth) ||
+                   (physheight != dstinfo->physheight)) {
+                       dstinfo->physwidth  = physwidth;
+                       dstinfo->physheight = physheight;
+                       dstinfo->surfdirty = true;
+               }
+
+               /* Set new offset. */
+               if ((batch->dstoffsetX != dstoffsetX) ||
+                   (batch->dstoffsetY != dstoffsetY)) {
+                       batch->dstoffsetX = dstoffsetX;
+                       batch->dstoffsetY = dstoffsetY;
+                       dstinfo->surfdirty = true;
+               }
+       } else {
+               GCDBG(GCZONE_SURF, "multi-source disabled.\n");
+
+               /* Determine surface size and render rectangle. */
+               process_rotation(srcinfo);
+
+               /* No adjustment necessary for single-source. */
+               dstoffsetX = 0;
+               dstoffsetY = 0;
+       }
+
+       batchfinalize = 0;
+
+       /* Reached maximum number of sources? */
+       if (batch->op.blit.srccount == gccontext->gccaps.maxsource)
+               batchfinalize |= GCBV_BATCH_FINALIZE_SRCCOUNT;
+
+       /* Previous operation was not blit? */
+       if (batch->batchend != do_blit_end)
+               batchfinalize |= GCBV_BATCH_FINALIZE_OPERATION;
+
+       /* Previous blit was not multi-sourced? */
+       else if (!batch->op.blit.multisrc)
+               batchfinalize |= GCBV_BATCH_FINALIZE_MULTISRC;
+
+       /* Current blit is not multi-sourced? */
+       if (!multisrc)
+               batchfinalize |= GCBV_BATCH_FINALIZE_ALIGN;
+
+       /* Destination has changed? */
+       if (dstinfo->surfdirty)
+               batchfinalize |= GCBV_BATCH_FINALIZE_FLAGS_DST;
+
+       if (dstinfo->cliprectdirty)
+               batchfinalize |= GCBV_BATCH_FINALIZE_FLAGS_CLIPRECT;
+
+       if (dstinfo->destrectdirty)
+               batchfinalize |= GCBV_BATCH_FINALIZE_FLAGS_DESTRECT;
+
+       /* Check if we need to finalize existing batch. */
+       if (batchfinalize) {
+               if (batch->batchend == do_blit_end)
+                       gcbv_debug_finalize_batch(batchfinalize);
+
+               /* Finalize existing batch if any. */
+               bverror = batch->batchend(bvbltparams, batch);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Blit batch. */
+               batch->batchend = do_blit_end;
+
+               /* Initialize the new batch. */
+               gcblit = &batch->op.blit;
+               gcblit->blockenable = false;
+               gcblit->srccount = 0;
+               gcblit->multisrc = multisrc;
+
+               /* Set the destination format. */
+               gcblit->format  = dstinfo->format.format;
+               gcblit->swizzle = dstinfo->format.swizzle;
+               gcblit->endian = dstinfo->format.endian;
+
+               /* Set the destination coordinates. */
+               gcblit->dstrect.left   = dstadj->left   - dstoffsetX;
+               gcblit->dstrect.top    = dstadj->top    - dstoffsetY;
+               gcblit->dstrect.right  = dstadj->right  - dstoffsetX;
+               gcblit->dstrect.bottom = dstadj->bottom - dstoffsetY;
+       }
+
+       if (dstinfo->surfdirty) {
+               /* Map the destination. */
+               bverror = do_map(dstinfo->buf.desc, batch, &dstmap);
+               if (bverror != BVERR_NONE) {
+                       bvbltparams->errdesc = gccontext->bverrorstr;
+                       goto exit;
+               }
+
+               /* Set the new destination. */
+               bverror = set_dst(bvbltparams, batch, dstmap);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+       }
+
+       /* Map the source. */
+       bverror = do_map(srcinfo->buf.desc, batch, &srcmap);
+       if (bverror != BVERR_NONE) {
+               bvbltparams->errdesc = gccontext->bverrorstr;
+               goto exit;
+       }
+
+       /***********************************************************************
+       ** Configure source.
+       */
+
+       /* We need to walk in blocks if the source and the destination
+        * surfaces are orthogonal to each other. */
+       if (orthogonal)
+               batch->op.blit.blockenable = true;
+
+       /* Shortcut to the register index. */
+       index = batch->op.blit.srccount;
+
+       /* Set surface parameters. */
+       if (index == 0) {
+               /* Allocate command buffer. */
+               bverror = claim_buffer(bvbltparams, batch,
+                                      sizeof(struct gcmosrc0),
+                                      (void **) &gcmosrc0);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               add_fixup(bvbltparams, batch, &gcmosrc0->address,
+                         srcinfo->bytealign1);
+
+               gcmosrc0->config_ldst = gcmosrc0_config_ldst;
+               gcmosrc0->address = GET_MAP_HANDLE(srcmap);
+               gcmosrc0->stride = srcinfo->stride1;
+               gcmosrc0->rotation.raw = 0;
+               gcmosrc0->rotation.reg.surf_width = srcinfo->physwidth;
+               gcmosrc0->config.raw = 0;
+               gcmosrc0->config.reg.swizzle = srcinfo->format.swizzle;
+               gcmosrc0->config.reg.format = srcinfo->format.format;
+               gcmosrc0->config.reg.endian = srcinfo->format.endian;
+               gcmosrc0->origin.reg.x = srcadj->left;
+               gcmosrc0->origin.reg.y = srcadj->top;
+               gcmosrc0->size.reg = gcregsrcsize_max;
+
+               gcmosrc0->rotation_ldst = gcmosrc0_rotation_ldst;
+               gcmosrc0->rotationheight.reg.height = srcinfo->physheight;
+               gcmosrc0->rotationangle.raw = 0;
+               gcmosrc0->rotationangle.reg.src = rotencoding[srcinfo->angle];
+               gcmosrc0->rotationangle.reg.dst = rotencoding[dstinfo->angle];
+               gcmosrc0->rotationangle.reg.src_mirror = srcinfo->mirror;
+               gcmosrc0->rotationangle.reg.dst_mirror = GCREG_MIRROR_NONE;
+
+               gcmosrc0->rop_ldst = gcmosrc0_rop_ldst;
+               gcmosrc0->rop.raw = 0;
+               gcmosrc0->rop.reg.type = GCREG_ROP_TYPE_ROP3;
+               gcmosrc0->rop.reg.fg = (unsigned char) srcinfo->rop;
+
+               gcmosrc0->mult_ldst = gcmosrc0_mult_ldst;
+               gcmosrc0->mult.raw = 0;
+               gcmosrc0->mult.reg.srcglobalpremul = srcinfo->srcglobalpremul;
+
+               if (srcinfo->format.premultiplied)
+                       gcmosrc0->mult.reg.srcpremul
+                       = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_DISABLE;
+               else
+                       gcmosrc0->mult.reg.srcpremul
+                       = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_ENABLE;
+
+               if (dstinfo->format.premultiplied) {
+                       gcmosrc0->mult.reg.dstpremul
+                       = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_DISABLE;
+
+                       gcmosrc0->mult.reg.dstdemul
+                       = GCREG_COLOR_MULTIPLY_MODES_DST_DEMULTIPLY_DISABLE;
+               } else {
+                       gcmosrc0->mult.reg.dstpremul
+                       = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_ENABLE;
+
+                       gcmosrc0->mult.reg.dstdemul
+                       = GCREG_COLOR_MULTIPLY_MODES_DST_DEMULTIPLY_ENABLE;
+               }
+
+               /* Program blending. */
+               bverror = set_blending(bvbltparams, batch, srcinfo);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Program YUV source. */
+               if (srcinfo->format.type == BVFMT_YUV) {
+                       bverror = set_yuvsrc(bvbltparams, batch,
+                                            srcinfo, srcmap);
+                       if (bverror != BVERR_NONE)
+                               goto exit;
+               }
+       } else {
+               /* Allocate command buffer. */
+               bverror = claim_buffer(bvbltparams, batch,
+                                      sizeof(struct gcmosrc),
+                                      (void **) &gcmosrc);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               add_fixup(bvbltparams, batch, &gcmosrc->address,
+                         srcinfo->bytealign1);
+
+               gcmosrc->address_ldst = gcmosrc_address_ldst[index];
+               gcmosrc->address = GET_MAP_HANDLE(srcmap);
+               gcmosrc->stride_ldst = gcmosrc_stride_ldst[index];
+               gcmosrc->stride = srcinfo->stride1;
+
+               gcmosrc->rotation_ldst = gcmosrc_rotation_ldst[index];
+               gcmosrc->rotation.raw = 0;
+               gcmosrc->rotation.reg.surf_width = srcinfo->physwidth;
+
+               gcmosrc->config_ldst = gcmosrc_config_ldst[index];
+               gcmosrc->config.raw = 0;
+               gcmosrc->config.reg.swizzle = srcinfo->format.swizzle;
+               gcmosrc->config.reg.format = srcinfo->format.format;
+               gcmosrc->config.reg.endian = srcinfo->format.endian;
+
+               gcmosrc->origin_ldst = gcmosrc_origin_ldst[index];
+               gcmosrc->origin.reg.x = srcadj->left;
+               gcmosrc->origin.reg.y = srcadj->top;
+
+               gcmosrc->size_ldst = gcmosrc_size_ldst[index];
+               gcmosrc->size.reg = gcregsrcsize_max;
+
+               gcmosrc->rotationheight_ldst
+                       = gcmosrc_rotationheight_ldst[index];
+               gcmosrc->rotationheight.reg.height = srcinfo->physheight;
+
+               gcmosrc->rotationangle_ldst
+                       = gcmosrc_rotationangle_ldst[index];
+               gcmosrc->rotationangle.raw = 0;
+               gcmosrc->rotationangle.reg.src = rotencoding[srcinfo->angle];
+               gcmosrc->rotationangle.reg.dst = rotencoding[dstinfo->angle];
+               gcmosrc->rotationangle.reg.src_mirror = srcinfo->mirror;
+               gcmosrc->rotationangle.reg.dst_mirror = GCREG_MIRROR_NONE;
+
+               gcmosrc->rop_ldst = gcmosrc_rop_ldst[index];
+               gcmosrc->rop.raw = 0;
+               gcmosrc->rop.reg.type = GCREG_ROP_TYPE_ROP3;
+               gcmosrc->rop.reg.fg = (unsigned char) srcinfo->rop;
+
+               gcmosrc->mult_ldst = gcmosrc_mult_ldst[index];
+               gcmosrc->mult.raw = 0;
+               gcmosrc->mult.reg.srcglobalpremul = srcinfo->srcglobalpremul;
+
+               if (srcinfo->format.premultiplied)
+                       gcmosrc->mult.reg.srcpremul
+                       = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_DISABLE;
+               else
+                       gcmosrc->mult.reg.srcpremul
+                       = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_ENABLE;
+
+               if (dstinfo->format.premultiplied) {
+                       gcmosrc->mult.reg.dstpremul
+                       = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_DISABLE;
+
+                       gcmosrc->mult.reg.dstdemul
+                       = GCREG_COLOR_MULTIPLY_MODES_DST_DEMULTIPLY_DISABLE;
+               } else {
+                       gcmosrc->mult.reg.dstpremul
+                       = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_ENABLE;
+
+                       gcmosrc->mult.reg.dstdemul
+                       = GCREG_COLOR_MULTIPLY_MODES_DST_DEMULTIPLY_ENABLE;
+               }
+
+               /* Program blending. */
+               bverror = set_blending_index(bvbltparams, batch,
+                                            srcinfo, index);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Program YUV source. */
+               if (srcinfo->format.type == BVFMT_YUV) {
+                       bverror = set_yuvsrc_index(bvbltparams, batch,
+                                                  srcinfo, srcmap, index);
+                       if (bverror != BVERR_NONE)
+                               goto exit;
+               }
+       }
+
+       batch->op.blit.srccount += 1;
+
+exit:
+       GCEXITARG(GCZONE_BLIT, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
diff --git a/drivers/misc/gcx/gcbv/gcbuffer.c b/drivers/misc/gcx/gcbv/gcbuffer.c
new file mode 100644 (file)
index 0000000..1d1fde7
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Vivante Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "gcbv.h"
+
+#define GCZONE_NONE            0
+#define GCZONE_ALL             (~0U)
+#define GCZONE_BATCH_ALLOC     (1 << 0)
+#define GCZONE_BUFFER_ALLOC    (1 << 1)
+#define GCZONE_FIXUP_ALLOC     (1 << 2)
+#define GCZONE_FIXUP           (1 << 3)
+
+GCDBG_FILTERDEF(buffer, GCZONE_NONE,
+               "batchalloc",
+               "bufferalloc"
+               "fixupalloc",
+               "fixup")
+
+
+/*******************************************************************************
+** Miscellaneous defines and macros.
+*/
+
+#define GC_BUFFER_INIT_SIZE \
+( \
+       GC_BUFFER_SIZE - max(sizeof(struct gcbuffer), GC_BUFFER_RESERVE) \
+)
+
+#define GC_BUFFER_RESERVE \
+( \
+       sizeof(struct gcmopipesel) + \
+       sizeof(struct gcmommumaster) + \
+       sizeof(struct gcmommuflush) + \
+       sizeof(struct gcmosignal) + \
+       sizeof(struct gccmdend) \
+)
+
+
+/*******************************************************************************
+ * Batch/command buffer management.
+ */
+
+enum bverror do_end(struct bvbltparams *bvbltparams,
+                   struct gcbatch *gcbatch)
+{
+       return BVERR_NONE;
+}
+
+enum bverror allocate_batch(struct bvbltparams *bvbltparams,
+                           struct gcbatch **gcbatch)
+{
+       enum bverror bverror;
+       struct gccontext *gccontext = get_context();
+       struct gcbatch *temp;
+       struct gcbuffer *gcbuffer;
+
+       GCENTER(GCZONE_BATCH_ALLOC);
+
+       /* Lock access to batch management. */
+       GCLOCK(&gccontext->batchlock);
+
+       if (list_empty(&gccontext->batchvac)) {
+               temp = gcalloc(struct gcbatch, sizeof(struct gcbatch));
+               if (temp == NULL) {
+                       BVSETBLTERROR(BVERR_OOM,
+                                     "batch header allocation failed");
+                       goto exit;
+               }
+
+               GCDBG(GCZONE_BATCH_ALLOC, "allocated new batch = 0x%08X\n",
+                     (unsigned int) temp);
+       } else {
+               struct list_head *head;
+               head = gccontext->batchvac.next;
+               temp = list_entry(head, struct gcbatch, link);
+               list_del(head);
+
+               GCDBG(GCZONE_BATCH_ALLOC, "reusing batch = 0x%08X\n",
+                     (unsigned int) temp);
+       }
+
+       memset(temp, 0, sizeof(struct gcbatch));
+       temp->structsize = sizeof(struct gcbatch);
+       temp->batchend = do_end;
+       INIT_LIST_HEAD(&temp->buffer);
+       INIT_LIST_HEAD(&temp->unmap);
+       INIT_LIST_HEAD(&temp->link);
+
+       bverror = append_buffer(bvbltparams, temp, &gcbuffer);
+       if (bverror != BVERR_NONE) {
+               free_batch(temp);
+               goto exit;
+       }
+
+       *gcbatch = temp;
+
+       GCDBG(GCZONE_BATCH_ALLOC, "batch allocated = 0x%08X\n",
+             (unsigned int) temp);
+
+exit:
+       /* Unlock access to batch management. */
+       GCUNLOCK(&gccontext->batchlock);
+
+       GCEXITARG(GCZONE_BATCH_ALLOC, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+void free_batch(struct gcbatch *gcbatch)
+{
+       struct list_head *head;
+       struct gccontext *gccontext = get_context();
+       struct gcbuffer *gcbuffer;
+
+       GCENTERARG(GCZONE_BATCH_ALLOC, "batch = 0x%08X\n",
+                  (unsigned int) gcbatch);
+
+       /* Lock access. */
+       GCLOCK(&gccontext->batchlock);
+       GCLOCK(&gccontext->bufferlock);
+       GCLOCK(&gccontext->fixuplock);
+       GCLOCK(&gccontext->maplock);
+
+       /* Free implicit unmappings. */
+       list_splice_init(&gcbatch->unmap, &gccontext->unmapvac);
+
+       /* Free command buffers. */
+       while (!list_empty(&gcbatch->buffer)) {
+               head = gcbatch->buffer.next;
+               gcbuffer = list_entry(head, struct gcbuffer, link);
+
+               /* Free fixups. */
+               list_splice_init(&gcbuffer->fixup, &gccontext->fixupvac);
+
+               /* Free the command buffer. */
+               list_move(&gcbuffer->link, &gccontext->buffervac);
+       }
+
+       /* Free the batch. */
+       list_add(&gcbatch->link, &gccontext->batchvac);
+
+       /* Unlock access. */
+       GCUNLOCK(&gccontext->maplock);
+       GCUNLOCK(&gccontext->fixuplock);
+       GCUNLOCK(&gccontext->bufferlock);
+       GCUNLOCK(&gccontext->batchlock);
+
+       GCEXIT(GCZONE_BATCH_ALLOC);
+}
+
+enum bverror append_buffer(struct bvbltparams *bvbltparams,
+                          struct gcbatch *gcbatch,
+                          struct gcbuffer **gcbuffer)
+{
+       enum bverror bverror;
+       struct gccontext *gccontext = get_context();
+       struct gcbuffer *temp;
+
+       GCENTERARG(GCZONE_BUFFER_ALLOC, "batch = 0x%08X\n",
+                  (unsigned int) gcbatch);
+
+       /* Lock access to buffer management. */
+       GCLOCK(&gccontext->bufferlock);
+
+       if (list_empty(&gccontext->buffervac)) {
+               temp = gcalloc(struct gcbuffer, GC_BUFFER_SIZE);
+               if (temp == NULL) {
+                       BVSETBLTERROR(BVERR_OOM,
+                                     "command buffer allocation failed");
+                       goto exit;
+               }
+
+               list_add_tail(&temp->link, &gcbatch->buffer);
+
+               GCDBG(GCZONE_BUFFER_ALLOC, "allocated new buffer = 0x%08X\n",
+                     (unsigned int) temp);
+       } else {
+               struct list_head *head;
+               head = gccontext->buffervac.next;
+               temp = list_entry(head, struct gcbuffer, link);
+
+               list_move_tail(&temp->link, &gcbatch->buffer);
+
+               GCDBG(GCZONE_BUFFER_ALLOC, "reusing buffer = 0x%08X\n",
+                     (unsigned int) temp);
+       }
+
+       INIT_LIST_HEAD(&temp->fixup);
+       temp->pixelcount = 0;
+       temp->head = temp->tail = (unsigned int *) (temp + 1);
+       temp->available = GC_BUFFER_INIT_SIZE;
+
+       GCDBG(GCZONE_BUFFER_ALLOC, "new buffer appended = 0x%08X\n",
+             (unsigned int) temp);
+
+       *gcbuffer = temp;
+       bverror = BVERR_NONE;
+
+exit:
+       /* Unlock access to buffer management. */
+       GCUNLOCK(&gccontext->bufferlock);
+
+       GCEXITARG(GCZONE_BUFFER_ALLOC, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+static enum bverror allocate_fixup(struct bvbltparams *bvbltparams,
+                                  struct gcbuffer *gcbuffer,
+                                  struct gcfixup **gcfixup)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gccontext *gccontext = get_context();
+       struct gcfixup *temp;
+
+       if (list_empty(&gccontext->fixupvac)) {
+               temp = gcalloc(struct gcfixup, sizeof(struct gcfixup));
+               if (temp == NULL) {
+                       BVSETBLTERROR(BVERR_OOM, "fixup allocation failed");
+                       goto exit;
+               }
+
+               list_add_tail(&temp->link, &gcbuffer->fixup);
+
+               GCDBG(GCZONE_FIXUP_ALLOC,
+                     "new fixup struct allocated = 0x%08X\n",
+                     (unsigned int) temp);
+       } else {
+               struct list_head *head;
+               head = gccontext->fixupvac.next;
+               temp = list_entry(head, struct gcfixup, link);
+
+               list_move_tail(&temp->link, &gcbuffer->fixup);
+
+               GCDBG(GCZONE_FIXUP_ALLOC, "fixup struct reused = 0x%08X\n",
+                       (unsigned int) temp);
+       }
+
+       temp->count = 0;
+       *gcfixup = temp;
+
+exit:
+       return bverror;
+}
+
+enum bverror add_fixup(struct bvbltparams *bvbltparams,
+                      struct gcbatch *gcbatch,
+                      unsigned int *ptr,
+                      unsigned int surfoffset)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gccontext *gccontext = get_context();
+       struct list_head *head;
+       struct gcbuffer *buffer;
+       struct gcfixup *gcfixup;
+
+       GCENTERARG(GCZONE_FIXUP, "batch = 0x%08X, fixup ptr = 0x%08X\n",
+                  (unsigned int) gcbatch, (unsigned int) ptr);
+
+       /* Lock access to fixup management. */
+       GCLOCK(&gccontext->fixuplock);
+
+       /* Get the current command buffer. */
+       if (list_empty(&gcbatch->buffer)) {
+               GCERR("no command buffers are allocated");
+               goto exit;
+       }
+       head = gcbatch->buffer.prev;
+       buffer = list_entry(head, struct gcbuffer, link);
+
+       /* No fixups? Allocate one. */
+       if (list_empty(&buffer->fixup)) {
+               GCDBG(GCZONE_FIXUP_ALLOC, "no fixups allocated.\n");
+               bverror = allocate_fixup(bvbltparams, buffer, &gcfixup);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+       } else {
+               /* Get the current fixup. */
+               head = buffer->fixup.prev;
+               gcfixup = list_entry(head, struct gcfixup, link);
+
+               /* No more room? */
+               if (gcfixup->count == GC_FIXUP_MAX) {
+                       GCDBG(GCZONE_FIXUP_ALLOC,
+                             "out of room, allocating new.\n");
+                       bverror = allocate_fixup(bvbltparams, buffer, &gcfixup);
+                       if (bverror != BVERR_NONE)
+                               goto exit;
+               }
+       }
+
+       GCDBG(GCZONE_FIXUP, "buffer = 0x%08X, fixup struct = 0x%08X\n",
+             (unsigned int) buffer, (unsigned int) gcfixup);
+
+       gcfixup->fixup[gcfixup->count].dataoffset = ptr - buffer->head;
+       gcfixup->fixup[gcfixup->count].surfoffset = surfoffset;
+       gcfixup->count += 1;
+
+       GCDBG(GCZONE_FIXUP, "fixup offset = 0x%08X\n", ptr - buffer->head);
+       GCDBG(GCZONE_FIXUP, "surface offset = 0x%08X\n", surfoffset);
+
+exit:
+       /* Unlock access to fixup management. */
+       GCUNLOCK(&gccontext->fixuplock);
+
+       GCEXITARG(GCZONE_FIXUP, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+enum bverror claim_buffer(struct bvbltparams *bvbltparams,
+                         struct gcbatch *gcbatch,
+                         unsigned int size,
+                         void **buffer)
+{
+       enum bverror bverror;
+       struct list_head *head;
+       struct gcbuffer *gcbuffer;
+
+       GCENTERARG(GCZONE_BUFFER_ALLOC, "batch = 0x%08X, size = %d\n",
+                  (unsigned int) gcbatch, size);
+
+       if (size > GC_BUFFER_INIT_SIZE) {
+               GCERR("requested size is too big.\n");
+               BVSETBLTERROR(BVERR_OOM,
+                             "command buffer allocation failed");
+               goto exit;
+       }
+
+       /* Get the current command buffer. */
+       head = gcbatch->buffer.prev;
+       gcbuffer = list_entry(head, struct gcbuffer, link);
+
+       GCDBG(GCZONE_BUFFER_ALLOC, "buffer = 0x%08X, available = %d\n",
+             (unsigned int) gcbuffer, gcbuffer->available);
+
+       if (gcbuffer->available < size) {
+               bverror = append_buffer(bvbltparams, gcbatch, &gcbuffer);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+       }
+
+       *buffer = gcbuffer->tail;
+       gcbuffer->tail = (unsigned int *)
+                       ((unsigned char *) gcbuffer->tail + size);
+       gcbuffer->available -= size;
+       gcbatch->size += size;
+       bverror = BVERR_NONE;
+
+exit:
+       GCEXITARG(GCZONE_BUFFER_ALLOC, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
diff --git a/drivers/misc/gcx/gcbv/gcbv.c b/drivers/misc/gcx/gcbv/gcbv.c
new file mode 100644 (file)
index 0000000..de4d074
--- /dev/null
@@ -0,0 +1,2178 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Vivante Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "gcbv.h"
+
+#define GCZONE_NONE            0
+#define GCZONE_ALL             (~0U)
+#define GCZONE_INIT            (1 << 0)
+#define GCZONE_MAPPING         (1 << 1)
+#define GCZONE_BUFFER          (1 << 2)
+#define GCZONE_DEST            (1 << 3)
+#define GCZONE_SRC             (1 << 4)
+#define GCZONE_MASK            (1 << 5)
+#define GCZONE_BATCH           (1 << 6)
+#define GCZONE_BLIT            (1 << 7)
+#define GCZONE_CACHE           (1 << 8)
+#define GCZONE_CALLBACK                (1 << 9)
+#define GCZONE_TEMP            (1 << 10)
+#define GCZONE_BLEND           (1 << 11)
+
+GCDBG_FILTERDEF(bv, GCZONE_NONE,
+               "init",
+               "mapping",
+               "buffer",
+               "dest",
+               "source",
+               "mask",
+               "batch",
+               "blit",
+               "cache",
+               "callback",
+               "tempbuffer",
+               "blending")
+
+
+/*******************************************************************************
+** Global driver data access.
+*/
+
+struct gccontext *get_context(void)
+{
+       static struct gccontext gccontext;
+       return &gccontext;
+}
+
+
+/*******************************************************************************
+ * Debugging.
+ */
+
+#if GCDEBUG_ENABLE
+#define GCDUMPBATCH(batch) \
+       dumpbatch(batch)
+
+#define GCVERIFYBATCH(changeflags, prevrect, currrect) \
+       verify_batch(changeflags, prevrect, currrect)
+
+static void dumpbatch(struct gcbatch *gcbatch)
+{
+       struct list_head *gcbufferhead;
+       struct gcbuffer *gcbuffer;
+       struct list_head *gcfixuphead;
+       struct gcfixup *gcfixup;
+       unsigned int i, size;
+
+       if ((GCDBGFILTER.zone & (GCZONE_BUFFER)) == 0)
+               return;
+
+       GCDBG(GCZONE_BUFFER, "BATCH DUMP (0x%08X)\n",
+               (unsigned int) gcbatch);
+
+       list_for_each(gcbufferhead, &gcbatch->buffer) {
+               gcbuffer = list_entry(gcbufferhead, struct gcbuffer, link);
+
+               list_for_each(gcfixuphead, &gcbuffer->fixup) {
+                       gcfixup = list_entry(gcfixuphead, struct gcfixup, link);
+
+                       GCDBG(GCZONE_BUFFER,
+                               "  Fixup table @ 0x%08X, count = %d:\n",
+                               (unsigned int) gcfixup, gcfixup->count);
+
+                       for (i = 0; i < gcfixup->count; i += 1) {
+                               GCDBG(GCZONE_BUFFER, "  [%02d]"
+                                       " buffer offset = 0x%08X,"
+                                       " surface offset = 0x%08X\n",
+                                       i,
+                                       gcfixup->fixup[i].dataoffset * 4,
+                                       gcfixup->fixup[i].surfoffset);
+                       }
+               }
+
+               size = (unsigned char *) gcbuffer->tail
+                    - (unsigned char *) gcbuffer->head;
+               GCDUMPBUFFER(GCZONE_BUFFER, gcbuffer->head, 0, size);
+       }
+}
+
+static void verify_batch(unsigned int changeflags,
+                               struct bvrect *prevrect,
+                               struct bvrect *currrect)
+{
+       if ((changeflags & 1) == 0) {
+               /* Origin did not change. */
+               if ((prevrect->left != currrect->left) ||
+                       (prevrect->top != currrect->top)) {
+                       GCERR("origin changed\n");
+                       GCERR("  previous = %d,%d\n",
+                               prevrect->left, prevrect->top);
+                       GCERR("  current = %d,%d\n",
+                               currrect->left, currrect->top);
+               }
+       }
+
+       if ((changeflags & 2) == 0) {
+               /* Size did not change. */
+               if ((prevrect->width != currrect->width) ||
+                       (prevrect->height != currrect->height)) {
+                       GCERR("size changed\n");
+                       GCERR("  previous = %dx%d\n",
+                               prevrect->width, prevrect->height);
+                       GCERR("  current = %dx%d\n",
+                               currrect->width, currrect->height);
+               }
+       }
+
+       prevrect->left = currrect->left;
+       prevrect->top = currrect->top;
+       prevrect->width = currrect->width;
+       prevrect->height = currrect->height;
+}
+#else
+#define GCDUMPBATCH(...)
+#define GCVERIFYBATCH(...)
+#endif
+
+
+/*******************************************************************************
+ * Error handling.
+ */
+
+#define BVSETBLTSURFERROR(errorid, errordesc) \
+do { \
+       struct gccontext *tmpcontext = get_context(); \
+       snprintf(tmpcontext->bverrorstr, sizeof(tmpcontext->bverrorstr), \
+                g_surferr[errorid].message, errordesc.id); \
+       GCDUMPSTRING("%s(%d): [ERROR] %s\n", __func__, __LINE__, \
+                    tmpcontext->bverrorstr); \
+       bverror = errordesc.base + g_surferr[errorid].offset; \
+       bvbltparams->errdesc = tmpcontext->bverrorstr; \
+} while (0)
+
+#define GCBVERR_DESC           0
+#define GCBVERR_DESC_VERS      1
+#define GCBVERR_DESC_VIRTADDR  2
+#define GCBVERR_TILE           3
+#define GCBVERR_TILE_VERS      4
+#define GCBVERR_TILE_VIRTADDR  5
+#define GCBVERR_GEOM           6
+#define GCBVERR_GEOM_VERS      7
+#define GCBVERR_GEOM_FORMAT    8
+
+struct bvsurferrorid {
+       char *id;
+       enum bverror base;
+};
+
+struct bvsurferror {
+       unsigned int offset;
+       char *message;
+};
+
+static struct bvsurferror g_surferr[] = {
+       /* GCBVERR_DESC */
+       {    0, "%s desc structure is not set" },
+
+       /* GCBVERR_DESC_VERS */
+       {  100, "%s desc structure has invalid size" },
+
+       /* GCBVERR_DESC_VIRTADDR */
+       {  200, "%s desc virtual pointer is not set" },
+
+       /* GCBVERR_TILE: FIXME/TODO define error code */
+       {    0, "%s tileparams structure is not set" },
+
+       /* GCBVERR_TILE_VERS */
+       { 3000, "%s tileparams structure has invalid size" },
+
+       /* GCBVERR_TILE_VIRTADDR: FIXME/TODO define error code */
+       {  200, "%s tileparams virtual pointer is not set" },
+
+       /* GCBVERR_GEOM */
+       { 1000, "%s geom structure is not set" },
+
+       /* GCBVERR_GEOM_VERS */
+       { 1100, "%s geom structure has invalid size" },
+
+       /* GCBVERR_GEOM_FORMAT */
+       { 1200, "%s invalid format specified" },
+};
+
+static struct bvsurferrorid g_destsurferr = { "dst",  BVERR_DSTDESC };
+static struct bvsurferrorid g_src1surferr = { "src1", BVERR_SRC1DESC };
+static struct bvsurferrorid g_src2surferr = { "src2", BVERR_SRC2DESC };
+static struct bvsurferrorid g_masksurferr = { "mask", BVERR_MASKDESC };
+
+
+/*******************************************************************************
+ * Callback info management.
+ */
+
+/* BLTsville callback function. */
+struct gccallbackbltsville {
+       /* Function pointer. */
+       void (*fn) (struct bvcallbackerror *err, unsigned long callbackdata);
+
+       /* Callback data. */
+       unsigned long data;
+};
+
+/* Information for freeing a surface. */
+struct gccallbackfreesurface {
+       /* Pointer to the buffer descriptor. */
+       struct bvbuffdesc *desc;
+
+       /* Pointer to the buffer. */
+       void *ptr;
+};
+
+/* Callback information. */
+struct gccallbackinfo {
+       union {
+               /* BLTsville callback function. */
+               struct gccallbackbltsville callback;
+
+               /* Information for freeing a surface. */
+               struct gccallbackfreesurface freesurface;
+       } info;
+
+       /* Previous/next callback information. */
+       struct list_head link;
+};
+
+static enum bverror get_callbackinfo(struct gccallbackinfo **gccallbackinfo)
+{
+       enum bverror bverror;
+       struct gccontext *gccontext = get_context();
+       struct gccallbackinfo *temp;
+
+       /* Lock access to callback info lists. */
+       GCLOCK(&gccontext->callbacklock);
+
+       if (list_empty(&gccontext->callbackvac)) {
+               temp = gcalloc(struct gccallbackinfo,
+                              sizeof(struct gccallbackinfo));
+               if (temp == NULL) {
+                       bverror = BVERR_OOM;
+                       goto exit;
+               }
+               list_add(&temp->link, &gccontext->callbacklist);
+       } else {
+               struct list_head *head;
+               head = gccontext->callbackvac.next;
+               temp = list_entry(head, struct gccallbackinfo, link);
+               list_move(head, &gccontext->callbacklist);
+       }
+
+       *gccallbackinfo = temp;
+       bverror = BVERR_NONE;
+
+exit:
+       /* Unlock access to callback info lists. */
+       GCUNLOCK(&gccontext->callbacklock);
+
+       return bverror;
+}
+
+static void free_callback(struct gccallbackinfo *gccallbackinfo)
+{
+       struct gccontext *gccontext = get_context();
+
+       /* Lock access to callback info lists. */
+       GCLOCK(&gccontext->callbacklock);
+
+       list_move(&gccallbackinfo->link, &gccontext->callbackvac);
+
+       /* Unlock access to callback info lists. */
+       GCUNLOCK(&gccontext->callbacklock);
+}
+
+void callbackbltsville(void *callbackinfo)
+{
+       struct gccallbackinfo *gccallbackinfo;
+
+       GCENTER(GCZONE_CALLBACK);
+
+       gccallbackinfo = (struct gccallbackinfo *) callbackinfo;
+       GCDBG(GCZONE_CALLBACK, "bltsville_callback = 0x%08X\n",
+             (unsigned int) gccallbackinfo->info.callback.fn);
+       GCDBG(GCZONE_CALLBACK, "bltsville_param    = 0x%08X\n",
+             (unsigned int) gccallbackinfo->info.callback.data);
+
+       gccallbackinfo->info.callback.fn(NULL,
+                                        gccallbackinfo->info.callback.data);
+       free_callback(gccallbackinfo);
+
+       GCEXIT(GCZONE_CALLBACK);
+}
+
+void callbackfreesurface(void *callbackinfo)
+{
+       struct gccallbackinfo *gccallbackinfo;
+
+       GCENTER(GCZONE_CALLBACK);
+
+       gccallbackinfo = (struct gccallbackinfo *) callbackinfo;
+       GCDBG(GCZONE_CALLBACK, "freeing descriptir @ 0x%08X\n",
+             (unsigned int) gccallbackinfo->info.freesurface.desc);
+       GCDBG(GCZONE_CALLBACK, "freeing memory @ 0x%08X\n",
+             (unsigned int) gccallbackinfo->info.freesurface.ptr);
+
+       free_surface(gccallbackinfo->info.freesurface.desc,
+                    gccallbackinfo->info.freesurface.ptr);
+       free_callback(gccallbackinfo);
+
+       GCEXIT(GCZONE_CALLBACK);
+}
+
+
+/*******************************************************************************
+ * Temporary buffer management.
+ */
+
+enum bverror allocate_temp(struct bvbltparams *bvbltparams,
+                          unsigned int size)
+{
+       enum bverror bverror;
+       struct gccontext *gccontext = get_context();
+
+       GCENTER(GCZONE_TEMP);
+
+       /* Existing buffer too small? */
+       if ((gccontext->tmpbuffdesc != NULL) &&
+           (gccontext->tmpbuffdesc->length < size)) {
+               GCDBG(GCZONE_TEMP, "freeing current buffer.\n");
+               bverror = free_temp(true);
+               if (bverror != BVERR_NONE) {
+                       bvbltparams->errdesc = gccontext->bverrorstr;
+                       goto exit;
+               }
+       }
+
+       /* Allocate new buffer if necessary. */
+       if ((size > 0) && (gccontext->tmpbuffdesc == NULL)) {
+               /* Allocate temporary surface. */
+               bverror = allocate_surface(&gccontext->tmpbuffdesc,
+                                          &gccontext->tmpbuff,
+                                          size);
+               if (bverror != BVERR_NONE) {
+                       bvbltparams->errdesc = gccontext->bverrorstr;
+                       goto exit;
+               }
+
+               GCDBG(GCZONE_TEMP, "buffdesc @ 0x%08X\n",
+                     gccontext->tmpbuffdesc);
+               GCDBG(GCZONE_TEMP, "allocated @ 0x%08X\n",
+                     gccontext->tmpbuff);
+               GCDBG(GCZONE_TEMP, "size = %d\n",
+                     size);
+
+               /* Map the buffer explicitly. */
+               bverror = bv_map(gccontext->tmpbuffdesc);
+               if (bverror != BVERR_NONE) {
+                       bvbltparams->errdesc = gccontext->bverrorstr;
+                       goto exit;
+               }
+       }
+
+       /* Success. */
+       bverror = BVERR_NONE;
+
+exit:
+       GCEXIT(GCZONE_TEMP);
+       return bverror;
+}
+
+enum bverror free_temp(bool schedule)
+{
+       enum bverror bverror;
+       struct gccontext *gccontext = get_context();
+       struct gccallbackinfo *gccallbackinfo;
+       struct gcicallbackarm gcicallbackarm;
+
+       /* Is the buffer allocated? */
+       if (gccontext->tmpbuffdesc == NULL) {
+               bverror = BVERR_NONE;
+               goto exit;
+       }
+
+       /* Unmap the buffer. */
+       bverror = bv_unmap(gccontext->tmpbuffdesc);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /* Cannot be mapped. */
+       if (gccontext->tmpbuffdesc->map != NULL) {
+               BVSETERROR(BVERR_OOM, "temporary buffer is still mapped");
+               goto exit;
+       }
+
+       /* Free the buffer. */
+       if (schedule) {
+               bverror = get_callbackinfo(&gccallbackinfo);
+               if (bverror != BVERR_NONE) {
+                       BVSETERROR(BVERR_OOM,
+                                  "callback allocation failed");
+                       goto exit;
+               }
+
+               gccallbackinfo->info.freesurface.desc = gccontext->tmpbuffdesc;
+               gccallbackinfo->info.freesurface.ptr = gccontext->tmpbuff;
+               gcicallbackarm.callback = callbackfreesurface;
+               gcicallbackarm.callbackparam = gccallbackinfo;
+
+               /* Schedule to free the buffer. */
+               gc_callback_wrapper(&gcicallbackarm);
+
+               /* Error? */
+               if (gcicallbackarm.gcerror != GCERR_NONE) {
+                       BVSETERROR(BVERR_OOM, "unable to schedule callback");
+                       goto exit;
+               }
+       } else {
+               /* Free the buffer immediately. */
+               free_surface(gccontext->tmpbuffdesc, gccontext->tmpbuff);
+       }
+
+       /* Reset the buffer descriptor. */
+       gccontext->tmpbuffdesc = NULL;
+       gccontext->tmpbuff = NULL;
+
+exit:
+       return bverror;
+}
+
+
+/*******************************************************************************
+ * Program the destination.
+ */
+
+enum bverror set_dst(struct bvbltparams *bvbltparams,
+                    struct gcbatch *batch,
+                    struct bvbuffmap *dstmap)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gcsurface *dstinfo;
+       struct gcmodst *gcmodst;
+
+       GCENTER(GCZONE_DEST);
+
+       /* Get a shortcut to the destination surface descriptor. */
+       dstinfo = &batch->dstinfo;
+
+       /* Did destination surface change? */
+       if (dstinfo->surfdirty) {
+               /* Allocate command buffer. */
+               bverror = claim_buffer(bvbltparams, batch,
+                                      sizeof(struct gcmodst),
+                                      (void **) &gcmodst);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Add the address fixup. */
+               add_fixup(bvbltparams, batch, &gcmodst->address,
+                         dstinfo->bytealign1);
+
+               /* Set surface parameters. */
+               gcmodst->config_ldst = gcmodst_config_ldst;
+               gcmodst->address = GET_MAP_HANDLE(dstmap);
+               gcmodst->stride = dstinfo->stride1;
+
+               /* Set surface width and height. */
+               gcmodst->rotation.raw = 0;
+               gcmodst->rotation.reg.surf_width = dstinfo->physwidth;
+               gcmodst->rotationheight_ldst = gcmodst_rotationheight_ldst;
+               gcmodst->rotationheight.raw = 0;
+               gcmodst->rotationheight.reg.height = dstinfo->physheight;
+
+               /* Disable hardware clipping. */
+               gcmodst->clip_ldst = gcmodst_clip_ldst;
+               gcmodst->cliplt.raw = 0;
+               gcmodst->cliprb.raw = 0;
+               gcmodst->cliprb.reg.right = GC_CLIP_RESET_RIGHT;
+               gcmodst->cliprb.reg.bottom = GC_CLIP_RESET_BOTTOM;
+       }
+
+exit:
+       GCEXITARG(GCZONE_DEST, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+
+/*******************************************************************************
+ * Program blending.
+ */
+
+enum bverror set_blending(struct bvbltparams *bvbltparams,
+                         struct gcbatch *batch,
+                         struct gcsurface *srcinfo)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gcmoalphaoff *gcmoalphaoff;
+       struct gcmoalpha *gcmoalpha;
+       struct gcmoglobal *gcmoglobal;
+       struct gcalpha *gca;
+
+       GCENTER(GCZONE_BLEND);
+
+       gca = srcinfo->gca;
+       if (gca == NULL) {
+               bverror = claim_buffer(bvbltparams, batch,
+                                      sizeof(struct gcmoalphaoff),
+                                      (void **) &gcmoalphaoff);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               gcmoalphaoff->control_ldst = gcmoalphaoff_control_ldst[0];
+               gcmoalphaoff->control.reg = gcregalpha_off;
+
+               GCDBG(GCZONE_BLEND, "blending disabled.\n");
+       } else {
+               bverror = claim_buffer(bvbltparams, batch,
+                                      sizeof(struct gcmoalpha),
+                                      (void **) &gcmoalpha);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               gcmoalpha->config_ldst = gcmoalpha_config_ldst;
+               gcmoalpha->control.reg = gcregalpha_on;
+
+               gcmoalpha->mode.raw = 0;
+               gcmoalpha->mode.reg.src_global_alpha_mode
+                       = srcinfo->srcglobalmode;
+               gcmoalpha->mode.reg.dst_global_alpha_mode
+                       = srcinfo->dstglobalmode;
+
+               gcmoalpha->mode.reg.src_blend
+                       = gca->srcconfig->factor_mode;
+               gcmoalpha->mode.reg.src_color_reverse
+                       = gca->srcconfig->color_reverse;
+
+               gcmoalpha->mode.reg.dst_blend
+                       = gca->dstconfig->factor_mode;
+               gcmoalpha->mode.reg.dst_color_reverse
+                       = gca->dstconfig->color_reverse;
+
+               GCDBG(GCZONE_BLEND, "dst blend:\n");
+               GCDBG(GCZONE_BLEND, "  factor = %d\n",
+                       gcmoalpha->mode.reg.dst_blend);
+               GCDBG(GCZONE_BLEND, "  inverse = %d\n",
+                       gcmoalpha->mode.reg.dst_color_reverse);
+
+               GCDBG(GCZONE_BLEND, "src blend:\n");
+               GCDBG(GCZONE_BLEND, "  factor = %d\n",
+                       gcmoalpha->mode.reg.src_blend);
+               GCDBG(GCZONE_BLEND, "  inverse = %d\n",
+                       gcmoalpha->mode.reg.src_color_reverse);
+       }
+
+       if (srcinfo->globalcolorenable) {
+               bverror = claim_buffer(bvbltparams, batch,
+                                      sizeof(struct gcmoglobal),
+                                      (void **) &gcmoglobal);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               gcmoglobal->color_ldst = gcmoglobal_color_ldst;
+               gcmoglobal->srcglobal.raw = srcinfo->globalcolor;
+               gcmoglobal->dstglobal.raw = srcinfo->globalcolor;
+       }
+
+exit:
+       GCEXITARG(GCZONE_BLEND, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+enum bverror set_blending_index(struct bvbltparams *bvbltparams,
+                               struct gcbatch *batch,
+                               struct gcsurface *srcinfo,
+                               unsigned int index)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gcmoalphaoff *gcmoalphaoff;
+       struct gcmoxsrcalpha *gcmoxsrcalpha;
+       struct gcmoxsrcglobal *gcmoxsrcglobal;
+       struct gcalpha *gca;
+
+       GCENTER(GCZONE_BLEND);
+
+       gca = srcinfo->gca;
+       if (gca == NULL) {
+               bverror = claim_buffer(bvbltparams, batch,
+                                      sizeof(struct gcmoalphaoff),
+                                      (void **) &gcmoalphaoff);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               gcmoalphaoff->control_ldst = gcmoalphaoff_control_ldst[index];
+               gcmoalphaoff->control.reg = gcregalpha_off;
+
+               GCDBG(GCZONE_BLEND, "blending disabled.\n");
+       } else {
+               bverror = claim_buffer(bvbltparams, batch,
+                                      sizeof(struct gcmoxsrcalpha),
+                                      (void **) &gcmoxsrcalpha);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               gcmoxsrcalpha->control_ldst = gcmoxsrcalpha_control_ldst[index];
+               gcmoxsrcalpha->control.reg = gcregalpha_on;
+
+               gcmoxsrcalpha->mode_ldst = gcmoxsrcalpha_mode_ldst[index];
+               gcmoxsrcalpha->mode.raw = 0;
+               gcmoxsrcalpha->mode.reg.src_global_alpha_mode
+                       = srcinfo->srcglobalmode;
+               gcmoxsrcalpha->mode.reg.dst_global_alpha_mode
+                       = srcinfo->dstglobalmode;
+
+               gcmoxsrcalpha->mode.reg.src_blend
+                       = gca->srcconfig->factor_mode;
+               gcmoxsrcalpha->mode.reg.src_color_reverse
+                       = gca->srcconfig->color_reverse;
+
+               gcmoxsrcalpha->mode.reg.dst_blend
+                       = gca->dstconfig->factor_mode;
+               gcmoxsrcalpha->mode.reg.dst_color_reverse
+                       = gca->dstconfig->color_reverse;
+
+               GCDBG(GCZONE_BLEND, "dst blend:\n");
+               GCDBG(GCZONE_BLEND, "  factor = %d\n",
+                       gcmoxsrcalpha->mode.reg.dst_blend);
+               GCDBG(GCZONE_BLEND, "  inverse = %d\n",
+                       gcmoxsrcalpha->mode.reg.dst_color_reverse);
+
+               GCDBG(GCZONE_BLEND, "src blend:\n");
+               GCDBG(GCZONE_BLEND, "  factor = %d\n",
+                       gcmoxsrcalpha->mode.reg.src_blend);
+               GCDBG(GCZONE_BLEND, "  inverse = %d\n",
+                       gcmoxsrcalpha->mode.reg.src_color_reverse);
+       }
+
+       if (srcinfo->globalcolorenable) {
+               bverror = claim_buffer(bvbltparams, batch,
+                                      sizeof(struct gcmoxsrcglobal),
+                                      (void **) &gcmoxsrcglobal);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               gcmoxsrcglobal->srcglobal_ldst
+                       = gcmoxsrcglobal_srcglobal_ldst[index];
+               gcmoxsrcglobal->srcglobal.raw = srcinfo->globalcolor;
+
+               gcmoxsrcglobal->dstglobal_ldst
+                       = gcmoxsrcglobal_dstglobal_ldst[index];
+               gcmoxsrcglobal->dstglobal.raw = srcinfo->globalcolor;
+       }
+
+exit:
+       GCEXITARG(GCZONE_BLEND, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+/*******************************************************************************
+ * Program YUV source.
+ */
+
+void set_computeyuv(struct gcsurface *srcinfo, int x, int y)
+{
+       int pixalign, bytealign;
+       unsigned int height1, size1;
+       unsigned int height2, size2;
+       unsigned int origin;
+       int ssX, ssY;
+
+       GCENTER(GCZONE_SRC);
+
+       /* Compute base address alignment. */
+       pixalign = get_pixel_offset(srcinfo, 0);
+       bytealign = (pixalign * (int) srcinfo->format.bitspp) / 8;
+
+       /* Determine the physical height of the first plane. */
+       height1 = ((srcinfo->angle % 2) == 0)
+               ? srcinfo->height
+               : srcinfo->width;
+
+       /* Determine the size of the first plane. */
+       size1 = srcinfo->stride1 * height1;
+
+       /* Determine the stride of the second plane. */
+       srcinfo->stride2 = srcinfo->stride1
+                        / srcinfo->format.cs.yuv.xsample;
+
+       /* Determine subsample pixel position. */
+       ssX = x / srcinfo->format.cs.yuv.xsample;
+       ssY = y / srcinfo->format.cs.yuv.ysample;
+
+       switch (srcinfo->format.cs.yuv.planecount) {
+       case 2:
+               /* U and V are interleaved in one plane. */
+               ssX *= 2;
+               srcinfo->stride2 *= 2;
+
+               /* Determnine the origin offset. */
+               origin = ssY * srcinfo->stride2 + ssX;
+
+               /* Compute the alignment of the second plane. */
+               srcinfo->bytealign2 = bytealign + size1 + origin;
+
+               GCDBG(GCZONE_SRC, "plane2 offset (bytes) = 0x%08X\n",
+                       srcinfo->bytealign2);
+               GCDBG(GCZONE_SRC, "plane2 stride = %d\n",
+                       srcinfo->stride2);
+               break;
+
+       case 3:
+               /* Determine the physical height of the U/V planes. */
+               height2 = height1 / srcinfo->format.cs.yuv.ysample;
+
+               /* Determine the size of the U/V planes. */
+               size2 = srcinfo->stride2 * height2;
+
+               /* Determnine the origin offset. */
+               origin = ssY * srcinfo->stride2 + ssX;
+
+               /* Compute the alignment of the U/V planes. */
+               srcinfo->bytealign2 = bytealign + size1 + origin;
+               srcinfo->bytealign3 = bytealign + size1 + size2 + origin;
+
+               /* Determine the stride of the U/V planes. */
+               srcinfo->stride3 = srcinfo->stride2;
+
+               GCDBG(GCZONE_SRC, "plane2 offset (bytes) = 0x%08X\n",
+                     srcinfo->bytealign2);
+               GCDBG(GCZONE_SRC, "plane2 stride = %d\n",
+                       srcinfo->stride2);
+               GCDBG(GCZONE_SRC, "plane3 offset (bytes) = 0x%08X\n",
+                     srcinfo->bytealign3);
+               GCDBG(GCZONE_SRC, "plane3 stride = %d\n",
+                       srcinfo->stride3);
+               break;
+       }
+
+       GCEXIT(GCZONE_SRC);
+}
+
+enum bverror set_yuvsrc(struct bvbltparams *bvbltparams,
+                       struct gcbatch *batch,
+                       struct gcsurface *srcinfo,
+                       struct bvbuffmap *srcmap)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gcmoyuv1 *gcmoyuv1;
+       struct gcmoyuv2 *gcmoyuv2;
+       struct gcmoyuv3 *gcmoyuv3;
+
+       GCENTER(GCZONE_SRC);
+
+       GCDBG(GCZONE_SRC, "plane count %d.\n",
+             srcinfo->format.cs.yuv.planecount);
+
+       switch (srcinfo->format.cs.yuv.planecount) {
+       case 1:
+               bverror = claim_buffer(bvbltparams, batch,
+                                      sizeof(struct gcmoyuv1),
+                                      (void **) &gcmoyuv1);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Set YUV parameters. */
+               gcmoyuv1->pectrl_ldst = gcmoyuv_pectrl_ldst;
+               gcmoyuv1->pectrl.raw = 0;
+               gcmoyuv1->pectrl.reg.standard
+                       = srcinfo->format.cs.yuv.std;
+               gcmoyuv1->pectrl.reg.swizzle
+                       = srcinfo->format.swizzle;
+               gcmoyuv1->pectrl.reg.convert
+                       = GCREG_PE_CONTROL_YUVRGB_DISABLED;
+               break;
+
+       case 2:
+               bverror = claim_buffer(bvbltparams, batch,
+                                       sizeof(struct gcmoyuv2),
+                                       (void **) &gcmoyuv2);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Set YUV parameters. */
+               gcmoyuv2->pectrl_ldst = gcmoyuv_pectrl_ldst;
+               gcmoyuv2->pectrl.raw = 0;
+               gcmoyuv2->pectrl.reg.standard
+                       = srcinfo->format.cs.yuv.std;
+               gcmoyuv2->pectrl.reg.swizzle
+                       = srcinfo->format.swizzle;
+               gcmoyuv2->pectrl.reg.convert
+                       = GCREG_PE_CONTROL_YUVRGB_DISABLED;
+
+               /* Program U/V plane. */
+               add_fixup(bvbltparams, batch, &gcmoyuv2->uplaneaddress,
+                         srcinfo->bytealign2);
+               gcmoyuv2->plane_ldst = gcmoyuv2_plane_ldst;
+               gcmoyuv2->uplaneaddress = GET_MAP_HANDLE(srcmap);
+               gcmoyuv2->uplanestride = srcinfo->stride2;
+               break;
+
+       case 3:
+               bverror = claim_buffer(bvbltparams, batch,
+                                       sizeof(struct gcmoyuv3),
+                                       (void **) &gcmoyuv3);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Set YUV parameters. */
+               gcmoyuv3->pectrl_ldst = gcmoyuv_pectrl_ldst;
+               gcmoyuv3->pectrl.raw = 0;
+               gcmoyuv3->pectrl.reg.standard
+                       = srcinfo->format.cs.yuv.std;
+               gcmoyuv3->pectrl.reg.swizzle
+                       = srcinfo->format.swizzle;
+               gcmoyuv3->pectrl.reg.convert
+                       = GCREG_PE_CONTROL_YUVRGB_DISABLED;
+
+               /* Program U/V planes. */
+               add_fixup(bvbltparams, batch, &gcmoyuv3->uplaneaddress,
+                         srcinfo->bytealign2);
+               add_fixup(bvbltparams, batch, &gcmoyuv3->vplaneaddress,
+                         srcinfo->bytealign3);
+               gcmoyuv3->plane_ldst = gcmoyuv3_plane_ldst;
+               gcmoyuv3->uplaneaddress = GET_MAP_HANDLE(srcmap);
+               gcmoyuv3->uplanestride  = srcinfo->stride2;
+               gcmoyuv3->vplaneaddress = GET_MAP_HANDLE(srcmap);
+               gcmoyuv3->vplanestride  = srcinfo->stride3;
+               break;
+
+       default:
+               GCERR("invlaid plane count %d.\n",
+                     srcinfo->format.cs.yuv.planecount);
+       }
+
+exit:
+       GCEXITARG(GCZONE_SRC, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+enum bverror set_yuvsrc_index(struct bvbltparams *bvbltparams,
+                             struct gcbatch *batch,
+                             struct gcsurface *srcinfo,
+                             struct bvbuffmap *srcmap,
+                             unsigned int index)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gcmoxsrcyuv1 *gcmoxsrcyuv1;
+       struct gcmoxsrcyuv2 *gcmoxsrcyuv2;
+       struct gcmoxsrcyuv3 *gcmoxsrcyuv3;
+
+       GCENTER(GCZONE_SRC);
+
+       GCDBG(GCZONE_SRC, "plane count %d.\n",
+             srcinfo->format.cs.yuv.planecount);
+
+       switch (srcinfo->format.cs.yuv.planecount) {
+       case 1:
+               bverror = claim_buffer(bvbltparams, batch,
+                                      sizeof(struct gcmoxsrcyuv1),
+                                      (void **) &gcmoxsrcyuv1);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Set YUV parameters. */
+               gcmoxsrcyuv1->pectrl_ldst
+                       = gcmoxsrcyuv_pectrl_ldst[index];
+               gcmoxsrcyuv1->pectrl.raw = 0;
+               gcmoxsrcyuv1->pectrl.reg.standard
+                       = srcinfo->format.cs.yuv.std;
+               gcmoxsrcyuv1->pectrl.reg.swizzle
+                       = srcinfo->format.swizzle;
+               gcmoxsrcyuv1->pectrl.reg.convert
+                       = GCREG_PE_CONTROL_YUVRGB_DISABLED;
+               break;
+
+       case 2:
+               bverror = claim_buffer(bvbltparams, batch,
+                                       sizeof(struct gcmoxsrcyuv2),
+                                       (void **) &gcmoxsrcyuv2);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Set YUV parameters. */
+               gcmoxsrcyuv2->pectrl_ldst
+                       = gcmoxsrcyuv_pectrl_ldst[index];
+               gcmoxsrcyuv2->pectrl.raw = 0;
+               gcmoxsrcyuv2->pectrl.reg.standard
+                       = srcinfo->format.cs.yuv.std;
+               gcmoxsrcyuv2->pectrl.reg.swizzle
+                       = srcinfo->format.swizzle;
+               gcmoxsrcyuv2->pectrl.reg.convert
+                       = GCREG_PE_CONTROL_YUVRGB_DISABLED;
+
+               /* Program U/V plane. */
+               add_fixup(bvbltparams, batch, &gcmoxsrcyuv2->uplaneaddress,
+                         srcinfo->bytealign2);
+               gcmoxsrcyuv2->uplaneaddress_ldst
+                       = gcmoxsrcyuv_uplaneaddress_ldst[index];
+               gcmoxsrcyuv2->uplaneaddress = GET_MAP_HANDLE(srcmap);
+               gcmoxsrcyuv2->uplanestride_ldst
+                       = gcmoxsrcyuv_uplanestride_ldst[index];
+               gcmoxsrcyuv2->uplanestride = srcinfo->stride2;
+               break;
+
+       case 3:
+               bverror = claim_buffer(bvbltparams, batch,
+                                       sizeof(struct gcmoxsrcyuv3),
+                                       (void **) &gcmoxsrcyuv3);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Set YUV parameters. */
+               gcmoxsrcyuv3->pectrl_ldst
+                       = gcmoxsrcyuv_pectrl_ldst[index];
+               gcmoxsrcyuv3->pectrl.raw = 0;
+               gcmoxsrcyuv3->pectrl.reg.standard
+                       = srcinfo->format.cs.yuv.std;
+               gcmoxsrcyuv3->pectrl.reg.swizzle
+                       = srcinfo->format.swizzle;
+               gcmoxsrcyuv3->pectrl.reg.convert
+                       = GCREG_PE_CONTROL_YUVRGB_DISABLED;
+
+               /* Program U/V planes. */
+               add_fixup(bvbltparams, batch, &gcmoxsrcyuv3->uplaneaddress,
+                         srcinfo->bytealign2);
+               add_fixup(bvbltparams, batch, &gcmoxsrcyuv3->vplaneaddress,
+                         srcinfo->bytealign3);
+               gcmoxsrcyuv3->uplaneaddress_ldst
+                       = gcmoxsrcyuv_uplaneaddress_ldst[index];
+               gcmoxsrcyuv3->uplaneaddress = GET_MAP_HANDLE(srcmap);
+               gcmoxsrcyuv3->uplanestride_ldst
+                       = gcmoxsrcyuv_uplanestride_ldst[index];
+               gcmoxsrcyuv3->uplanestride  = srcinfo->stride2;
+               gcmoxsrcyuv3->vplaneaddress_ldst
+                       = gcmoxsrcyuv_vplaneaddress_ldst[index];
+               gcmoxsrcyuv3->vplaneaddress = GET_MAP_HANDLE(srcmap);
+               gcmoxsrcyuv3->vplanestride_ldst
+                       = gcmoxsrcyuv_vplanestride_ldst[index];
+               gcmoxsrcyuv3->vplanestride  = srcinfo->stride3;
+               break;
+
+       default:
+               GCERR("invlaid plane count %d.\n",
+                     srcinfo->format.cs.yuv.planecount);
+       }
+
+exit:
+       GCEXITARG(GCZONE_SRC, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+
+/*******************************************************************************
+ * Surface compare and validation.
+ */
+
+static inline bool equal_rects(struct bvrect *rect1, struct bvrect *rect2)
+{
+       if (rect1->left != rect2->left)
+               return false;
+
+       if (rect1->top != rect2->top)
+               return false;
+
+       if (rect1->width != rect2->width)
+               return false;
+
+       if (rect1->height != rect2->height)
+               return false;
+
+       return true;
+}
+
+/* The function verifies whether the two buffer descriptors and rectangles
+   define the same physical area. */
+static bool same_phys_area(struct bvbuffdesc *surf1, struct bvrect *rect1,
+                          struct bvbuffdesc *surf2, struct bvrect *rect2)
+{
+       struct bvphysdesc *physdesc1;
+       struct bvphysdesc *physdesc2;
+
+       /* If pointers are the same, things are much easier. */
+       if (surf1 == surf2)
+               /* Compare the rectangles. For simplicity we don't consider
+                  cases with partially overlapping rectangles at this time. */
+               return equal_rects(rect1, rect2);
+
+       /* Assume diffrent areas if the types are different. */
+       if (surf1->auxtype != surf2->auxtype)
+               return false;
+
+       if (surf1->auxtype == BVAT_PHYSDESC) {
+               physdesc1 = (struct bvphysdesc *) surf1->auxptr;
+               physdesc2 = (struct bvphysdesc *) surf2->auxptr;
+
+               /* Same physical descriptor? */
+               if (physdesc1 == physdesc2)
+                       return equal_rects(rect1, rect2);
+
+               /* Same page array? */
+               if (physdesc1->pagearray == physdesc2->pagearray)
+                       return equal_rects(rect1, rect2);
+
+               /* Pageoffsets must match since different buffers
+                * can share the same first page (eg nv12).
+                */
+               if (physdesc1->pageoffset != physdesc2->pageoffset)
+                       return false;
+
+               /* Assume the same surface if first pages match. */
+               if (physdesc1->pagearray[0] == physdesc2->pagearray[0])
+                       return equal_rects(rect1, rect2);
+
+       } else {
+               if (surf1->virtaddr == surf2->virtaddr)
+                       return equal_rects(rect1, rect2);
+       }
+
+       return false;
+}
+
+static int verify_surface(unsigned int tile,
+                         union bvinbuff *surf,
+                         struct bvsurfgeom *geom)
+{
+       if (tile) {
+               if (surf->tileparams == NULL)
+                       return GCBVERR_TILE;
+
+               if (surf->tileparams->structsize <
+                   STRUCTSIZE(surf->tileparams, srcheight))
+                       return GCBVERR_TILE_VERS;
+
+               /* FIXME/TODO */
+               return GCBVERR_TILE;
+       } else {
+               if (surf->desc == NULL)
+                       return GCBVERR_DESC;
+
+               if (surf->desc->structsize < STRUCTSIZE(surf->desc, map))
+                       return GCBVERR_DESC_VERS;
+       }
+
+       if (geom == NULL)
+               return GCBVERR_GEOM;
+
+       if (geom->structsize < STRUCTSIZE(geom, palette))
+               return GCBVERR_GEOM_VERS;
+
+       /* Validation successful. */
+       return -1;
+}
+
+
+/*******************************************************************************
+ * Operation.
+ */
+
+static enum bverror do_op(struct bvbltparams *bvbltparams,
+                         struct gcbatch *gcbatch,
+                         int srccount,
+                         struct gcsurface *srcinfo,
+                         struct gcalpha *gca)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gcsurface *dstinfo;
+       int sw, sh, dw, dh;
+
+       GCDBG(GCZONE_BLIT, "processing source %d.\n", srcinfo->index + 1);
+
+       if (gca == NULL) {
+               GCDBG(GCZONE_BLIT,
+                     "  blending disabled.\n");
+
+               srcinfo->gca = NULL;
+               srcinfo->globalcolorenable = false;
+               srcinfo->srcglobalpremul = GCREG_SRC_GLOBAL_PREMULTIPLY_DISABLE;
+               srcinfo->srcglobalmode = GCREG_GLOBAL_ALPHA_MODE_NORMAL;
+               srcinfo->dstglobalmode = GCREG_GLOBAL_ALPHA_MODE_NORMAL;
+       } else {
+               if (srcinfo->index == 0) {
+                       /* First source. */
+
+                       if (srccount == 1) {
+                               /* Only one source. */
+                               GCDBG(GCZONE_BLIT,
+                                     "  enabling blending.\n");
+
+                               srcinfo->gca = gca;
+                               gca->srcconfig = gca->k1;
+                               gca->dstconfig = gca->k2;
+
+                       } else {
+                               /* Two sources. */
+                               GCDBG(GCZONE_BLIT,
+                                     "  disabling blending for src1.\n");
+
+                               srcinfo->gca = NULL;
+                       }
+
+                       if (gca->globalcolorenable) {
+                               srcinfo->globalcolorenable = true;
+                               srcinfo->globalcolor = gca->globalcolor;
+                               srcinfo->srcglobalpremul
+                                       = GCREG_SRC_GLOBAL_PREMULTIPLY_ALPHA;
+                               srcinfo->srcglobalmode
+                                       = GCREG_GLOBAL_ALPHA_MODE_SCALED;
+                               srcinfo->dstglobalmode
+                                       = GCREG_GLOBAL_ALPHA_MODE_NORMAL;
+                       } else {
+                               srcinfo->globalcolorenable = false;
+                               srcinfo->srcglobalpremul
+                                       = GCREG_SRC_GLOBAL_PREMULTIPLY_DISABLE;
+                               srcinfo->srcglobalmode
+                                       = GCREG_GLOBAL_ALPHA_MODE_NORMAL;
+                               srcinfo->dstglobalmode
+                                       = GCREG_GLOBAL_ALPHA_MODE_NORMAL;
+                       }
+               } else {
+                       /* Second source. */
+                       GCDBG(GCZONE_BLIT,
+                               "  enabling blending.\n");
+
+                       srcinfo->gca = gca;
+                       gca->srcconfig = gca->k2;
+                       gca->dstconfig = gca->k1;
+
+                       if (gca->globalcolorenable) {
+                               srcinfo->globalcolorenable = true;
+                               srcinfo->globalcolor = gca->globalcolor;
+                               srcinfo->srcglobalpremul
+                                       = GCREG_SRC_GLOBAL_PREMULTIPLY_DISABLE;
+                               srcinfo->srcglobalmode
+                                       = GCREG_GLOBAL_ALPHA_MODE_NORMAL;
+                               srcinfo->dstglobalmode
+                                       = GCREG_GLOBAL_ALPHA_MODE_SCALED;
+                       } else {
+                               srcinfo->globalcolorenable = false;
+                               srcinfo->srcglobalpremul
+                                       = GCREG_SRC_GLOBAL_PREMULTIPLY_DISABLE;
+                               srcinfo->srcglobalmode
+                                       = GCREG_GLOBAL_ALPHA_MODE_NORMAL;
+                               srcinfo->dstglobalmode
+                                       = GCREG_GLOBAL_ALPHA_MODE_NORMAL;
+                       }
+               }
+       }
+
+       sw = srcinfo->rect.orig.right  - srcinfo->rect.orig.left;
+       sh = srcinfo->rect.orig.bottom - srcinfo->rect.orig.top;
+
+       dw = bvbltparams->dstrect.width;
+       dh = bvbltparams->dstrect.height;
+
+       GCDBG(GCZONE_BLIT, "  srcsize %dx%d.\n", sw, sh);
+       GCDBG(GCZONE_BLIT, "  dstsize %dx%d.\n", dw, dh);
+
+       if ((sw == 0) || (sh == 0)) {
+               GCDBG(GCZONE_BLIT, "  empty source, skipping.\n");
+       } else if ((dw == 0) || (dh == 0)) {
+               GCDBG(GCZONE_BLIT, "  empty destination, skipping.\n");
+       } else if ((sw == 1) && (sh == 1) && (srcinfo->buf.desc->virtaddr)) {
+               GCDBG(GCZONE_BLIT, "  op: fill.\n");
+               bverror = do_fill(bvbltparams, gcbatch, srcinfo);
+       } else if ((sw == dw) && (sh == dh)) {
+               GCDBG(GCZONE_BLIT, "  op: bitblit.\n");
+               bverror = do_blit(bvbltparams, gcbatch, srcinfo);
+       } else {
+               GCDBG(GCZONE_BLIT, "  op: filter.\n");
+               bverror = do_filter(bvbltparams, gcbatch, srcinfo);
+       }
+
+       /* Reset dirty flags. */
+       dstinfo = &gcbatch->dstinfo;
+       dstinfo->surfdirty = false;
+       dstinfo->rectdirty = false;
+       dstinfo->cliprectdirty = false;
+       dstinfo->destrectdirty = false;
+
+       return bverror;
+}
+
+
+/*******************************************************************************
+ * Library constructor and destructor.
+ */
+
+void bv_init(void)
+{
+       struct gccontext *gccontext = get_context();
+       struct gcicaps gcicaps;
+       unsigned i, j;
+
+       GCDBG_REGISTER(bv,     ~GCZONE_BUFFER);
+       GCDBG_REGISTER(parser, GCZONE_ALL);
+       GCDBG_REGISTER(map,    GCZONE_NONE);
+       GCDBG_REGISTER(buffer, GCZONE_NONE);
+       GCDBG_REGISTER(fill,   GCZONE_ALL);
+       GCDBG_REGISTER(blit,   GCZONE_ALL);
+       GCDBG_REGISTER(filter, GCZONE_ALL);
+
+       gcbv_debug_init();
+
+       GCLOCK_INIT(&gccontext->batchlock);
+       GCLOCK_INIT(&gccontext->bufferlock);
+       GCLOCK_INIT(&gccontext->fixuplock);
+       GCLOCK_INIT(&gccontext->maplock);
+       GCLOCK_INIT(&gccontext->callbacklock);
+
+       INIT_LIST_HEAD(&gccontext->unmapvac);
+       INIT_LIST_HEAD(&gccontext->buffervac);
+       INIT_LIST_HEAD(&gccontext->fixupvac);
+       INIT_LIST_HEAD(&gccontext->batchvac);
+       INIT_LIST_HEAD(&gccontext->callbacklist);
+       INIT_LIST_HEAD(&gccontext->callbackvac);
+
+       /* Initialize the filter cache. */
+       for (i = 0; i < GC_FILTER_COUNT; i += 1)
+               for (j = 0; j < GC_TAP_COUNT; j += 1)
+                       INIT_LIST_HEAD(&gccontext->filtercache[i][j].list);
+
+       /* Query hardware caps. */
+       gc_getcaps_wrapper(&gcicaps);
+       if (gcicaps.gcerror == GCERR_NONE) {
+               gccontext->gcmodel = gcicaps.gcmodel;
+               gccontext->gcrevision = gcicaps.gcrevision;
+               gccontext->gcdate = gcicaps.gcdate;
+               gccontext->gctime = gcicaps.gctime;
+
+               gccontext->gccaps.l2cachefor420
+                       = (gcicaps.gcfeatures2.reg.l2cachefor420 != 0);
+
+               if (gcicaps.gcfeatures3.reg.newfeatures0) {
+                       gccontext->gccaps.maxsource = 8;
+                       gccontext->gccaps.strictalign = false;
+               } else {
+                       gccontext->gccaps.maxsource = 4;
+                       gccontext->gccaps.strictalign = true;
+               }
+
+               gccontext->gccaps.swizzlefixed
+                       = (gcicaps.gcfeatures3.reg.deenhancements1 != 0);
+
+               GCDBG(GCZONE_INIT, "chip model: %X\n",
+                     gccontext->gcmodel);
+               GCDBG(GCZONE_INIT, "chip revision: %X\n",
+                     gccontext->gcrevision);
+               GCDBG(GCZONE_INIT, "chip date: %X\n",
+                     gccontext->gcdate);
+               GCDBG(GCZONE_INIT, "chip time: %X\n",
+                     gccontext->gctime);
+               GCDBG(GCZONE_INIT, "max source: %d\n",
+                     gccontext->gccaps.maxsource);
+               GCDBG(GCZONE_INIT, "strict alignment: %d\n",
+                     gccontext->gccaps.strictalign);
+               GCDBG(GCZONE_INIT, "swizzle fixed: %d\n",
+                     gccontext->gccaps.swizzlefixed);
+       } else {
+               GCERR("failed to get chip caps.\n");
+               gccontext->gccaps.l2cachefor420 = false;
+               gccontext->gccaps.maxsource = 4;
+               gccontext->gccaps.strictalign = true;
+               gccontext->gccaps.swizzlefixed = false;
+       }
+}
+
+void bv_exit(void)
+{
+       struct gccontext *gccontext = get_context();
+       struct bvbuffmap *bvbuffmap;
+       struct list_head *head;
+       struct gcschedunmap *gcschedunmap;
+       struct gcbuffer *gcbuffer;
+       struct gcfixup *gcfixup;
+       struct gcbatch *gcbatch;
+       struct gccallbackinfo *gccallbackinfo;
+
+       while (gccontext->buffmapvac != NULL) {
+               bvbuffmap = gccontext->buffmapvac;
+               gccontext->buffmapvac = bvbuffmap->nextmap;
+               gcfree(bvbuffmap);
+       }
+
+       while (!list_empty(&gccontext->unmapvac)) {
+               head = gccontext->unmapvac.next;
+               gcschedunmap = list_entry(head, struct gcschedunmap, link);
+               list_del(head);
+               gcfree(gcschedunmap);
+       }
+
+       while (!list_empty(&gccontext->buffervac)) {
+               head = gccontext->buffervac.next;
+               gcbuffer = list_entry(head, struct gcbuffer, link);
+               list_del(head);
+               gcfree(gcbuffer);
+       }
+
+       while (!list_empty(&gccontext->fixupvac)) {
+               head = gccontext->fixupvac.next;
+               gcfixup = list_entry(head, struct gcfixup, link);
+               list_del(head);
+               gcfree(gcfixup);
+       }
+
+       while (!list_empty(&gccontext->batchvac)) {
+               head = gccontext->batchvac.next;
+               gcbatch = list_entry(head, struct gcbatch, link);
+               list_del(head);
+               gcfree(gcbatch);
+       }
+
+       while (!list_empty(&gccontext->callbacklist)) {
+               head = gccontext->callbacklist.next;
+               list_move(head, &gccontext->callbackvac);
+       }
+
+       while (!list_empty(&gccontext->callbackvac)) {
+               head = gccontext->callbackvac.next;
+               gccallbackinfo = list_entry(head, struct gccallbackinfo, link);
+               list_del(head);
+               gcfree(gccallbackinfo);
+       }
+
+       free_temp(false);
+
+       gcbv_debug_shutdown();
+}
+
+
+/*******************************************************************************
+ * Library API.
+ */
+
+enum bverror bv_map(struct bvbuffdesc *bvbuffdesc)
+{
+       enum bverror bverror;
+       struct bvbuffmap *bvbuffmap;
+
+       GCENTERARG(GCZONE_MAPPING, "bvbuffdesc = 0x%08X\n",
+                  (unsigned int) bvbuffdesc);
+
+       if (bvbuffdesc == NULL) {
+               BVSETERROR(BVERR_BUFFERDESC, "bvbuffdesc is NULL");
+               goto exit;
+       }
+
+       if (bvbuffdesc->structsize < STRUCTSIZE(bvbuffdesc, map)) {
+               BVSETERROR(BVERR_BUFFERDESC_VERS, "argument has invalid size");
+               goto exit;
+       }
+
+       bverror = do_map(bvbuffdesc, NULL, &bvbuffmap);
+
+exit:
+       GCEXITARG(GCZONE_MAPPING, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+enum bverror bv_unmap(struct bvbuffdesc *bvbuffdesc)
+{
+       enum bverror bverror = BVERR_NONE;
+       enum bverror otherbverror = BVERR_NONE;
+       struct gccontext *gccontext = get_context();
+       struct bvbuffmap *prev = NULL;
+       struct bvbuffmap *bvbuffmap;
+       struct bvbuffmapinfo *bvbuffmapinfo;
+       struct gcimap gcimap;
+
+       GCENTERARG(GCZONE_MAPPING, "bvbuffdesc = 0x%08X\n",
+                  (unsigned int) bvbuffdesc);
+
+       /* Lock access to the mapping list. */
+       GCLOCK(&gccontext->maplock);
+
+       if (bvbuffdesc == NULL) {
+               BVSETERROR(BVERR_BUFFERDESC, "bvbuffdesc is NULL");
+               goto exit;
+       }
+
+       if (bvbuffdesc->structsize < STRUCTSIZE(bvbuffdesc, map)) {
+               BVSETERROR(BVERR_BUFFERDESC_VERS, "argument has invalid size");
+               goto exit;
+       }
+
+       /* Is the buffer mapped? */
+       bvbuffmap = bvbuffdesc->map;
+       if (bvbuffmap == NULL) {
+               GCDBG(GCZONE_MAPPING, "buffer isn't mapped.\n");
+               goto exit;
+       }
+
+       /* Try to find our mapping. */
+       while (bvbuffmap != NULL) {
+               if (bvbuffmap->bv_unmap == bv_unmap)
+                       break;
+               prev = bvbuffmap;
+               bvbuffmap = bvbuffmap->nextmap;
+       }
+
+       /* Are there other implementations? */
+       if ((prev != NULL) || (bvbuffmap->nextmap != NULL)) {
+               GCDBG(GCZONE_MAPPING,
+                     "have mappings from other implementations.\n");
+
+               /* Was our mapping found? */
+               if (bvbuffmap == NULL) {
+                       GCDBG(GCZONE_MAPPING,
+                             "no mapping from our implementation.\n");
+
+                       /* No, call other implementations. */
+                       bverror = bvbuffdesc->map->bv_unmap(bvbuffdesc);
+                       goto exit;
+               }
+
+               if (bvbuffmap->structsize
+                               < STRUCTSIZE(bvbuffmap, nextmap)) {
+                       BVSETERROR(BVERR_BUFFERDESC_VERS,
+                                  "unsupported bvbuffdesc version");
+                       goto exit;
+               }
+
+               /* Remove our mapping. */
+               if (prev == NULL)
+                       bvbuffdesc->map = bvbuffmap->nextmap;
+               else
+                       prev->nextmap = bvbuffmap->nextmap;
+
+               /* Call other implementation. */
+               otherbverror = bvbuffdesc->map->bv_unmap(bvbuffdesc);
+
+               /* Add our mapping back. */
+               bvbuffmap->nextmap = bvbuffdesc->map;
+               bvbuffdesc->map = bvbuffmap;
+               prev = NULL;
+       } else {
+               GCDBG(GCZONE_MAPPING,
+                     "no mappings from other implementations.\n");
+       }
+
+       /* Get the info structure. */
+       bvbuffmapinfo = (struct bvbuffmapinfo *) bvbuffmap->handle;
+
+       GCDBG(GCZONE_MAPPING, "bvbuffmap = 0x%08X\n", (unsigned int) bvbuffmap);
+       GCDBG(GCZONE_MAPPING, "handle = 0x%08X\n", bvbuffmapinfo->handle);
+
+       /* Explicit unmapping. */
+       if (bvbuffmapinfo->usermap == 0)
+               GCERR("explicit count is already zero.\n");
+       bvbuffmapinfo->usermap = 0;
+
+       GCDBG(GCZONE_MAPPING, "explicit count = %d\n",
+               bvbuffmapinfo->usermap);
+       GCDBG(GCZONE_MAPPING, "implicit count = %d\n",
+               bvbuffmapinfo->automap);
+
+       /* Do we have implicit mappings? */
+       if (bvbuffmapinfo->automap > 0) {
+               GCDBG(GCZONE_MAPPING, "have implicit unmappings.\n");
+               goto exit;
+       }
+
+       /* Unmap the buffer. */
+       memset(&gcimap, 0, sizeof(gcimap));
+       gcimap.handle = bvbuffmapinfo->handle;
+       gc_unmap_wrapper(&gcimap);
+       if (gcimap.gcerror != GCERR_NONE) {
+               BVSETERROR(BVERR_OOM, "unable to free gccore memory");
+               goto exit;
+       }
+
+       /* Remove from the buffer descriptor list. */
+       if (prev == NULL)
+               bvbuffdesc->map = bvbuffmap->nextmap;
+       else
+               prev->nextmap = bvbuffmap->nextmap;
+
+       /* Invalidate the record. */
+       bvbuffmap->structsize = 0;
+
+       /* Add to the vacant list. */
+       bvbuffmap->nextmap = gccontext->buffmapvac;
+       gccontext->buffmapvac = bvbuffmap;
+
+exit:
+       /* Unlock access to the mapping list. */
+       GCUNLOCK(&gccontext->maplock);
+
+       GCEXITARG(GCZONE_MAPPING, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+enum bverror bv_blt(struct bvbltparams *bvbltparams)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gccontext *gccontext = get_context();
+       struct gcalpha *gca = NULL;
+       struct gcalpha _gca;
+       unsigned int op, type, blend, format;
+       unsigned int batchexec = 0;
+       bool nop = false;
+       struct gcbatch *gcbatch;
+       struct gcsurface *dstinfo;
+       struct bvrect *dstrect;
+       bool dstonly, src1used, src2used, maskused;
+       struct gcsurface srcinfo[2];
+       unsigned short rop;
+       struct gcicommit gcicommit;
+       int i, srccount, res;
+
+       GCENTERARG(GCZONE_BLIT, "bvbltparams = 0x%08X\n",
+                  (unsigned int) bvbltparams);
+
+       /* Verify blt parameters structure. */
+       if (bvbltparams == NULL) {
+               BVSETERROR(BVERR_BLTPARAMS_VERS, "bvbltparams is NULL");
+               goto exit;
+       }
+
+       if (bvbltparams->structsize < STRUCTSIZE(bvbltparams, callbackdata)) {
+               BVSETERROR(BVERR_BLTPARAMS_VERS, "argument has invalid size");
+               goto exit;
+       }
+
+       /* Reset the error message. */
+       bvbltparams->errdesc = NULL;
+
+       /* Verify the destination parameters structure. */
+       res = verify_surface(0, (union bvinbuff *) &bvbltparams->dstdesc,
+                               bvbltparams->dstgeom);
+       if (res != -1) {
+               BVSETBLTSURFERROR(res, g_destsurferr);
+               goto exit;
+       }
+
+       /* Extract the operation flags. */
+       op = (bvbltparams->flags & BVFLAG_OP_MASK) >> BVFLAG_OP_SHIFT;
+       type = (bvbltparams->flags & BVFLAG_BATCH_MASK) >> BVFLAG_BATCH_SHIFT;
+       GCDBG(GCZONE_BLIT, "op = %d\n", op);
+       GCDBG(GCZONE_BLIT, "type = %d\n", type);
+
+       switch (type) {
+       case (BVFLAG_BATCH_NONE >> BVFLAG_BATCH_SHIFT):
+               bverror = allocate_batch(bvbltparams, &gcbatch);
+               if (bverror != BVERR_NONE) {
+                       bvbltparams->errdesc = gccontext->bverrorstr;
+                       goto exit;
+               }
+
+               batchexec = 1;
+               gcbatch->batchflags = 0x7FFFFFFF;
+
+               GCDBG(GCZONE_BATCH, "BVFLAG_BATCH_NONE(0x%08X)\n",
+                     (unsigned int) gcbatch);
+               break;
+
+       case (BVFLAG_BATCH_BEGIN >> BVFLAG_BATCH_SHIFT):
+               bverror = allocate_batch(bvbltparams, &gcbatch);
+               if (bverror != BVERR_NONE) {
+                       bvbltparams->errdesc = gccontext->bverrorstr;
+                       goto exit;
+               }
+
+               bvbltparams->batch = (struct bvbatch *) gcbatch;
+
+               batchexec = 0;
+               bvbltparams->batchflags =
+               gcbatch->batchflags = 0x7FFFFFFF;
+
+               GCDBG(GCZONE_BATCH, "BVFLAG_BATCH_BEGIN(0x%08X)\n",
+                     (unsigned int) gcbatch);
+               break;
+
+       case (BVFLAG_BATCH_CONTINUE >> BVFLAG_BATCH_SHIFT):
+               gcbatch = (struct gcbatch *) bvbltparams->batch;
+               if (gcbatch == NULL) {
+                       BVSETBLTERROR(BVERR_BATCH, "batch is not initialized");
+                       goto exit;
+               }
+
+               if (gcbatch->structsize < STRUCTSIZE(gcbatch, unmap)) {
+                       BVSETBLTERROR(BVERR_BATCH, "invalid batch");
+                       goto exit;
+               }
+
+               batchexec = 0;
+               gcbatch->batchflags = bvbltparams->batchflags;
+
+               GCDBG(GCZONE_BATCH, "BVFLAG_BATCH_CONTINUE(0x%08X)\n",
+                     (unsigned int) gcbatch);
+               break;
+
+       case (BVFLAG_BATCH_END >> BVFLAG_BATCH_SHIFT):
+               gcbatch = (struct gcbatch *) bvbltparams->batch;
+               if (gcbatch == NULL) {
+                       BVSETBLTERROR(BVERR_BATCH, "batch is not initialized");
+                       goto exit;
+               }
+
+               if (gcbatch->structsize < STRUCTSIZE(gcbatch, unmap)) {
+                       BVSETBLTERROR(BVERR_BATCH, "invalid batch");
+                       goto exit;
+               }
+
+               batchexec = 1;
+               nop = (bvbltparams->batchflags & BVBATCH_ENDNOP) != 0;
+               gcbatch->batchflags = bvbltparams->batchflags;
+
+               GCDBG(GCZONE_BATCH, "BVFLAG_BATCH_END(0x%08X)\n",
+                     (unsigned int) gcbatch);
+               break;
+
+       default:
+               BVSETBLTERROR(BVERR_BATCH, "unrecognized batch type");
+               goto exit;
+       }
+
+       GCDBG(GCZONE_BATCH, "batchflags=0x%08X\n",
+               (unsigned int) gcbatch->batchflags);
+
+       if (!nop) {
+               /* Get a shortcut to the destination rectangle. */
+               dstrect = &bvbltparams->dstrect;
+
+               /* Verify the batch change flags. */
+               GCVERIFYBATCH(gcbatch->batchflags >> 12,
+                             &gcbatch->prevdstrect, dstrect);
+
+               switch (op) {
+               case (BVFLAG_ROP >> BVFLAG_OP_SHIFT):
+                       GCDBG(GCZONE_BLIT, "BVFLAG_ROP\n");
+
+                       rop = bvbltparams->op.rop;
+                       dstonly = (rop == 0x0000) | (rop == 0x5555)
+                               | (rop == 0xAAAA) | (rop == 0xFFFF);
+                       src1used = ((((rop & 0xCCCC)  >> 2)
+                                ^    (rop & 0x3333)) != 0);
+                       src2used = ((((rop & 0xF0F0)  >> 4)
+                                ^    (rop & 0x0F0F)) != 0);
+                       maskused = ((((rop & 0xFF00)  >> 8)
+                                ^    (rop & 0x00FF)) != 0);
+                       break;
+
+               case (BVFLAG_BLEND >> BVFLAG_OP_SHIFT):
+                       GCDBG(GCZONE_BLIT, "BVFLAG_BLEND\n");
+
+                       blend = bvbltparams->op.blend;
+                       format = blend & BVBLENDDEF_FORMAT_MASK;
+                       maskused = (blend & BVBLENDDEF_REMOTE) != 0;
+                       rop = 0xCCCC;
+
+                       GCDBG(GCZONE_BLIT, "blend = 0x%08X (%s)\n",
+                             blend, gc_bvblend_name(blend));
+
+                       if (format != BVBLENDDEF_FORMAT_CLASSIC) {
+                               BVSETBLTERROR(BVERR_BLEND,
+                                             "unrecognized blend format");
+                               goto exit;
+                       }
+
+                       if (blend == BVBLEND_CLEAR) {
+                               dstonly = true;
+                               src1used = false;
+                               src2used = false;
+                       } else {
+                               bverror = parse_blend(bvbltparams, blend,
+                                                     &_gca);
+                               if (bverror != BVERR_NONE)
+                                       goto exit;
+
+                               gca = &_gca;
+
+                               dstonly = false;
+                               src1used = gca->src1used;
+                               src2used = gca->src2used;
+                       }
+                       break;
+
+               case (BVFLAG_FILTER >> BVFLAG_OP_SHIFT):
+                       GCDBG(GCZONE_BLIT, "BVFLAG_FILTER\n");
+                       BVSETBLTERROR(BVERR_OP,
+                                     "filter operation not supported");
+                       goto exit;
+
+               default:
+                       BVSETBLTERROR(BVERR_OP, "unrecognized operation");
+                       goto exit;
+               }
+
+               /* Reset the number of sources. */
+               srccount = 0;
+
+               /* Determine what's changed in the destination. */
+               dstinfo = &gcbatch->dstinfo;
+               dstinfo->surfdirty
+                       = ((gcbatch->batchflags & BVBATCH_DST) != 0);
+               dstinfo->cliprectdirty
+                       = ((gcbatch->batchflags & BVBATCH_CLIPRECT) != 0);
+               dstinfo->destrectdirty
+                       = ((gcbatch->batchflags & BVBATCH_DESTRECT) != 0);
+               dstinfo->rectdirty
+                       = dstinfo->cliprectdirty || dstinfo->destrectdirty;
+
+               /* Verify the src1 parameters structure. */
+               if (src1used) {
+                       GCDBG(GCZONE_SRC, "source #1: used\n");
+                       res = verify_surface(
+                               bvbltparams->flags & BVBATCH_TILE_SRC1,
+                               &bvbltparams->src1, bvbltparams->src1geom);
+                       if (res != -1) {
+                               BVSETBLTSURFERROR(res, g_src1surferr);
+                               goto exit;
+                       }
+
+                       /* Verify the batch change flags. */
+                       GCVERIFYBATCH(gcbatch->batchflags >> 14,
+                                     &gcbatch->prevsrc1rect,
+                                     &bvbltparams->src1rect);
+
+                       /* Same as the destination? */
+                       if (same_phys_area(bvbltparams->src1.desc,
+                                          &bvbltparams->src1rect,
+                                          bvbltparams->dstdesc,
+                                          dstrect)) {
+                               GCDBG(GCZONE_BLIT,
+                                     "  src1 same as destination\n");
+                       } else {
+                               bverror = parse_source(bvbltparams,
+                                                      gcbatch,
+                                                      &srcinfo[srccount],
+                                                      0, rop);
+                               if (bverror != BVERR_NONE)
+                                       goto exit;
+
+                               srccount += 1;
+                       }
+               }
+
+               /* Verify the src2 parameters structure. */
+               if (src2used) {
+                       GCDBG(GCZONE_SRC, "source #2: used\n");
+                       res = verify_surface(
+                               bvbltparams->flags & BVBATCH_TILE_SRC2,
+                               &bvbltparams->src2, bvbltparams->src2geom);
+                       if (res != -1) {
+                               BVSETBLTSURFERROR(res, g_src2surferr);
+                               goto exit;
+                       }
+
+                       /* Verify the batch change flags. */
+                       GCVERIFYBATCH(gcbatch->batchflags >> 16,
+                                     &gcbatch->prevsrc2rect,
+                                     &bvbltparams->src2rect);
+
+                       /* Same as the destination? */
+                       if (same_phys_area(bvbltparams->src2.desc,
+                                          &bvbltparams->src2rect,
+                                          bvbltparams->dstdesc,
+                                          dstrect)) {
+                               GCDBG(GCZONE_BLIT,
+                                     "  src2 same as destination\n");
+                       } else {
+                               bverror = parse_source(bvbltparams,
+                                                      gcbatch,
+                                                      &srcinfo[srccount],
+                                                      1, rop);
+                               if (bverror != BVERR_NONE)
+                                       goto exit;
+
+                               srccount += 1;
+                       }
+               }
+
+               /* Verify the mask parameters structure. */
+               if (maskused) {
+                       GCDBG(GCZONE_MASK, "mask: used\n");
+                       res = verify_surface(
+                               bvbltparams->flags & BVBATCH_TILE_MASK,
+                               &bvbltparams->mask, bvbltparams->maskgeom);
+                       if (res != -1) {
+                               BVSETBLTSURFERROR(res, g_masksurferr);
+                               goto exit;
+                       }
+
+                       /* Verify the batch change flags. */
+                       GCVERIFYBATCH(gcbatch->batchflags >> 18,
+                                     &gcbatch->prevmaskrect,
+                                     &bvbltparams->maskrect);
+
+                       BVSETBLTERROR(BVERR_OP,
+                                     "operation with mask not supported");
+                       goto exit;
+               }
+
+               /* Destination only? */
+               if (dstonly) {
+                       static unsigned int pixel[8];
+                       static struct bvbuffdesc dummysrcdesc;
+                       static struct gcsurface dummysrcinfo;
+                       static bool dummyinit;
+
+                       GCDBG(GCZONE_BLIT, "target only operation.\n");
+
+                       if (!dummyinit) {
+                               GCDBG(GCZONE_BLIT,
+                                     "initializing dummy source.\n");
+
+                               dummysrcdesc.structsize
+                                       = sizeof(struct bvbuffdesc);
+                               dummysrcdesc.virtaddr = pixel;
+                               dummysrcdesc.length = sizeof(pixel);
+
+                               dummysrcinfo.buf.desc = &dummysrcdesc;
+                               dummysrcinfo.width = 1;
+                               dummysrcinfo.height = 1;
+                               dummysrcinfo.stride1 = sizeof(pixel);
+                               dummysrcinfo.rect.orig.right = 1;
+                               dummysrcinfo.rect.orig.bottom = 1;
+
+                               parse_format(bvbltparams, OCDFMT_RGBA24,
+                                            &dummysrcinfo.format);
+
+                               dummyinit = true;
+                       }
+
+                       dummysrcinfo.rop = rop;
+                       bverror = do_op(bvbltparams, gcbatch,
+                                       1, &dummysrcinfo, NULL);
+                       if (bverror != BVERR_NONE)
+                               goto exit;
+               } else {
+                       GCDBG(GCZONE_BLIT, "srccount = %d\n", srccount);
+
+                       if (srccount == 0) {
+                               BVSETBLTERROR(BVERR_OP,
+                                             "operation not supported");
+                               goto exit;
+                       }
+
+                       for (i = 0; i < srccount; i += 1) {
+                               bverror = do_op(bvbltparams, gcbatch,
+                                               srccount, &srcinfo[i], gca);
+                               if (bverror != BVERR_NONE)
+                                       goto exit;
+                       }
+               }
+       }
+
+       if (batchexec) {
+               struct gcmoflush *flush;
+
+               GCDBG(GCZONE_BLIT, "preparing to submit the batch.\n");
+
+               /* Finalize the current operation. */
+               bverror = gcbatch->batchend(bvbltparams, gcbatch);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Add PE flush. */
+               GCDBG(GCZONE_BLIT, "appending the flush.\n");
+               bverror = claim_buffer(bvbltparams, gcbatch,
+                                      sizeof(struct gcmoflush),
+                                      (void **) &flush);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               flush->flush_ldst = gcmoflush_flush_ldst;
+               flush->flush.reg = gcregflush_pe2D;
+
+               /* Process asynchronous operation. */
+               if ((bvbltparams->flags & BVFLAG_ASYNC) == 0) {
+                       GCDBG(GCZONE_BLIT, "synchronous batch.\n");
+                       gcicommit.callback = NULL;
+                       gcicommit.callbackparam = NULL;
+                       gcicommit.asynchronous = false;
+               } else {
+                       struct gccallbackinfo *gccallbackinfo;
+
+                       GCDBG(GCZONE_BLIT, "asynchronous batch (0x%08X):\n",
+                             bvbltparams->flags);
+
+                       if (bvbltparams->callbackfn == NULL) {
+                               GCDBG(GCZONE_BLIT, "no callback given.\n");
+                               gcicommit.callback = NULL;
+                               gcicommit.callbackparam = NULL;
+                       } else {
+                               bverror = get_callbackinfo(&gccallbackinfo);
+                               if (bverror != BVERR_NONE) {
+                                       BVSETBLTERROR(BVERR_OOM,
+                                                     "callback allocation "
+                                                     "failed");
+                                       goto exit;
+                               }
+
+                               gccallbackinfo->info.callback.fn
+                                       = bvbltparams->callbackfn;
+                               gccallbackinfo->info.callback.data
+                                       = bvbltparams->callbackdata;
+
+                               gcicommit.callback = callbackbltsville;
+                               gcicommit.callbackparam = gccallbackinfo;
+
+                               GCDBG(GCZONE_BLIT,
+                                     "gcbv_callback = 0x%08X\n",
+                                     (unsigned int) gcicommit.callback);
+                               GCDBG(GCZONE_BLIT,
+                                     "gcbv_param    = 0x%08X\n",
+                                     (unsigned int) gcicommit.callbackparam);
+                               GCDBG(GCZONE_BLIT,
+                                     "bltsville_callback = 0x%08X\n",
+                                     (unsigned int)
+                                     gccallbackinfo->info.callback.fn);
+                               GCDBG(GCZONE_BLIT,
+                                     "bltsville_param    = 0x%08X\n",
+                                     (unsigned int)
+                                     gccallbackinfo->info.callback.data);
+                       }
+
+                       gcicommit.asynchronous = true;
+               }
+
+               /* Process scheduled unmappings. */
+               do_unmap_implicit(gcbatch);
+
+               INIT_LIST_HEAD(&gcicommit.unmap);
+               list_splice_init(&gcbatch->unmap, &gcicommit.unmap);
+
+               /* Pass the batch for execution. */
+               GCDUMPBATCH(gcbatch);
+
+               gcicommit.gcerror = GCERR_NONE;
+               gcicommit.entrypipe = GCPIPE_2D;
+               gcicommit.exitpipe = GCPIPE_2D;
+
+               INIT_LIST_HEAD(&gcicommit.buffer);
+               list_splice_init(&gcbatch->buffer, &gcicommit.buffer);
+
+               GCDBG(GCZONE_BLIT, "submitting the batch.\n");
+               gc_commit_wrapper(&gcicommit);
+
+               /* Move the lists back to the batch. */
+               list_splice_init(&gcicommit.buffer, &gcbatch->buffer);
+               list_splice_init(&gcicommit.unmap, &gcbatch->unmap);
+
+               /* Error? */
+               if (gcicommit.gcerror != GCERR_NONE) {
+                       switch (gcicommit.gcerror) {
+                       case GCERR_OODM:
+                       case GCERR_CTX_ALLOC:
+                               BVSETBLTERROR(BVERR_OOM,
+                                             "unable to allocate gccore "
+                                             "memory");
+                               goto exit;
+                       default:
+                               BVSETBLTERROR(BVERR_RSRC,
+                                             "gccore error");
+
+                               goto exit;
+                       }
+               }
+
+               GCDBG(GCZONE_BLIT, "batch is submitted.\n");
+       }
+
+exit:
+       if ((gcbatch != NULL) && batchexec) {
+               free_batch(gcbatch);
+               bvbltparams->batch = NULL;
+       }
+
+       GCEXITARG(GCZONE_BLIT, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+enum bverror bv_cache(struct bvcopparams *copparams)
+{
+       enum bverror bverror = BVERR_NONE;
+       unsigned int bytespp = 0; /* bytes per pixel */
+       unsigned long vert_offset, horiz_offset;
+       unsigned int true_width, true_height;
+
+       struct c2dmrgn rgn[3];
+       int container_size = 0;
+
+       unsigned long subsample;
+       unsigned long vendor;
+       unsigned long layout;
+       unsigned long size;
+       unsigned long container;
+
+       subsample = copparams->geom->format & OCDFMTDEF_SUBSAMPLE_MASK;
+       vendor = copparams->geom->format & OCDFMTDEF_VENDOR_MASK;
+       layout = copparams->geom->format & OCDFMTDEF_LAYOUT_MASK;
+       size = copparams->geom->format & OCDFMTDEF_COMPONENTSIZEMINUS1_MASK;
+       container = copparams->geom->format & OCDFMTDEF_CONTAINER_MASK;
+
+       if (vendor != OCDFMTDEF_VENDOR_ALL) {
+               bverror = BVERR_FORMAT;
+               goto exit;
+       }
+
+       if (copparams->geom->orientation % 180 != 0) {
+               true_width = copparams->rect->height;
+               true_height = copparams->rect->width;
+       } else {
+               true_width = copparams->rect->width;
+               true_height = copparams->rect->height;
+       }
+
+       switch (container) {
+       case OCDFMTDEF_CONTAINER_8BIT:
+               container_size = 8;
+               break;
+
+       case OCDFMTDEF_CONTAINER_16BIT:
+               container_size = 16;
+               break;
+
+       case OCDFMTDEF_CONTAINER_24BIT:
+               container_size = 24;
+               break;
+
+       case OCDFMTDEF_CONTAINER_32BIT:
+               container_size = 32;
+               break;
+
+       case OCDFMTDEF_CONTAINER_48BIT:
+               container_size = 48;
+               break;
+
+       case OCDFMTDEF_CONTAINER_64BIT:
+               container_size = 64;
+               break;
+       }
+
+       switch (layout) {
+       case OCDFMTDEF_PACKED:
+               switch (subsample) {
+               case OCDFMTDEF_SUBSAMPLE_NONE:
+                       if (size >= 8) {
+                               bytespp = container_size / 8;
+                       } else {
+                               GCERR("format not supported.\n");
+                               bverror = BVERR_FORMAT;
+                               goto exit;
+                       }
+                       break;
+
+               case OCDFMTDEF_SUBSAMPLE_422_YCbCr:
+                       bytespp = (container_size / 2) / 8;
+                       break;
+
+               default:
+                       bverror = BVERR_FORMAT;
+                       goto exit;
+               }
+
+               rgn[0].span = true_width * bytespp;
+               rgn[0].lines = true_height;
+               rgn[0].stride = copparams->geom->virtstride;
+               horiz_offset = copparams->rect->left * bytespp;
+               vert_offset = copparams->rect->top;
+
+               rgn[0].start = (void *) ((unsigned long)
+                               copparams->desc->virtaddr +
+                               vert_offset * rgn[0].stride +
+                               horiz_offset);
+
+               gcbvcacheop(1, rgn, copparams->cacheop);
+               break;
+
+       case OCDFMTDEF_2_PLANE_YCbCr:
+               /* 1 byte per pixel */
+               rgn[0].span = true_width;
+               rgn[0].lines = true_height;
+               rgn[0].stride = copparams->geom->virtstride;
+               rgn[0].start = (void *)
+                       ((unsigned long) copparams->desc->virtaddr +
+                        copparams->rect->top * rgn[0].stride +
+                        copparams->rect->left);
+
+               rgn[1].span = true_width;
+               rgn[1].lines = true_height / 2;
+               rgn[1].stride = copparams->geom->virtstride;
+               rgn[1].start = rgn[0].start +
+                       copparams->geom->height * rgn[0].stride;
+
+               GCDBG(GCZONE_CACHE,
+                     "virtaddr %p start[0] 0x%08x start[1] 0x%08x\n",
+                     copparams->desc->virtaddr, rgn[0].start, rgn[1].start);
+
+               gcbvcacheop(2, rgn, copparams->cacheop);
+               break;
+
+       default:
+               GCERR("format 0x%x (%d) not supported.\n",
+                     copparams->geom->format, copparams->geom->format);
+               bverror = BVERR_FORMAT;
+               break;
+       }
+
+exit:
+       if (bverror != BVERR_NONE)
+               GCERR("bverror = %d\n", bverror);
+
+       return bverror;
+}
diff --git a/drivers/misc/gcx/gcbv/gcbv.h b/drivers/misc/gcx/gcbv/gcbv.h
new file mode 100644 (file)
index 0000000..7e5696a
--- /dev/null
@@ -0,0 +1,615 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Vivante Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCBV_H
+#define GCBV_H
+
+#include "gcmain.h"
+
+/*******************************************************************************
+ * Miscellaneous defines and macros.
+ */
+
+#define GC_MAX_BASE_ALIGN 64
+
+#define GCBV_BATCH_FINALIZE_SRCCOUNT       (1 << 0)
+#define GCBV_BATCH_FINALIZE_MULTISRC       (1 << 1)
+#define GCBV_BATCH_FINALIZE_ALIGN          (1 << 2)
+#define GCBV_BATCH_FINALIZE_FLAGS_DST      (1 << 3)
+#define GCBV_BATCH_FINALIZE_FLAGS_DESTRECT (1 << 4)
+#define GCBV_BATCH_FINALIZE_FLAGS_CLIPRECT (1 << 5)
+#define GCBV_BATCH_FINALIZE_OPERATION      (1 << 6)
+
+#if !defined(BVBATCH_DESTRECT)
+#define BVBATCH_DESTRECT (BVBATCH_DSTRECT_ORIGIN | BVBATCH_DSTRECT_SIZE)
+#endif
+
+#if !defined(BVBATCH_SRC1RECT)
+#define BVBATCH_SRC1RECT (BVBATCH_SRC1RECT_ORIGIN | BVBATCH_SRC1RECT_SIZE)
+#endif
+
+#if !defined(BVBATCH_SRC2RECT)
+#define BVBATCH_SRC2RECT (BVBATCH_SRC2RECT_ORIGIN | BVBATCH_SRC2RECT_SIZE)
+#endif
+
+#define STRUCTSIZE(structptr, lastmember) \
+( \
+       (size_t) &structptr->lastmember + \
+       sizeof(structptr->lastmember) - \
+       (size_t) structptr \
+)
+
+#define GET_MAP_HANDLE(map) \
+( \
+       ((struct bvbuffmapinfo *) map->handle)->handle \
+)
+
+#define GC_CLIP_RESET_LEFT     ((unsigned short) 0)
+#define GC_CLIP_RESET_TOP      ((unsigned short) 0)
+#define GC_CLIP_RESET_RIGHT    ((unsigned short) ((1 << 15) - 1))
+#define GC_CLIP_RESET_BOTTOM   ((unsigned short) ((1 << 15) - 1))
+
+#define BVSETERROR(error, message, ...) \
+do { \
+       struct gccontext *tmpcontext = get_context(); \
+       snprintf(tmpcontext->bverrorstr, sizeof(tmpcontext->bverrorstr), \
+                message, ##__VA_ARGS__); \
+       GCDUMPSTRING("%s(%d): [ERROR] %s\n", __func__, __LINE__, \
+                    tmpcontext->bverrorstr); \
+       bverror = error; \
+} while (0)
+
+#define BVSETBLTERROR(error, message, ...) \
+do { \
+       struct gccontext *tmpcontext = get_context(); \
+       snprintf(tmpcontext->bverrorstr, sizeof(tmpcontext->bverrorstr), \
+                message, ##__VA_ARGS__); \
+       GCDUMPSTRING("%s(%d): [ERROR] %s\n", __func__, __LINE__, \
+                    tmpcontext->bverrorstr); \
+       bvbltparams->errdesc = tmpcontext->bverrorstr; \
+       bverror = error; \
+} while (0)
+
+#define GCPRINT_RECT(zone, name, rect) \
+{ \
+       GCDBG(zone, \
+             name " = (%d,%d)-(%d,%d), %dx%d\n", \
+             (rect)->left, (rect)->top, \
+             (rect)->right, (rect)->bottom, \
+             (rect)->right - (rect)->left, \
+             (rect)->bottom - (rect)->top); \
+}
+
+
+/*******************************************************************************
+ * Kernel table definitions.
+ */
+
+#define GC_TAP_COUNT           9
+#define GC_PHASE_BITS          5
+#define GC_PHASE_MAX_COUNT     (1 << GC_PHASE_BITS)
+#define GC_PHASE_LOAD_COUNT    (GC_PHASE_MAX_COUNT / 2 + 1)
+#define GC_COEFFICIENT_COUNT   (GC_PHASE_LOAD_COUNT * GC_TAP_COUNT)
+#define GC_FILTER_CACHE_MAX    10
+
+enum gcfiltertype {
+       GC_FILTER_SYNC,
+       GC_FILTER_BLUR,
+
+       /* Number of supported filter types. */
+       GC_FILTER_COUNT
+};
+
+struct gcfilterkernel {
+       enum gcfiltertype type;
+       unsigned int kernelsize;
+       unsigned int srcsize;
+       unsigned int dstsize;
+       unsigned int scalefactor;
+       short kernelarray[GC_COEFFICIENT_COUNT];
+       struct list_head link;
+};
+
+struct gcfiltercache {
+       unsigned int count;
+       struct list_head list;                  /* gcfilterkernel */
+};
+
+
+/*******************************************************************************
+ * Global data structure.
+ */
+
+struct gccaps {
+       bool l2cachefor420;
+       unsigned int maxsource;
+       bool strictalign;
+       bool swizzlefixed;
+};
+
+struct gccontext {
+       /* Last generated error message. */
+       char bverrorstr[128];
+
+       /* Capabilities and characteristics. */
+       unsigned int gcmodel;
+       unsigned int gcrevision;
+       unsigned int gcdate;
+       unsigned int gctime;
+       struct gccaps gccaps;
+
+       /* Dynamically allocated structure cache. */
+       struct bvbuffmap *buffmapvac;           /* bvbuffmap */
+       struct list_head unmapvac;              /* gcschedunmap */
+       struct list_head buffervac;             /* gcbuffer */
+       struct list_head fixupvac;              /* gcfixup */
+       struct list_head batchvac;              /* gcbatch */
+
+       /* Callback lists. */
+       struct list_head callbacklist;          /* gccallbackinfo */
+       struct list_head callbackvac;           /* gccallbackinfo */
+
+       /* Access locks. */
+       GCLOCK_TYPE batchlock;
+       GCLOCK_TYPE bufferlock;
+       GCLOCK_TYPE fixuplock;
+       GCLOCK_TYPE maplock;
+       GCLOCK_TYPE callbacklock;
+
+       /* Kernel table cache. */
+       struct gcfilterkernel *loadedfilter;    /* gcfilterkernel */
+       struct gcfiltercache filtercache[GC_FILTER_COUNT][GC_TAP_COUNT];
+
+       /* Temporary buffer descriptor. */
+       struct bvbuffdesc *tmpbuffdesc;
+       void *tmpbuff;
+};
+
+
+/*******************************************************************************
+ * Mapping structures.
+ */
+
+/* bvbuffmap struct attachment. */
+struct bvbuffmapinfo {
+       /* Mapped handle for the buffer. */
+       unsigned long handle;
+
+       /* Number of times the client explicitly mapped this buffer. */
+       int usermap;
+
+       /* Number of times implicit mapping happened. */
+       int automap;
+};
+
+
+/*******************************************************************************
+ * Color format.
+ */
+
+#define BVFMT_RGB      1
+#define BVFMT_YUV      2
+
+struct bvcomponent {
+       unsigned int shift;
+       unsigned int size;
+       unsigned int mask;
+};
+
+struct bvcsrgb {
+       struct bvcomponent r;
+       struct bvcomponent g;
+       struct bvcomponent b;
+       struct bvcomponent a;
+};
+
+struct bvformatxlate {
+       unsigned int type;
+       unsigned int bitspp;
+       unsigned int allocbitspp;
+       unsigned int format;
+       unsigned int swizzle;
+       unsigned int endian;
+       bool premultiplied;
+       bool zerofill;
+
+       union {
+               struct {
+                       const struct bvcsrgb *comp;
+               } rgb;
+
+               struct {
+                       unsigned int std;
+                       unsigned int planecount;
+                       unsigned int xsample;
+                       unsigned int ysample;
+               } yuv;
+       } cs;
+};
+
+
+/*******************************************************************************
+ * Alpha blending.
+ */
+
+/* Alpha blending hardware configuration. */
+struct gcblendconfig {
+       unsigned char factor_mode;
+       unsigned char color_reverse;
+
+       bool src1used;
+       bool src2used;
+};
+
+/* Alpha blending descriptor. */
+struct gcalpha {
+       bool globalcolorenable;
+       unsigned int globalcolor;
+
+       struct gcblendconfig *k1;
+       struct gcblendconfig *k2;
+
+       struct gcblendconfig *srcconfig;
+       struct gcblendconfig *dstconfig;
+
+       bool src1used;
+       bool src2used;
+};
+
+
+/*******************************************************************************
+ * Rotation and mirror defines.
+ */
+
+#define GCREG_ROT_ANGLE_ROT0   0x0
+#define GCREG_ROT_ANGLE_ROT90  0x4
+#define GCREG_ROT_ANGLE_ROT180 0x5
+#define GCREG_ROT_ANGLE_ROT270 0x6
+
+#define ROT_ANGLE_INVALID      -1
+#define ROT_ANGLE_0            0
+#define ROT_ANGLE_90           1
+#define ROT_ANGLE_180          2
+#define ROT_ANGLE_270          3
+
+#define GCREG_MIRROR_NONE      0x0
+#define GCREG_MIRROR_X         0x1
+#define GCREG_MIRROR_Y         0x2
+#define GCREG_MIRROR_XY                0x3
+
+extern const unsigned int rotencoding[];
+
+
+/*******************************************************************************
+ * Surface descriptor.
+ */
+
+struct gcrectset {
+       /* Render rectangle as specified by the client. */
+       struct gcrect orig;
+
+       /* Clipped rectangle. */
+       struct gcrect clip;
+
+       /* Clipped rectangle adjusted for base address misalignment. */
+       struct gcrect adj;
+};
+
+struct gcsurface {
+       /* Surface change flags. */
+       bool surfdirty;
+       bool rectdirty;
+       bool destrectdirty;
+       bool cliprectdirty;
+
+       /* BLTsville source index (-1 for dst, 0 for src1 and 1 for src2). */
+       int index;
+
+       /* Surface buffer descriptor. */
+       union bvinbuff buf;
+
+       /* Geometry size as specified by the client. */
+       unsigned int width;
+       unsigned int height;
+
+       /* Geometry size adjusted for base address misalignment. */
+       unsigned int adjwidth;
+       unsigned int adjheight;
+
+       /* Physical size of the surface (adjusted and 0 degree rotated). */
+       unsigned int physwidth;
+       unsigned int physheight;
+
+       /* Plane strides. */
+       long stride1;
+       long stride2;
+       long stride3;
+
+       /* Base address alignment in pixels. */
+       int xpixalign;
+       int ypixalign;
+
+       /* Base address alignment in bytes. */
+       int bytealign1;
+       int bytealign2;
+       int bytealign3;
+
+       /* Surface format. */
+       struct bvformatxlate format;
+
+       /* Rotation angle. */
+       int angle;
+       int adjangle;
+
+       /* Render rectangles. */
+       struct gcrectset rect;
+
+       /* Aux render rectangles. */
+       bool haveaux;
+       struct gcrectset auxrect;
+
+       /* Mirror setting. */
+       unsigned int mirror;
+
+       /* ROP. */
+       unsigned short rop;
+
+       /* Blending info. */
+       struct gcalpha *gca;
+       bool globalcolorenable;
+       unsigned int globalcolor;
+       unsigned char srcglobalpremul;
+       unsigned char srcglobalmode;
+       unsigned char dstglobalmode;
+};
+
+
+/*******************************************************************************
+ * Batch structures.
+ */
+
+/* Operation finalization call. */
+struct gcbatch;
+typedef enum bverror (*gcbatchend) (struct bvbltparams *bvbltparams,
+                                   struct gcbatch *gcbatch);
+
+/* Blit states. */
+struct gcblit {
+       /* Number of sources in the operation. */
+       unsigned int srccount;
+
+       /* Multi source enable flag. */
+       bool multisrc;
+
+       /* Computed destination rectangle coordinates; in multi-source
+        * setup can be modified to match new destination and source
+        * geometry. */
+       struct gcrect dstrect;
+
+       /* Block walker enable. */
+       bool blockenable;
+
+       /* Destination format and swizzle. */
+       unsigned int format;
+       unsigned int swizzle;
+       unsigned int endian;
+};
+
+/* Filter states. */
+struct gcfilter {
+       /* Kernel size. */
+       unsigned int horkernelsize;
+       unsigned int verkernelsize;
+
+       /* Scale factors. */
+       unsigned int horscalefactor;
+       unsigned int verscalefactor;
+};
+
+/* Batch header. */
+struct gcbatch {
+       /* Used to ID structure version. */
+       unsigned int structsize;
+
+       /* Batch change flags. */
+       unsigned long batchflags;
+
+       /* Pointer to the function to finalize the current operation. */
+       gcbatchend batchend;
+
+       /* State of the current operation. */
+       struct {
+               struct gcblit blit;
+               struct gcfilter filter;
+       } op;
+
+       /* Destination surface. */
+       struct gcsurface dstinfo;
+
+       /* Clipping deltas; used to correct the source coordinates for
+        * single source blits. */
+       struct gcrect clipdelta;
+
+       /* Destination rectangle adjustment offsets. */
+       int dstoffsetX;
+       int dstoffsetY;
+
+#if GCDEBUG_ENABLE
+       /* Rectangle validation storage. */
+       struct bvrect prevdstrect;
+       struct bvrect prevsrc1rect;
+       struct bvrect prevsrc2rect;
+       struct bvrect prevmaskrect;
+#endif
+
+       /* Total size of the command buffer. */
+       unsigned int size;
+
+       /* Command buffer list (gcbuffer). */
+       struct list_head buffer;
+
+       /* Scheduled implicit unmappings (gcschedunmap). */
+       struct list_head unmap;
+
+       /* Batch linked list (gcbatch). */
+       struct list_head link;
+};
+
+
+/*******************************************************************************
+ * Internal API entries.
+ */
+
+/* Get the pointer to the context. */
+struct gccontext *get_context(void);
+
+/* Validation. */
+bool null_rect(struct gcrect *gcrect);
+bool valid_rect(struct gcsurface *gcsurface, struct gcrect *gcrect);
+
+/* Rotation processing. */
+void rotate_rect(int angle,
+                struct gcsurface *gcsurface, struct gcrect *rect);
+void rotate_geom(int angle, struct gcsurface *gcsurface);
+void adjust_angle(struct gcsurface *srcinfo, struct gcsurface *dstinfo);
+void process_rotation(struct gcsurface *gcsurface);
+
+/* Parsers. */
+enum bverror parse_format(struct bvbltparams *bvbltparams,
+                         enum ocdformat ocdformat,
+                         struct bvformatxlate *format);
+enum bverror parse_blend(struct bvbltparams *bvbltparams,
+                        enum bvblend blend,
+                        struct gcalpha *gca);
+enum bverror parse_destination(struct bvbltparams *bvbltparams,
+                              struct gcbatch *gcbatch);
+enum bverror parse_source(struct bvbltparams *bvbltparams,
+                         struct gcbatch *batch,
+                         struct gcsurface *srcinfo,
+                         unsigned int index,
+                         unsigned short rop);
+enum bverror parse_scalemode(struct bvbltparams *bvbltparams,
+                            struct gcbatch *batch);
+
+/* Return surface alignment offset. */
+int get_pixel_offset(struct gcsurface *gcsurface, int offset);
+
+/* Buffer mapping. */
+enum bverror do_map(struct bvbuffdesc *bvbuffdesc,
+                   struct gcbatch *gcbatch,
+                   struct bvbuffmap **map);
+void do_unmap_implicit(struct gcbatch *gcbatch);
+
+/* Batch/command buffer management. */
+enum bverror do_end(struct bvbltparams *bvbltparams,
+                   struct gcbatch *gcbatch);
+enum bverror allocate_batch(struct bvbltparams *bvbltparams,
+                           struct gcbatch **gcbatch);
+void free_batch(struct gcbatch *gcbatch);
+enum bverror append_buffer(struct bvbltparams *bvbltparams,
+                          struct gcbatch *gcbatch,
+                          struct gcbuffer **gcbuffer);
+
+enum bverror add_fixup(struct bvbltparams *bvbltparams,
+                      struct gcbatch *gcbatch,
+                      unsigned int *fixup,
+                      unsigned int surfoffset);
+enum bverror claim_buffer(struct bvbltparams *bvbltparams,
+                         struct gcbatch *gcbatch,
+                         unsigned int size,
+                         void **buffer);
+
+/* Temporary buffer management. */
+enum bverror allocate_temp(struct bvbltparams *bvbltparams,
+                          unsigned int size);
+enum bverror free_temp(bool schedule);
+
+/* Program the destination. */
+enum bverror set_dst(struct bvbltparams *bltparams,
+                    struct gcbatch *batch,
+                    struct bvbuffmap *dstmap);
+
+/* Program blending. */
+enum bverror set_blending(struct bvbltparams *bvbltparams,
+                         struct gcbatch *batch,
+                         struct gcsurface *srcinfo);
+enum bverror set_blending_index(struct bvbltparams *bvbltparams,
+                               struct gcbatch *batch,
+                               struct gcsurface *srcinfo,
+                               unsigned int index);
+
+/* Program YUV source. */
+void set_computeyuv(struct gcsurface *srcinfo, int x, int y);
+enum bverror set_yuvsrc(struct bvbltparams *bvbltparams,
+                       struct gcbatch *batch,
+                       struct gcsurface *srcinfo,
+                       struct bvbuffmap *srcmap);
+enum bverror set_yuvsrc_index(struct bvbltparams *bvbltparams,
+                             struct gcbatch *batch,
+                             struct gcsurface *srcinfo,
+                             struct bvbuffmap *srcmap,
+                             unsigned int index);
+
+/* Rendering entry points. */
+enum bverror do_fill(struct bvbltparams *bltparams,
+                    struct gcbatch *gcbatch,
+                    struct gcsurface *srcinfo);
+enum bverror do_blit(struct bvbltparams *bltparams,
+                    struct gcbatch *gcbatch,
+                    struct gcsurface *srcinfo);
+enum bverror do_filter(struct bvbltparams *bvbltparams,
+                      struct gcbatch *gcbatch,
+                      struct gcsurface *srcinfo);
+
+#endif
diff --git a/drivers/misc/gcx/gcbv/gcbvdebug.c b/drivers/misc/gcx/gcbv/gcbvdebug.c
new file mode 100644 (file)
index 0000000..b6488e9
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * gcbvdebug.c
+ *
+ * Copyright (C) 2010-2011 Vivante Corporation.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include "gcbv.h"
+
+static struct dentry *debug_root;
+
+/*****************************************************************************/
+
+static struct {
+       unsigned int srccount;
+       unsigned int multisrc;
+       unsigned int align;
+       unsigned int flags_dst;
+       unsigned int flags_destrect;
+       unsigned int flags_cliprect;
+
+} finalize_batch_reason;
+
+static int bfr_show(struct seq_file *s, void *data)
+{
+       seq_printf(s, "      srccount: %d\n",
+                  finalize_batch_reason.srccount);
+       seq_printf(s, "      multisrc: %d\n",
+                  finalize_batch_reason.multisrc);
+       seq_printf(s, "         align: %d\n",
+                  finalize_batch_reason.align);
+       seq_printf(s, "     flags_dst: %d\n",
+                  finalize_batch_reason.flags_dst);
+       seq_printf(s, "flags_destrect: %d\n",
+                  finalize_batch_reason.flags_destrect);
+       seq_printf(s, "flags_cliprect: %d\n",
+                  finalize_batch_reason.flags_cliprect);
+
+       memset(&finalize_batch_reason, 0, sizeof(finalize_batch_reason));
+
+       return 0;
+}
+
+static int bfr_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, bfr_show, 0);
+}
+
+static const struct file_operations fops_bfr = {
+       .open    = bfr_open,
+       .write   = NULL,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = single_release,
+};
+
+void gcbv_debug_finalize_batch(unsigned int reason)
+{
+       if (reason & GCBV_BATCH_FINALIZE_SRCCOUNT)
+               finalize_batch_reason.srccount++;
+
+       if (reason & GCBV_BATCH_FINALIZE_MULTISRC)
+               finalize_batch_reason.multisrc++;
+
+       if (reason & GCBV_BATCH_FINALIZE_ALIGN)
+               finalize_batch_reason.align++;
+
+       if (reason & GCBV_BATCH_FINALIZE_FLAGS_DST)
+               finalize_batch_reason.flags_dst++;
+
+       if (reason & GCBV_BATCH_FINALIZE_FLAGS_CLIPRECT)
+               finalize_batch_reason.flags_cliprect++;
+
+       if (reason & GCBV_BATCH_FINALIZE_FLAGS_DESTRECT)
+               finalize_batch_reason.flags_destrect++;
+}
+
+/*****************************************************************************/
+
+#define MAX_BLT_SOURCES   8
+
+static struct gc_blt_status {
+       int totalCount;
+       long long int totalPixels;
+       int srcCount[MAX_BLT_SOURCES + 1];
+       long long int srcCountPixels[MAX_BLT_SOURCES + 1];
+
+       int two_dim_one_pass;
+       int two_dim_two_pass;
+       int one_dim;
+
+} blt_stats;
+
+void gcbv_debug_blt(int srccount, int dstWidth, int dstHeight)
+{
+       int pixels;
+
+       if (srccount > MAX_BLT_SOURCES)
+               return;
+
+       pixels = dstWidth * dstHeight;
+
+       blt_stats.srcCount[srccount]++;
+       blt_stats.srcCountPixels[srccount] += pixels;
+
+       blt_stats.totalPixels += pixels;
+       blt_stats.totalCount++;
+}
+
+void gcbv_debug_scaleblt(bool scalex, bool scaley, bool singlepass)
+{
+       if (scalex && scaley) {
+               if (singlepass)
+                       blt_stats.two_dim_one_pass++;
+               else
+                       blt_stats.two_dim_two_pass++;
+       } else
+               blt_stats.one_dim++;
+}
+
+static void blt_stats_reset(void)
+{
+       int i;
+
+       for (i = 1; i <= MAX_BLT_SOURCES; i++) {
+               blt_stats.srcCount[i] = 0;
+               blt_stats.srcCountPixels[i] = 0;
+       }
+
+       blt_stats.totalCount = 0;
+       blt_stats.totalPixels = 0;
+
+       blt_stats.two_dim_one_pass = 0;
+       blt_stats.two_dim_two_pass = 0;
+       blt_stats.one_dim = 0;
+}
+
+static int blt_stats_show(struct seq_file *s, void *data)
+{
+       int i;
+
+       seq_printf(s, "total blts: %d\n", blt_stats.totalCount);
+
+       if (blt_stats.totalCount) {
+               for (i = 1; i <= MAX_BLT_SOURCES; i++) {
+                       int count = blt_stats.srcCount[i];
+
+                       seq_printf(s, " %d src: %d (%d%%)\n",
+                                  i,
+                                  count,
+                                  count * 100 / blt_stats.totalCount);
+               }
+       }
+
+       seq_printf(s, "total dst pixels: %lld\n", blt_stats.totalPixels);
+
+       if (blt_stats.totalPixels) {
+               for (i = 1; i <= MAX_BLT_SOURCES; i++) {
+                       long long int count = blt_stats.srcCountPixels[i];
+                       long long int total = blt_stats.totalPixels;
+
+                       seq_printf(s, " %d src: %lld (%lld%%)\n",
+                                  i,
+                                  count,
+                                  div64_s64(count * 100, total));
+               }
+       }
+
+       seq_printf(s, "scaled 2x2: %d\n", blt_stats.two_dim_two_pass);
+       seq_printf(s, "scaled 2x1: %d\n", blt_stats.two_dim_one_pass);
+       seq_printf(s, "scaled 1x1: %d\n", blt_stats.one_dim);
+
+       blt_stats_reset();
+
+       return 0;
+}
+
+static int blt_stats_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, blt_stats_show, 0);
+}
+
+static const struct file_operations fops_blt_stats = {
+       .open = blt_stats_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+/*****************************************************************************/
+
+void gcbv_debug_init(void)
+{
+       debug_root = debugfs_create_dir("gcbv", NULL);
+       if (!debug_root)
+               return;
+
+       debugfs_create_file("blt_stats", 0664, debug_root, NULL,
+                           &fops_blt_stats);
+       debugfs_create_file("batch_finalize_reason", 0664, debug_root, NULL,
+                           &fops_bfr);
+}
+
+void gcbv_debug_shutdown(void)
+{
+       if (debug_root)
+               debugfs_remove_recursive(debug_root);
+}
diff --git a/drivers/misc/gcx/gcbv/gcbvdebug.h b/drivers/misc/gcx/gcbv/gcbvdebug.h
new file mode 100644 (file)
index 0000000..fec8d6d
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * gcbvdebug.h
+ *
+ * Copyright (C) 2010-2011 Vivante Corporation.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef GCBVDEBUG_H
+#define GCBVDEBUG_H
+
+void gcbv_debug_init(void);
+void gcbv_debug_shutdown(void);
+
+void gcbv_debug_finalize_batch(unsigned int reason);
+
+void gcbv_debug_blt(int srccount, int dstWidth, int dstHeight);
+void gcbv_debug_scaleblt(bool scalex, bool scaley, bool singlepass);
+
+#endif
diff --git a/drivers/misc/gcx/gcbv/gcfill.c b/drivers/misc/gcx/gcbv/gcfill.c
new file mode 100644 (file)
index 0000000..df69434
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Vivante Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "gcbv.h"
+
+#define GCZONE_NONE            0
+#define GCZONE_ALL             (~0U)
+#define GCZONE_COLOR           (1 << 0)
+#define GCZONE_FILL            (1 << 1)
+
+GCDBG_FILTERDEF(fill, GCZONE_NONE,
+               "color",
+               "fill")
+
+
+static inline unsigned int extract_component(unsigned int pixel,
+                                            const struct bvcomponent *desc,
+                                            bool zerofill)
+{
+       unsigned int component;
+       unsigned int component8;
+
+       component = (pixel & desc->mask) >> desc->shift;
+       GCDBG(GCZONE_COLOR, "mask=0x%08X, shift=%d, component=0x%08X\n",
+               desc->mask, desc->shift, component);
+
+       switch (desc->size) {
+       case 0:
+               component8 = zerofill ? 0x00 : 0xFF;
+               GCDBG(GCZONE_COLOR, "component8=0x%08X\n", component8);
+               break;
+
+       case 1:
+               component8 = component ? 0xFF : 0x00;
+               GCDBG(GCZONE_COLOR, "component8=0x%08X\n", component8);
+               break;
+
+       case 4:
+               component8 = component | (component << 4);
+               GCDBG(GCZONE_COLOR, "component8=0x%08X\n", component8);
+               break;
+
+       case 5:
+               component8 = (component << 3) | (component >> 2);
+               GCDBG(GCZONE_COLOR, "component8=0x%08X\n", component8);
+               break;
+
+       case 6:
+               component8 = (component << 2) | (component >> 4);
+               GCDBG(GCZONE_COLOR, "component8=0x%08X\n", component8);
+               break;
+
+       default:
+               component8 = component;
+               GCDBG(GCZONE_COLOR, "component8=0x%08X\n", component8);
+       }
+
+       return component8;
+}
+
+static unsigned int getinternalcolor(void *ptr, struct bvformatxlate *format)
+{
+       unsigned int srcpixel, dstpixel;
+       unsigned int r, g, b, a;
+
+       switch (format->bitspp) {
+       case 16:
+               srcpixel = *(unsigned short *) ptr;
+               GCDBG(GCZONE_COLOR, "srcpixel=0x%08X\n", srcpixel);
+               break;
+
+       case 32:
+               srcpixel = *(unsigned int *) ptr;
+               GCDBG(GCZONE_COLOR, "srcpixel=0x%08X\n", srcpixel);
+               break;
+
+       default:
+               srcpixel = 0;
+               GCDBG(GCZONE_COLOR, "srcpixel=0x%08X\n", srcpixel);
+       }
+
+       r = extract_component(srcpixel, &format->cs.rgb.comp->r,
+                             format->zerofill);
+       g = extract_component(srcpixel, &format->cs.rgb.comp->g,
+                             format->zerofill);
+       b = extract_component(srcpixel, &format->cs.rgb.comp->b,
+                             format->zerofill);
+       a = extract_component(srcpixel, &format->cs.rgb.comp->a,
+                             format->zerofill);
+
+       GCDBG(GCZONE_COLOR, "(r,g,b,a)=0x%02X,0x%02X,0x%02X,0x%02X\n",
+             r, g, b, a);
+
+       dstpixel = (a << 24) | (r << 16) | (g <<  8) | b;
+
+       GCDBG(GCZONE_COLOR, "dstpixel=0x%08X\n", dstpixel);
+
+       return dstpixel;
+}
+
+enum bverror do_fill(struct bvbltparams *bvbltparams,
+                    struct gcbatch *batch,
+                    struct gcsurface *srcinfo)
+{
+       enum bverror bverror;
+       struct gccontext *gccontext = get_context();
+       struct gcsurface *dstinfo;
+       struct gcmofill *gcmofill;
+       unsigned char *fillcolorptr;
+       struct bvbuffmap *dstmap = NULL;
+       int physleft, phystop;
+       struct gcrect *srcorig;
+       struct gcrect *dstadj;
+
+       GCENTER(GCZONE_FILL);
+
+       /* Get a shortcut to the destination surface descriptor. */
+       dstinfo = &batch->dstinfo;
+
+       /* Only RGB source is supported. */
+       if (srcinfo->format.type != BVFMT_RGB) {
+               BVSETBLTERROR((srcinfo->index == 0)
+                                       ? BVERR_SRC1GEOM_FORMAT
+                                       : BVERR_SRC2GEOM_FORMAT,
+                             "only RGB source is supported for fill.");
+               goto exit;
+       }
+
+       /* Finish previous batch if any. */
+       bverror = batch->batchend(bvbltparams, batch);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /* Parse destination parameters. */
+       bverror = parse_destination(bvbltparams, batch);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /* Ignore the blit if destination rectangle is empty. */
+       if (null_rect(&dstinfo->rect.clip)) {
+               GCDBG(GCZONE_FILL, "empty destination rectangle.\n");
+               goto exit;
+       }
+
+       /* Map the destination. */
+       bverror = do_map(bvbltparams->dstdesc, batch, &dstmap);
+       if (bverror != BVERR_NONE) {
+               bvbltparams->errdesc = gccontext->bverrorstr;
+               goto exit;
+       }
+
+       /* Set the new destination. */
+       bverror = set_dst(bvbltparams, batch, dstmap);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /***********************************************************************
+       ** Allocate command buffer.
+       */
+
+       bverror = claim_buffer(bvbltparams, batch,
+                              sizeof(struct gcmofill),
+                              (void **) &gcmofill);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /***********************************************************************
+       ** Set dummy source.
+       */
+
+       /* Set surface dummy width and height. */
+       gcmofill->src.rotation_ldst = gcmofillsrc_rotation_ldst;
+       gcmofill->src.rotation.raw = 0;
+       gcmofill->src.rotation.reg.surf_width = 1;
+       gcmofill->src.config.raw = 0;
+
+       gcmofill->src.rotationheight_ldst = gcmofillsrc_rotationheight_ldst;
+       gcmofill->src.rotationheight.reg.height = 1;
+       gcmofill->src.rotationangle.raw = 0;
+       gcmofill->src.rotationangle.reg.dst = rotencoding[dstinfo->angle];
+       gcmofill->src.rotationangle.reg.dst_mirror = GCREG_MIRROR_NONE;
+
+       /* Disable alpha blending. */
+       gcmofill->src.alphacontrol_ldst = gcmofillsrc_alphacontrol_ldst;
+       gcmofill->src.alphacontrol.raw = 0;
+       gcmofill->src.alphacontrol.reg.enable = GCREG_ALPHA_CONTROL_ENABLE_OFF;
+
+       /***********************************************************************
+       ** Set fill color.
+       */
+
+       /* Get source rectangle shortcut. */
+       srcorig = &srcinfo->rect.orig;
+
+       switch (srcinfo->angle) {
+       case ROT_ANGLE_0:
+               physleft = srcorig->left;
+               phystop  = srcorig->top;
+               break;
+
+       case ROT_ANGLE_90:
+               physleft = srcorig->top;
+               phystop  = srcinfo->width - srcorig->left - 1;
+               break;
+
+       case ROT_ANGLE_180:
+               physleft = srcinfo->width  - srcorig->left - 1;
+               phystop  = srcinfo->height - srcorig->top  - 1;
+               break;
+
+       case ROT_ANGLE_270:
+               physleft = srcinfo->height - srcorig->top  - 1;
+               phystop  = srcorig->left;
+               break;
+
+       default:
+               physleft = 0;
+               phystop  = 0;
+               GCERR("invalid source angle %d.\n", srcinfo->angle);
+       }
+
+       fillcolorptr
+               = (unsigned char *) srcinfo->buf.desc->virtaddr
+               + phystop * srcinfo->stride1
+               + physleft * srcinfo->format.bitspp / 8;
+
+       gcmofill->clearcolor_ldst = gcmofill_clearcolor_ldst;
+       gcmofill->clearcolor.raw = getinternalcolor(fillcolorptr,
+                                                   &srcinfo->format);
+
+       /***********************************************************************
+       ** Configure and start fill.
+       */
+
+       /* Set destination configuration. */
+       gcmofill->dstconfig_ldst = gcmofill_dstconfig_ldst;
+       gcmofill->dstconfig.raw = 0;
+       gcmofill->dstconfig.reg.swizzle = dstinfo->format.swizzle;
+       gcmofill->dstconfig.reg.format = dstinfo->format.format;
+       gcmofill->dstconfig.reg.endian = dstinfo->format.endian;
+       gcmofill->dstconfig.reg.command = GCREG_DEST_CONFIG_COMMAND_CLEAR;
+
+       /* Set ROP3. */
+       gcmofill->rop_ldst = gcmofill_rop_ldst;
+       gcmofill->rop.raw = 0;
+       gcmofill->rop.reg.type = GCREG_ROP_TYPE_ROP3;
+       gcmofill->rop.reg.fg = srcinfo->rop;
+
+       /* Set START_DE command. */
+       gcmofill->startde.cmd.fld = gcfldstartde;
+
+       /* Set destination rectangle. */
+       dstadj = &dstinfo->rect.adj;
+       gcmofill->rect.left = dstadj->left;
+       gcmofill->rect.top = dstadj->top;
+       gcmofill->rect.right = dstadj->right;
+       gcmofill->rect.bottom = dstadj->bottom;
+
+exit:
+       GCEXITARG(GCZONE_FILL, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
diff --git a/drivers/misc/gcx/gcbv/gcfilter.c b/drivers/misc/gcx/gcbv/gcfilter.c
new file mode 100644 (file)
index 0000000..43d3615
--- /dev/null
@@ -0,0 +1,1490 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Vivante Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "gcbv.h"
+
+#define GCZONE_NONE            0
+#define GCZONE_ALL             (~0U)
+#define GCZONE_KERNEL          (1 << 0)
+#define GCZONE_FILTER          (1 << 1)
+#define GCZONE_BLEND           (1 << 2)
+#define GCZONE_TYPE            (1 << 3)
+#define GCZONE_SRC             (1 << 4)
+#define GCZONE_DEST            (1 << 5)
+#define GCZONE_SURF            (1 << 6)
+
+GCDBG_FILTERDEF(filter, GCZONE_NONE,
+               "kernel",
+               "filter",
+               "blend",
+               "type",
+               "src",
+               "dest",
+               "surf")
+
+
+/*******************************************************************************
+ * Miscellaneous defines.
+ */
+
+#define GC_BYTES_PER_CACHELINE (64)
+#define GC_BITS_PER_CACHELINE  (GC_BYTES_PER_CACHELINE * 8)
+#define GC_CACHELINE_ALIGN_16  (GC_BITS_PER_CACHELINE / 16 - 1)
+#define GC_CACHELINE_ALIGN_32  (GC_BITS_PER_CACHELINE / 32 - 1)
+
+enum gcscaletype {
+       GC_SCALE_OPF,
+       GC_SCALE_HOR,
+       GC_SCALE_VER,
+       GC_SCALE_HOR_FLIPPED,
+       GC_SCALE_VER_FLIPPED
+};
+
+/*******************************************************************************
+ * Scale factor format: unsigned 1.31 fixed point.
+ */
+
+#define GC_SCALE_TYPE          unsigned int
+#define GC_SCALE_FRACTION      31
+#define GC_SCALE_ONE           ((GC_SCALE_TYPE) (1 << GC_SCALE_FRACTION))
+
+
+/*******************************************************************************
+ * X coordinate format: signed 4.28 fixed point.
+ */
+
+#define GC_COORD_TYPE          int
+#define GC_COORD_FRACTION      28
+#define GC_COORD_PI            ((GC_COORD_TYPE) 0x3243F6C0)
+#define GC_COORD_2OVERPI       ((GC_COORD_TYPE) 0x0A2F9832)
+#define GC_COORD_PIOVER2       ((GC_COORD_TYPE) 0x1921FB60)
+#define GC_COORD_ZERO          ((GC_COORD_TYPE) 0)
+#define GC_COORD_HALF          ((GC_COORD_TYPE) (1 << (GC_COORD_FRACTION - 1)))
+#define GC_COORD_ONE           ((GC_COORD_TYPE) (1 << GC_COORD_FRACTION))
+#define GC_COORD_NEGONE                ((GC_COORD_TYPE) (~GC_COORD_ONE + 1))
+#define GC_COORD_SUBPIX_STEP   ((GC_COORD_TYPE) \
+                               (1 << (GC_COORD_FRACTION - GC_PHASE_BITS)))
+
+
+/*******************************************************************************
+ * Hardware coefficient format: signed 2.14 fixed point.
+ */
+
+#define GC_COEF_TYPE           short
+#define GC_COEF_FRACTION       14
+#define GC_COEF_ZERO           ((GC_COEF_TYPE) 0)
+#define GC_COEF_ONE            ((GC_COEF_TYPE) (1 << GC_COEF_FRACTION))
+#define GC_COEF_NEGONE         ((GC_COEF_TYPE) (~GC_COEF_ONE + 1))
+
+
+/*******************************************************************************
+ * Weight sum format: x.28 fixed point.
+ */
+
+#define GC_SUM_TYPE            long long
+#define GC_SUM_FRACTION                GC_COORD_FRACTION
+
+
+/*******************************************************************************
+ * Math shortcuts.
+ */
+
+#define computescale(dstsize, srcsize) ((GC_SCALE_TYPE) \
+       div_u64(((u64) (dstsize)) << GC_SCALE_FRACTION, (srcsize)) \
+)
+
+#define normweight(weight, sum) ((GC_COORD_TYPE) \
+       div64_s64(((s64) (weight)) << GC_COORD_FRACTION, (sum)) \
+)
+
+#define convertweight(weight) ((GC_COEF_TYPE) \
+       ((weight) >> (GC_COORD_FRACTION - GC_COEF_FRACTION)) \
+)
+
+
+/*******************************************************************************
+ * Fixed point SINE function. Takes a positive value in range [0..pi/2].
+ */
+
+static GC_COORD_TYPE sine(GC_COORD_TYPE x)
+{
+       static const GC_COORD_TYPE sinetable[] = {
+               0x00000000, 0x001FFFEB, 0x003FFF55, 0x005FFDC0,
+               0x007FFAAB, 0x009FF596, 0x00BFEE01, 0x00DFE36C,
+               0x00FFD557, 0x011FC344, 0x013FACB2, 0x015F9120,
+               0x017F7010, 0x019F4902, 0x01BF1B78, 0x01DEE6F2,
+               0x01FEAAEE, 0x021E66F0, 0x023E1A7C, 0x025DC50C,
+               0x027D6624, 0x029CFD48, 0x02BC89F8, 0x02DC0BB8,
+               0x02FB8204, 0x031AEC64, 0x033A4A5C, 0x03599B64,
+               0x0378DF08, 0x039814CC, 0x03B73C2C, 0x03D654B0,
+               0x03F55DDC, 0x04145730, 0x04334030, 0x04521868,
+               0x0470DF58, 0x048F9488, 0x04AE3770, 0x04CCC7A8,
+               0x04EB44A8, 0x0509ADF8, 0x05280328, 0x054643B0,
+               0x05646F28, 0x05828508, 0x05A084E0, 0x05BE6E38,
+               0x05DC4098, 0x05F9FB80, 0x06179E88, 0x06352928,
+               0x06529AF8, 0x066FF380, 0x068D3248, 0x06AA56D8,
+               0x06C760C0, 0x06E44F90, 0x070122C8, 0x071DD9F8,
+               0x073A74B8, 0x0756F290, 0x07735308, 0x078F95B0,
+               0x07ABBA20, 0x07C7BFD8, 0x07E3A678, 0x07FF6D88,
+               0x081B14A0, 0x08369B40, 0x08520110, 0x086D4590,
+               0x08886860, 0x08A36910, 0x08BE4730, 0x08D90250,
+               0x08F39A20, 0x090E0E10, 0x09285DD0, 0x094288E0,
+               0x095C8EF0, 0x09766F90, 0x09902A60, 0x09A9BEE0,
+               0x09C32CC0, 0x09DC7390, 0x09F592F0, 0x0A0E8A70,
+               0x0A2759C0, 0x0A400070, 0x0A587E20, 0x0A70D270,
+               0x0A88FD00, 0x0AA0FD60, 0x0AB8D350, 0x0AD07E50,
+               0x0AE7FE10, 0x0AFF5230, 0x0B167A50, 0x0B2D7610,
+               0x0B444520, 0x0B5AE730, 0x0B715BC0, 0x0B87A290,
+               0x0B9DBB40, 0x0BB3A580, 0x0BC960F0, 0x0BDEED30,
+               0x0BF44A00, 0x0C0976F0, 0x0C1E73D0, 0x0C334020,
+               0x0C47DBB0, 0x0C5C4620, 0x0C707F20, 0x0C848660,
+               0x0C985B80, 0x0CABFE50, 0x0CBF6E60, 0x0CD2AB80,
+               0x0CE5B550, 0x0CF88B80, 0x0D0B2DE0, 0x0D1D9C10,
+               0x0D2FD5C0, 0x0D41DAB0, 0x0D53AAA0, 0x0D654540,
+               0x0D76AA40, 0x0D87D970, 0x0D98D280, 0x0DA99530,
+               0x0DBA2140, 0x0DCA7650, 0x0DDA9450, 0x0DEA7AD0,
+               0x0DFA29B0, 0x0E09A0B0, 0x0E18DF80, 0x0E27E5F0,
+               0x0E36B3C0, 0x0E4548B0, 0x0E53A490, 0x0E61C720,
+               0x0E6FB020, 0x0E7D5F70, 0x0E8AD4C0, 0x0E980FF0,
+               0x0EA510B0, 0x0EB1D6F0, 0x0EBE6260, 0x0ECAB2D0,
+               0x0ED6C810, 0x0EE2A200, 0x0EEE4070, 0x0EF9A310,
+               0x0F04C9E0, 0x0F0FB490, 0x0F1A6300, 0x0F24D510,
+               0x0F2F0A80, 0x0F390340, 0x0F42BF10, 0x0F4C3DE0,
+               0x0F557F70, 0x0F5E83C0, 0x0F674A80, 0x0F6FD3B0,
+               0x0F781F20, 0x0F802CB0, 0x0F87FC40, 0x0F8F8DA0,
+               0x0F96E0D0, 0x0F9DF5B0, 0x0FA4CC00, 0x0FAB63D0,
+               0x0FB1BCF0, 0x0FB7D740, 0x0FBDB2B0, 0x0FC34F30,
+               0x0FC8ACA0, 0x0FCDCAF0, 0x0FD2AA10, 0x0FD749E0,
+               0x0FDBAA50, 0x0FDFCB50, 0x0FE3ACD0, 0x0FE74EC0,
+               0x0FEAB110, 0x0FEDD3C0, 0x0FF0B6B0, 0x0FF359F0,
+               0x0FF5BD50, 0x0FF7E0E0, 0x0FF9C490, 0x0FFB6850,
+               0x0FFCCC30, 0x0FFDF010, 0x0FFED400, 0x0FFF77F0,
+               0x0FFFDBF0, 0x0FFFFFE0, 0x0FFFE3D0, 0x0FFF87D0,
+               0x0FFEEBC0, 0x0FFE0FC0, 0x0FFCF3D0, 0x0FFB97E0
+       };
+
+       enum {
+               indexwidth = 8,
+               intwidth = 1,
+               indexshift = intwidth
+                          + GC_COORD_FRACTION
+                          - indexwidth
+       };
+
+       unsigned int p1, p2;
+       GC_COORD_TYPE p1x, p2x;
+       GC_COORD_TYPE p1y, p2y;
+       GC_COORD_TYPE dx, dy;
+       GC_COORD_TYPE a, b;
+       GC_COORD_TYPE result;
+
+       /* Determine the indices of two closest points in the table. */
+       p1 = ((unsigned int) x) >> indexshift;
+       p2 =  p1 + 1;
+
+       if ((p1 >= countof(sinetable)) || (p2 >= countof(sinetable))) {
+               GCERR("invalid table index.\n");
+               return GC_COORD_ZERO;
+       }
+
+       /* Determine the coordinates of the two closest points.  */
+       p1x = p1 << indexshift;
+       p2x = p2 << indexshift;
+
+       p1y = sinetable[p1];
+       p2y = sinetable[p2];
+
+       /* Determine the deltas. */
+       dx = p2x - p1x;
+       dy = p2y - p1y;
+
+       /* Find the slope and the y-intercept. */
+       b = (GC_COORD_TYPE) div64_s64(((s64) dy) << GC_COORD_FRACTION, dx);
+       a = p1y - (GC_COORD_TYPE) (((s64) b * p1x) >> GC_COORD_FRACTION);
+
+       /* Compute the result. */
+       result = a + (GC_COORD_TYPE) (((s64) b * x) >> GC_COORD_FRACTION);
+       return result;
+}
+
+
+/*******************************************************************************
+ * SINC function used in filter kernel generation.
+ */
+
+static GC_COORD_TYPE sinc_filter(GC_COORD_TYPE x, int radius)
+{
+       GC_COORD_TYPE result;
+       s64 radius64;
+       s64 pit, pitd;
+       s64 normpit, normpitd;
+       int negpit, negpitd;
+       int quadpit, quadpitd;
+       GC_COORD_TYPE sinpit, sinpitd;
+       GC_COORD_TYPE f1, f2;
+
+       if (x == GC_COORD_ZERO)
+               return GC_COORD_ONE;
+
+       radius64 = abs(radius) << GC_COORD_FRACTION;
+       if (x > radius64)
+               return GC_COORD_ZERO;
+
+       pit  = (((s64) GC_COORD_PI) * x) >> GC_COORD_FRACTION;
+       pitd = div_s64(pit, radius);
+
+       /* Sine table only has values for the first positive quadrant,
+        * remove the sign here. */
+       if (pit < 0) {
+               normpit = -pit;
+               negpit = 1;
+       } else {
+               normpit = pit;
+               negpit = 0;
+       }
+
+       if (pitd < 0) {
+               normpitd = -pitd;
+               negpitd = 1;
+       } else {
+               normpitd = pitd;
+               negpitd = 0;
+       }
+
+       /* Determine which quadrant we are in. */
+       quadpit = (int) ((normpit * GC_COORD_2OVERPI)
+               >> (2 * GC_COORD_FRACTION));
+       quadpitd = (int) ((normpitd * GC_COORD_2OVERPI)
+               >> (2 * GC_COORD_FRACTION));
+
+       /* Move coordinates to the first quadrant. */
+       normpit -= (s64) GC_COORD_PIOVER2 * quadpit;
+       normpitd -= (s64) GC_COORD_PIOVER2 * quadpitd;
+
+       /* Normalize the quadrant numbers. */
+       quadpit %= 4;
+       quadpitd %= 4;
+
+       /* Flip the coordinates if necessary. */
+       if ((quadpit == 1) || (quadpit == 3))
+               normpit = GC_COORD_PIOVER2 - normpit;
+
+       if ((quadpitd == 1) || (quadpitd == 3))
+               normpitd = GC_COORD_PIOVER2 - normpitd;
+
+       sinpit = sine((GC_COORD_TYPE) normpit);
+       sinpitd = sine((GC_COORD_TYPE) normpitd);
+
+       /* Negate depending on the quadrant. */
+       if (negpit) {
+               if ((quadpit == 0) || (quadpit == 1))
+                       sinpit = -sinpit;
+       } else {
+               if ((quadpit == 2) || (quadpit == 3))
+                       sinpit = -sinpit;
+       }
+
+       if (negpitd) {
+               if ((quadpitd == 0) || (quadpitd == 1))
+                       sinpitd = -sinpitd;
+       } else {
+               if ((quadpitd == 2) || (quadpitd == 3))
+                       sinpitd = -sinpitd;
+       }
+
+       f1 = (GC_COORD_TYPE)
+            div64_s64(((s64) sinpit) << GC_COORD_FRACTION, pit);
+       f2 = (GC_COORD_TYPE)
+            div64_s64(((s64) sinpitd) << GC_COORD_FRACTION, pitd);
+
+       result = (GC_COORD_TYPE) ((((s64) f1) * f2)
+              >> GC_COORD_FRACTION);
+
+       return result;
+}
+
+
+/*******************************************************************************
+ * Filter kernel generator based on SINC function.
+ */
+
+static void calculate_sync_filter(struct gcfilterkernel *gcfilterkernel)
+{
+       GC_SCALE_TYPE scale;
+       GC_COORD_TYPE subpixset[GC_TAP_COUNT];
+       GC_COORD_TYPE subpixeloffset;
+       GC_COORD_TYPE x, weight;
+       GC_SUM_TYPE weightsum;
+       short convweightsum;
+       int kernelhalf, padding;
+       int subpixpos, kernelpos;
+       short *kernelarray;
+       short count, adjustfrom, adjustment;
+       int index;
+
+       /* Compute the scale factor. */
+       scale = (gcfilterkernel->dstsize >= gcfilterkernel->srcsize)
+             ? GC_SCALE_ONE
+             : computescale(gcfilterkernel->dstsize, gcfilterkernel->srcsize);
+
+       /* Calculate the kernel half. */
+       kernelhalf = (int) (gcfilterkernel->kernelsize >> 1);
+
+       /* Init the subpixel offset. */
+       subpixeloffset = GC_COORD_HALF;
+
+       /* Determine kernel padding size. */
+       padding = (GC_TAP_COUNT - gcfilterkernel->kernelsize) / 2;
+
+       /* Set initial kernel array pointer. */
+       kernelarray = gcfilterkernel->kernelarray;
+
+       /* Loop through each subpixel. */
+       for (subpixpos = 0; subpixpos < GC_PHASE_LOAD_COUNT; subpixpos += 1) {
+               /* Compute weights. */
+               weightsum = GC_COORD_ZERO;
+               for (kernelpos = 0; kernelpos < GC_TAP_COUNT; kernelpos += 1) {
+                       /* Determine the current index. */
+                       index = kernelpos - padding;
+
+                       /* Pad with zeros left side. */
+                       if (index < 0) {
+                               subpixset[kernelpos] = GC_COORD_ZERO;
+                               continue;
+                       }
+
+                       /* Pad with zeros right side. */
+                       if (index >= (int) gcfilterkernel->kernelsize) {
+                               subpixset[kernelpos] = GC_COORD_ZERO;
+                               continue;
+                       }
+
+                       /* "Filter off" case. */
+                       if (gcfilterkernel->kernelsize == 1) {
+                               subpixset[kernelpos] = GC_COORD_ONE;
+
+                               /* Update the sum of the weights. */
+                               weightsum += GC_COORD_ONE;
+                               continue;
+                       }
+
+                       /* Compute X coordinate. */
+                       x = ((index - kernelhalf) << GC_COORD_FRACTION)
+                         + subpixeloffset;
+
+                       /* Scale the coordinate. */
+                       x = (GC_COORD_TYPE)
+                           ((((s64) x) * scale) >> GC_SCALE_FRACTION);
+
+                       /* Compute the weight. */
+                       subpixset[kernelpos] = sinc_filter(x, kernelhalf);
+
+                       /* Update the sum of the weights. */
+                       weightsum += subpixset[kernelpos];
+               }
+
+               /* Convert the weights to the hardware format. */
+               convweightsum = 0;
+               for (kernelpos = 0; kernelpos < GC_TAP_COUNT; kernelpos += 1) {
+                       /* Normalize the current weight. */
+                       weight = normweight(subpixset[kernelpos], weightsum);
+
+                       /* Convert the weight to fixed point. */
+                       if (weight == GC_COORD_ZERO)
+                               kernelarray[kernelpos] = GC_COEF_ZERO;
+                       else if (weight >= GC_COORD_ONE)
+                               kernelarray[kernelpos] = GC_COEF_ONE;
+                       else if (weight <= GC_COORD_NEGONE)
+                               kernelarray[kernelpos] = GC_COEF_NEGONE;
+                       else
+                               kernelarray[kernelpos] = convertweight(weight);
+
+                       /* Compute the sum of all coefficients. */
+                       convweightsum += kernelarray[kernelpos];
+               }
+
+               /* Adjust the fixed point coefficients so that the sum is 1. */
+               count = GC_COEF_ONE - convweightsum;
+               if (count < 0) {
+                       count = -count;
+                       adjustment = -1;
+               } else {
+                       adjustment = 1;
+               }
+
+               if (count > GC_TAP_COUNT) {
+                       GCERR("adjust count is too high = %d\n", count);
+               } else {
+                       adjustfrom = (GC_TAP_COUNT - count) / 2;
+                       for (kernelpos = 0; kernelpos < count; kernelpos += 1)
+                               kernelarray[adjustfrom + kernelpos]
+                                       += adjustment;
+               }
+
+               /* Advance the array pointer. */
+               kernelarray += GC_TAP_COUNT;
+
+               /* Advance to the next subpixel. */
+               subpixeloffset -= GC_COORD_SUBPIX_STEP;
+       }
+}
+
+
+/*******************************************************************************
+ * Loads a filter into the GPU.
+ */
+
+static enum bverror load_filter(struct bvbltparams *bvbltparams,
+                               struct gcbatch *batch,
+                               enum gcfiltertype type,
+                               unsigned int kernelsize,
+                               unsigned int scalefactor,
+                               unsigned int srcsize,
+                               unsigned int dstsize,
+                               struct gccmdldstate arraystate)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gccontext *gccontext = get_context();
+       struct gcfiltercache *filtercache;
+       struct list_head *filterlist;
+       struct list_head *filterhead;
+       struct gcfilterkernel *gcfilterkernel;
+       struct gcmofilterkernel *gcmofilterkernel;
+
+       GCDBG(GCZONE_KERNEL, "kernelsize = %d\n", kernelsize);
+       GCDBG(GCZONE_KERNEL, "srcsize = %d\n", srcsize);
+       GCDBG(GCZONE_KERNEL, "dstsize = %d\n", dstsize);
+       GCDBG(GCZONE_KERNEL, "scalefactor = 0x%08X\n", scalefactor);
+
+       /* Is the filter already loaded? */
+       if ((gccontext->loadedfilter != NULL) &&
+           (gccontext->loadedfilter->type == type) &&
+           (gccontext->loadedfilter->kernelsize == kernelsize) &&
+           (gccontext->loadedfilter->scalefactor == scalefactor)) {
+               GCDBG(GCZONE_KERNEL, "filter already computed.\n");
+               gcfilterkernel = gccontext->loadedfilter;
+               goto load;
+       }
+
+       /* Get the proper filter cache. */
+       filtercache = &gccontext->filtercache[type][kernelsize];
+       filterlist = &filtercache->list;
+
+       /* Try to find existing filter. */
+       GCDBG(GCZONE_KERNEL, "scanning for existing filter.\n");
+       list_for_each(filterhead, filterlist) {
+               gcfilterkernel = list_entry(filterhead,
+                                           struct gcfilterkernel,
+                                           link);
+               if (gcfilterkernel->scalefactor == scalefactor) {
+                       GCDBG(GCZONE_KERNEL, "filter found @ 0x%08X.\n",
+                             (unsigned int) gcfilterkernel);
+                       break;
+               }
+       }
+
+       /* Found the filter? */
+       if (filterhead != filterlist) {
+               /* Move the filter to the head of the list. */
+               if (filterlist->next != filterhead) {
+                       GCDBG(GCZONE_KERNEL, "moving to the head.\n");
+                       list_move(filterhead, filterlist);
+               }
+       } else {
+               GCDBG(GCZONE_KERNEL, "filter not found.\n");
+               if (filtercache->count == GC_FILTER_CACHE_MAX) {
+                       GCDBG(GCZONE_KERNEL,
+                             "reached the maximum number of filters.\n");
+                       filterhead = filterlist->prev;
+                       list_move(filterhead, filterlist);
+
+                       gcfilterkernel = list_entry(filterhead,
+                                                   struct gcfilterkernel,
+                                                   link);
+               } else {
+                       GCDBG(GCZONE_KERNEL, "allocating new filter.\n");
+                       gcfilterkernel = gcalloc(struct gcfilterkernel,
+                                                sizeof(struct gcfilterkernel));
+                       if (gcfilterkernel == NULL) {
+                               BVSETBLTERROR(BVERR_OOM,
+                                             "filter allocation failed");
+                               goto exit;
+                       }
+
+                       list_add(&gcfilterkernel->link, filterlist);
+               }
+
+               /* Update the number of filters. */
+               filtercache->count += 1;
+
+               /* Initialize the filter. */
+               gcfilterkernel->type = type;
+               gcfilterkernel->kernelsize = kernelsize;
+               gcfilterkernel->srcsize = srcsize;
+               gcfilterkernel->dstsize = dstsize;
+               gcfilterkernel->scalefactor = scalefactor;
+
+               /* Compute the coefficients. */
+               calculate_sync_filter(gcfilterkernel);
+       }
+
+load:
+       GCDBG(GCZONE_KERNEL, "loading filter.\n");
+
+       /* Load the filter. */
+       bverror = claim_buffer(bvbltparams, batch,
+                              sizeof(struct gcmofilterkernel),
+                              (void **) &gcmofilterkernel);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       gcmofilterkernel->kernelarray_ldst = arraystate;
+       memcpy(&gcmofilterkernel->kernelarray,
+              gcfilterkernel->kernelarray,
+              sizeof(gcfilterkernel->kernelarray));
+
+       /* Set the filter. */
+       gccontext->loadedfilter = gcfilterkernel;
+
+exit:
+       return bverror;
+}
+
+
+/*******************************************************************************
+ * Compute the scale factor.
+ */
+
+static inline unsigned int get_scale_factor(unsigned int srcsize,
+                                           unsigned int dstsize)
+{
+       if ((srcsize <= 1) || (dstsize <= 1))
+               return 0;
+
+       return ((srcsize - 1) << 16) / (dstsize - 1);
+}
+
+
+/*******************************************************************************
+ * Rasterizer setup.
+ */
+
+static enum bverror startvr(struct bvbltparams *bvbltparams,
+                           struct gcbatch *batch,
+                           struct bvbuffmap *srcmap,
+                           struct bvbuffmap *dstmap,
+                           struct gcsurface *srcinfo,
+                           struct gcsurface *dstinfo,
+                           unsigned int srcx,
+                           unsigned int srcy,
+                           struct gcrect *dstrect,
+                           int srcangle,
+                           int dstangle,
+                           enum gcscaletype scaletype)
+{
+       enum bverror bverror;
+       struct gccontext *gccontext = get_context();
+       struct gcfilter *gcfilter;
+
+       struct gcmovrdst *gcmovrdst;
+       struct gcmovrsrc *gcmovrsrc;
+       struct gcmostartvr *gcmostartvr;
+
+       struct gcrect srcorig;
+
+       GCENTERARG(GCZONE_FILTER, "scaletype = %d\n", scaletype);
+
+       /* Get a shortcut to the filter properties. */
+       gcfilter = &batch->op.filter;
+
+       /***********************************************************************
+        * Program the destination.
+        */
+
+       GCDBG(GCZONE_FILTER, "destination:\n");
+       GCDBG(GCZONE_FILTER, "  angle = %d\n", dstangle);
+       GCDBG(GCZONE_FILTER, "  pixalign = %d,%d\n",
+             dstinfo->xpixalign, dstinfo->ypixalign);
+       GCDBG(GCZONE_FILTER, "  bytealign = %d\n", dstinfo->bytealign1);
+       GCDBG(GCZONE_FILTER, "  virtstride = %d\n", dstinfo->stride1);
+       GCDBG(GCZONE_FILTER, "  format = %d\n", dstinfo->format.format);
+       GCDBG(GCZONE_FILTER, "  swizzle = %d\n", dstinfo->format.swizzle);
+       GCDBG(GCZONE_FILTER, "  endian = %d\n", dstinfo->format.endian);
+       GCDBG(GCZONE_FILTER, "  premul = %d\n", dstinfo->format.premultiplied);
+       GCDBG(GCZONE_FILTER, "  physwidth = %d\n", dstinfo->physwidth);
+       GCDBG(GCZONE_FILTER, "  physheight = %d\n", dstinfo->physheight);
+       GCPRINT_RECT(GCZONE_FILTER, "  rect", dstrect);
+
+       /* Allocate command buffer. */
+       bverror = claim_buffer(bvbltparams, batch,
+                              sizeof(struct gcmovrdst),
+                              (void **) &gcmovrdst);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /* Add the address fixup. */
+       add_fixup(bvbltparams, batch, &gcmovrdst->address, dstinfo->bytealign1);
+
+       /* Set surface parameters. */
+       gcmovrdst->config_ldst = gcmovrdst_config_ldst;
+       gcmovrdst->address = GET_MAP_HANDLE(dstmap);
+       gcmovrdst->stride = dstinfo->stride1;
+       gcmovrdst->config.raw = 0;
+       gcmovrdst->config.reg.swizzle = dstinfo->format.swizzle;
+       gcmovrdst->config.reg.format = dstinfo->format.format;
+       gcmovrdst->config.reg.endian = dstinfo->format.endian;
+
+       /* Set surface width and height. */
+       gcmovrdst->rotation.raw = 0;
+       gcmovrdst->rotation.reg.surf_width = dstinfo->physwidth;
+       gcmovrdst->rotationheight_ldst = gcmovrdst_rotationheight_ldst;
+       gcmovrdst->rotationheight.raw = 0;
+       gcmovrdst->rotationheight.reg.height = dstinfo->physheight;
+
+       /***********************************************************************
+        * Program the source.
+        */
+
+       /* Determine adjusted source bounding rectangle and origin. */
+       srcorig = srcinfo->rect.orig;
+       srcorig.left  -=  srcinfo->xpixalign;
+       srcorig.right -=  srcinfo->xpixalign;
+       srcx          -= (srcinfo->xpixalign << 16);
+
+       GCDBG(GCZONE_FILTER, "source:\n");
+       GCDBG(GCZONE_FILTER, "  angle = %d\n", srcangle);
+       GCDBG(GCZONE_FILTER, "  pixalign = %d,%d\n",
+             srcinfo->xpixalign, srcinfo->ypixalign);
+       GCDBG(GCZONE_FILTER, "  bytealign = %d\n", srcinfo->bytealign1);
+       GCDBG(GCZONE_FILTER, "  virtstride = %d\n", srcinfo->stride1);
+       GCDBG(GCZONE_FILTER, "  format = %d\n", srcinfo->format.format);
+       GCDBG(GCZONE_FILTER, "  swizzle = %d\n", srcinfo->format.swizzle);
+       GCDBG(GCZONE_FILTER, "  endian = %d\n", srcinfo->format.endian);
+       GCDBG(GCZONE_FILTER, "  premul = %d\n", srcinfo->format.premultiplied);
+       GCDBG(GCZONE_FILTER, "  physwidth = %d\n", srcinfo->physwidth);
+       GCDBG(GCZONE_FILTER, "  physheight = %d\n", srcinfo->physheight);
+       GCPRINT_RECT(GCZONE_FILTER, "  rect", &srcorig);
+
+       GCDBG(GCZONE_FILTER, "src origin: 0x%08X,0x%08X\n", srcx, srcy);
+
+       /* Allocate command buffer. */
+       bverror = claim_buffer(bvbltparams, batch,
+                              sizeof(struct gcmovrsrc),
+                              (void **) &gcmovrsrc);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       add_fixup(bvbltparams, batch, &gcmovrsrc->address, srcinfo->bytealign1);
+
+       gcmovrsrc->config_ldst = gcmovrsrc_config_ldst;
+
+       gcmovrsrc->address = GET_MAP_HANDLE(srcmap);
+       gcmovrsrc->stride = srcinfo->stride1;
+
+       gcmovrsrc->rotation.raw = 0;
+       gcmovrsrc->rotation.reg.surf_width = srcinfo->physwidth;
+
+       gcmovrsrc->config.raw = 0;
+       gcmovrsrc->config.reg.swizzle = srcinfo->format.swizzle;
+       gcmovrsrc->config.reg.format = srcinfo->format.format;
+       gcmovrsrc->config.reg.endian = srcinfo->format.endian;
+
+       if (gccontext->gccaps.l2cachefor420 &&
+           (srcinfo->format.type == BVFMT_YUV) &&
+           (srcinfo->format.cs.yuv.planecount > 1) &&
+           ((srcinfo->angle & 1) != 0))
+               gcmovrsrc->config.reg.disable420L2cache
+                       = GCREG_SRC_CONFIG_DISABLE420_L2_CACHE_DISABLED;
+
+       gcmovrsrc->pos_ldst = gcmovrsrc_pos_ldst;
+
+       /* Source image bounding box. */
+       gcmovrsrc->lt.reg.left = srcorig.left;
+       gcmovrsrc->lt.reg.top = srcorig.top;
+       gcmovrsrc->rb.reg.right = srcorig.right;
+       gcmovrsrc->rb.reg.bottom = srcorig.bottom;
+
+       /* Fractional origin. */
+       gcmovrsrc->x = srcx;
+       gcmovrsrc->y = srcy;
+
+       /* Program rotation. */
+       gcmovrsrc->rotation_ldst = gcmovrsrc_rotation_ldst;
+       gcmovrsrc->rotationheight.reg.height = srcinfo->physheight;
+       gcmovrsrc->rotationangle.raw = 0;
+       gcmovrsrc->rotationangle.reg.src = rotencoding[srcangle];
+       gcmovrsrc->rotationangle.reg.dst = rotencoding[dstangle];
+       gcmovrsrc->rotationangle.reg.src_mirror = srcinfo->mirror;
+       gcmovrsrc->rotationangle.reg.dst_mirror = dstinfo->mirror;
+
+       gcmovrsrc->rop_ldst = gcmovrsrc_rop_ldst;
+       gcmovrsrc->rop.raw = 0;
+       gcmovrsrc->rop.reg.type = GCREG_ROP_TYPE_ROP3;
+       gcmovrsrc->rop.reg.fg = 0xCC;
+
+       /* Program multiply modes. */
+       gcmovrsrc->mult_ldst = gcmovrsrc_mult_ldst;
+       gcmovrsrc->mult.raw = 0;
+       gcmovrsrc->mult.reg.srcglobalpremul = srcinfo->srcglobalpremul;
+
+       if (srcinfo->format.premultiplied)
+               gcmovrsrc->mult.reg.srcpremul
+               = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_DISABLE;
+       else
+               gcmovrsrc->mult.reg.srcpremul
+               = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_ENABLE;
+
+       if (dstinfo->format.premultiplied) {
+               gcmovrsrc->mult.reg.dstpremul
+               = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_DISABLE;
+
+               gcmovrsrc->mult.reg.dstdemul
+               = GCREG_COLOR_MULTIPLY_MODES_DST_DEMULTIPLY_DISABLE;
+       } else {
+               gcmovrsrc->mult.reg.dstpremul
+               = GCREG_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_ENABLE;
+
+               gcmovrsrc->mult.reg.dstdemul
+               = GCREG_COLOR_MULTIPLY_MODES_DST_DEMULTIPLY_ENABLE;
+       }
+
+       /* Program YUV source. */
+       if (srcinfo->format.type == BVFMT_YUV) {
+               bverror = set_yuvsrc(bvbltparams, batch, srcinfo, srcmap);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+       }
+
+       /***********************************************************************
+        * Program blending.
+        */
+
+       bverror = set_blending(bvbltparams, batch, srcinfo);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /***********************************************************************
+        * Start the operation.
+        */
+
+       bverror = claim_buffer(bvbltparams, batch,
+                              sizeof(struct gcmostartvr),
+                              (void **) &gcmostartvr);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       switch (scaletype) {
+       case GC_SCALE_OPF:
+               gcmostartvr->scalex = gcfilter->horscalefactor;
+               gcmostartvr->scaley = gcfilter->verscalefactor;
+               gcmostartvr->config = gcregvrconfig_onepass;
+               break;
+
+       case GC_SCALE_HOR:
+               gcmostartvr->scalex = gcfilter->horscalefactor;
+               gcmostartvr->scaley = 0;
+               gcmostartvr->config = gcregvrconfig_horizontal;
+               break;
+
+       case GC_SCALE_VER:
+               gcmostartvr->scalex = 0;
+               gcmostartvr->scaley = gcfilter->verscalefactor;
+               gcmostartvr->config = gcregvrconfig_vertical;
+               break;
+
+       case GC_SCALE_HOR_FLIPPED:
+               gcmostartvr->scalex = 0;
+               gcmostartvr->scaley = gcfilter->horscalefactor;
+               gcmostartvr->config = gcregvrconfig_vertical;
+               break;
+
+       case GC_SCALE_VER_FLIPPED:
+               gcmostartvr->scalex = gcfilter->verscalefactor;
+               gcmostartvr->scaley = 0;
+               gcmostartvr->config = gcregvrconfig_horizontal;
+               break;
+       }
+
+       gcmostartvr->scale_ldst = gcmostartvr_scale_ldst;
+       gcmostartvr->rect_ldst = gcmostartvr_rect_ldst;
+       gcmostartvr->config_ldst = gcmostartvr_config_ldst;
+
+       gcmostartvr->lt.left = dstrect->left;
+       gcmostartvr->lt.top = dstrect->top;
+       gcmostartvr->rb.right = dstrect->right;
+       gcmostartvr->rb.bottom = dstrect->bottom;
+
+exit:
+       GCEXITARG(GCZONE_FILTER, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+
+/*******************************************************************************
+ * Main fiter entry.
+ */
+
+enum bverror do_filter(struct bvbltparams *bvbltparams,
+                      struct gcbatch *batch,
+                      struct gcsurface *srcinfo)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gccontext *gccontext = get_context();
+
+       struct gcfilter *gcfilter;
+       struct gcsurface *dstinfo;
+
+       bool scalex, scaley;
+       bool singlepass, twopass;
+
+       struct gcrect *tmporig;
+       struct gcrect *srcorig, *srcclip;
+       struct gcrect *dstorig, *dstclip, *dstadj;
+
+       struct gcrect dstdelta;
+       struct gcrect srcdelta;
+
+       struct bvbuffmap *srcmap = NULL;
+       struct bvbuffmap *tmpmap = NULL;
+       struct bvbuffmap *dstmap = NULL;
+
+       struct gcmovrconfigex *gcmovrconfigex;
+
+       unsigned int srcx, srcy;
+       unsigned int srcwidth, srcheight;
+       unsigned int dstwidth, dstheight;
+       unsigned int horscalefactor, verscalefactor;
+       unsigned int kernelsize;
+       int angle;
+
+       GCENTER(GCZONE_FILTER);
+
+       /* Get some shortcuts. */
+       dstinfo = &batch->dstinfo;
+       gcfilter = &batch->op.filter;
+
+       /* Finish previous batch if any. */
+       bverror = batch->batchend(bvbltparams, batch);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /* ROP is not supported by the filters. */
+       if ((srcinfo->rop & 0xFF) != 0xCC) {
+               BVSETBLTERROR(BVERR_ROP,
+                             "only copy ROP is supported in scaling mode");
+               goto exit;
+       }
+
+       /* Zero-fill for source is not supported. */
+       if (srcinfo->format.zerofill) {
+               BVSETBLTERROR((srcinfo->index == 0)
+                                       ? BVERR_SRC1GEOM_FORMAT
+                                       : BVERR_SRC2GEOM_FORMAT,
+                             "0 filling is not supported.");
+               goto exit;
+       }
+
+       /* Parse the scale mode. */
+       bverror = parse_scalemode(bvbltparams, batch);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /* Parse destination parameters. */
+       bverror = parse_destination(bvbltparams, batch);
+       if (bverror != BVERR_NONE)
+               goto exit;
+
+       /* Ignore the blit if destination rectangle is empty. */
+       if (null_rect(&dstinfo->rect.clip)) {
+               GCDBG(GCZONE_DEST, "empty destination rectangle.\n");
+               goto exit;
+       }
+
+       /* Get rectangle shortcuts. */
+       srcorig = &srcinfo->rect.orig;
+       srcclip = &srcinfo->rect.clip;
+
+       if ((srcinfo->index == 1) && dstinfo->haveaux) {
+               GCDBG(GCZONE_FILTER, "picking aux set");
+               dstorig = &dstinfo->auxrect.orig;
+               dstclip = &dstinfo->auxrect.clip;
+               dstadj = &dstinfo->auxrect.adj;
+       } else {
+               GCDBG(GCZONE_FILTER, "picking main set");
+               dstorig = &dstinfo->rect.orig;
+               dstclip = &dstinfo->rect.clip;
+               dstadj = &dstinfo->rect.adj;
+       }
+
+       GCPRINT_RECT(GCZONE_FILTER, "original src", srcorig);
+       GCPRINT_RECT(GCZONE_FILTER, "original dst", dstorig);
+       GCPRINT_RECT(GCZONE_FILTER, "clipped dst", dstclip);
+       GCPRINT_RECT(GCZONE_FILTER, "adjusted dst", dstadj);
+
+       /* Compute the source alignments needed to compensate
+        * for the surface base address misalignment if any. */
+       srcinfo->xpixalign = get_pixel_offset(srcinfo, 0);
+       srcinfo->ypixalign = 0;
+       srcinfo->bytealign1 = (srcinfo->xpixalign
+                           * (int) srcinfo->format.bitspp) / 8;
+       GCDBG(GCZONE_SRC, "source surface offset (pixels) = %d,%d\n",
+               srcinfo->xpixalign, srcinfo->ypixalign);
+       GCDBG(GCZONE_SRC, "source surface offset (bytes) = %d\n",
+               srcinfo->bytealign1);
+
+       /* Determine physical size. */
+       if ((srcinfo->angle % 2) == 0) {
+               srcinfo->physwidth  = srcinfo->width
+                                   - srcinfo->xpixalign;
+               srcinfo->physheight = srcinfo->height
+                                   - srcinfo->ypixalign;
+       } else {
+               srcinfo->physwidth  = srcinfo->height
+                                   - srcinfo->xpixalign;
+               srcinfo->physheight = srcinfo->width
+                                   - srcinfo->ypixalign;
+       }
+       GCDBG(GCZONE_SRC, "source physical size = %dx%d\n",
+             srcinfo->physwidth, srcinfo->physheight);
+
+       /* OPF does not support source rotation, which can be compensated by
+        * using destination rotation. Compute the adjustment angle.
+        * For simplicity use the same algorythm for both OPF and TPF. */
+       srcinfo->adjangle = (4 - srcinfo->angle) % 4;
+       adjust_angle(srcinfo, dstinfo);
+
+       /* Set rotation angle. */
+       angle = dstinfo->angle;
+
+       /* Compute U/V plane offsets. */
+       if ((srcinfo->format.type == BVFMT_YUV) &&
+           (srcinfo->format.cs.yuv.planecount > 1))
+               set_computeyuv(srcinfo, 0, 0);
+
+       /* Determine the source and destination rectangles. */
+       srcwidth  = srcorig->right  - srcorig->left;
+       srcheight = srcorig->bottom - srcorig->top;
+       dstwidth  = dstorig->right  - dstorig->left;
+       dstheight = dstorig->bottom - dstorig->top;
+
+       GCDBG(GCZONE_FILTER, "adjusted input src size: %dx%d\n",
+             srcwidth, srcheight);
+       GCDBG(GCZONE_FILTER, "adjusted input dst size: %dx%d\n",
+             dstwidth, dstheight);
+
+       /* Determine the data path. */
+       scalex = (srcwidth  != dstwidth);
+       scaley = (srcheight != dstheight);
+
+       twopass = scalex && scaley;
+       if (twopass) {
+               if (((gcfilter->horkernelsize == 3) ||
+                    (gcfilter->horkernelsize == 5)) &&
+                   ((gcfilter->verkernelsize == 3) ||
+                    (gcfilter->verkernelsize == 5))) {
+                       singlepass = true;
+                       twopass = false;
+               } else {
+                       singlepass = false;
+               }
+       } else {
+               /* Two pass filter in one pass mode. */
+               if (!scalex && !scaley)
+                       GCERR("no scaling needed.\n");
+
+               GCDBG(GCZONE_FILTER, "only %s scaling needed.\n",
+                       scalex ? "horizontal" : "vertical");
+
+               singlepass = false;
+       }
+
+       gcbv_debug_scaleblt(scalex, scaley, singlepass);
+
+       /* Compute the scale factors. */
+       gcfilter->horscalefactor =
+       horscalefactor = get_scale_factor(srcwidth, dstwidth);
+       GCDBG(GCZONE_FILTER, "horscalefactor = 0x%08X\n", horscalefactor);
+
+       gcfilter->verscalefactor =
+       verscalefactor = get_scale_factor(srcheight, dstheight);
+       GCDBG(GCZONE_FILTER, "verscalefactor = 0x%08X\n", verscalefactor);
+
+       /* Compute the destination offsets. */
+       dstdelta.left   = dstclip->left   - dstorig->left;
+       dstdelta.top    = dstclip->top    - dstorig->top;
+       dstdelta.right  = dstclip->right  - dstorig->left;
+       dstdelta.bottom = dstclip->bottom - dstorig->top;
+       GCDBG(GCZONE_FILTER, "dst deltas = (%d,%d)-(%d,%d)\n",
+             dstdelta.left, dstdelta.top, dstdelta.right, dstdelta.bottom);
+
+       /* Compute the source offsets. */
+       srcdelta.left   =  dstdelta.left        * horscalefactor;
+       srcdelta.top    =  dstdelta.top         * verscalefactor;
+       srcdelta.right  = (dstdelta.right  - 1) * horscalefactor + (1 << 16);
+       srcdelta.bottom = (dstdelta.bottom - 1) * verscalefactor + (1 << 16);
+
+       /* Before rendering each destination pixel, the HW will select the
+        * corresponding source center pixel to apply the kernel around.
+        * To make this process precise we need to add 0.5 to source initial
+        * coordinates here; this will make HW pick the next source pixel if
+        * the fraction is equal or greater then 0.5. */
+       srcdelta.left   += 0x00008000;
+       srcdelta.top    += 0x00008000;
+       srcdelta.right  += 0x00008000;
+       srcdelta.bottom += 0x00008000;
+       GCDBG(GCZONE_FILTER, "src deltas = "
+             "(0x%08X,0x%08X)-(0x%08X,0x%08X)\n",
+             srcdelta.left, srcdelta.top, srcdelta.right, srcdelta.bottom);
+       GCDBG(GCZONE_FILTER, "src deltas (int) = (%d,%d)-(%d,%d)\n",
+             srcdelta.left >> 16, srcdelta.top >> 16,
+             srcdelta.right >> 16, srcdelta.bottom >> 16);
+
+       /* Determine clipped source rectangle. */
+       srcclip->left   = srcorig->left + (srcdelta.left   >> 16);
+       srcclip->top    = srcorig->top  + (srcdelta.top    >> 16);
+       srcclip->right  = srcorig->left + (srcdelta.right  >> 16);
+       srcclip->bottom = srcorig->top  + (srcdelta.bottom >> 16);
+
+       GCDBG(GCZONE_FILTER, "source:\n");
+       GCDBG(GCZONE_FILTER, "  stride = %d, geom = %dx%d\n",
+             srcinfo->stride1, srcinfo->width, srcinfo->height);
+       GCDBG(GCZONE_FILTER, "  rotation = %d\n", srcinfo->angle);
+       GCPRINT_RECT(GCZONE_FILTER, "  clipped rect", srcclip);
+
+       GCDBG(GCZONE_FILTER, "destination:\n");
+       GCDBG(GCZONE_FILTER, "  stride = %d, geom size = %dx%d\n",
+             dstinfo->stride1, dstinfo->width, dstinfo->height);
+       GCDBG(GCZONE_FILTER, "  rotation = %d\n", dstinfo->angle);
+       GCPRINT_RECT(GCZONE_FILTER, "  clipped rect", dstclip);
+
+       /* Validate the source rectangle. */
+       if (!valid_rect(srcinfo, srcclip)) {
+               BVSETBLTERROR((srcinfo->index == 0)
+                                       ? BVERR_SRC1RECT
+                                       : BVERR_SRC2RECT,
+                             "invalid source rectangle.");
+               goto exit;
+       }
+
+       /* Ignore the blit if source rectangle is empty. */
+       if (null_rect(srcclip)) {
+               GCDBG(GCZONE_SURF, "empty source rectangle.\n");
+               goto exit;
+       }
+
+       /* Map the source. */
+       bverror = do_map(srcinfo->buf.desc, batch, &srcmap);
+       if (bverror != BVERR_NONE) {
+               bvbltparams->errdesc = gccontext->bverrorstr;
+               goto exit;
+       }
+
+       /* Map the destination. */
+       bverror = do_map(dstinfo->buf.desc, batch, &dstmap);
+       if (bverror != BVERR_NONE) {
+               bvbltparams->errdesc = gccontext->bverrorstr;
+               goto exit;
+       }
+
+       /* Do single pass filter if we can. */
+       if (singlepass) {
+               GCDBG(GCZONE_TYPE, "single pass\n");
+
+               /* Determine the kernel size to use. */
+               kernelsize = max(gcfilter->horkernelsize,
+                                gcfilter->verkernelsize);
+
+               /* Set kernel size. */
+               bverror = claim_buffer(bvbltparams, batch,
+                                      sizeof(struct gcmovrconfigex),
+                                      (void **) &gcmovrconfigex);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               gcmovrconfigex->config_ldst = gcmovrconfigex_config_ldst;
+               gcmovrconfigex->config.raw = ~0U;
+               gcmovrconfigex->config.reg.kernelsize = kernelsize;
+               gcmovrconfigex->config.reg.mask_kernelsize
+                       = GCREG_VR_CONFIG_EX_MASK_FILTER_TAP_ENABLED;
+
+               /* Setup single pass. */
+               srcx = (srcorig->left << 16) + srcdelta.left;
+               srcy = (srcorig->top  << 16) + srcdelta.top;
+               GCDBG(GCZONE_SRC, "src origin: 0x%08X,0x%08X\n", srcx, srcy);
+
+               /* Load the horizontal filter. */
+               bverror = load_filter(bvbltparams, batch,
+                                     GC_FILTER_SYNC,
+                                     gcfilter->horkernelsize,
+                                     gcfilter->horscalefactor,
+                                     srcwidth, dstwidth,
+                                     gcmofilterkernel_horizontal_ldst);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Load the vertical filter. */
+               bverror = load_filter(bvbltparams, batch,
+                                     GC_FILTER_SYNC,
+                                     gcfilter->verkernelsize,
+                                     gcfilter->verscalefactor,
+                                     srcheight, dstheight,
+                                     gcmofilterkernel_vertical_ldst);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Start the operation. */
+               bverror = startvr(bvbltparams, batch,
+                                 srcmap, dstmap, srcinfo, dstinfo,
+                                 srcx, srcy, dstadj,
+                                 ROT_ANGLE_0, angle,
+                                 GC_SCALE_OPF);
+       } else if (twopass) {
+               unsigned int horkernelhalf;
+               unsigned int leftextra, rightextra;
+               unsigned int tmprectwidth, tmprectheight;
+               unsigned int tmpalignmask, dstalignmask;
+               unsigned int tmpsize;
+               struct gcsurface tmpinfo;
+
+               GCDBG(GCZONE_TYPE, "two pass\n");
+
+               /* Initialize the temporaty surface descriptor. */
+               tmpinfo.index = -1;
+               tmpinfo.angle = angle;
+               tmpinfo.mirror = GCREG_MIRROR_NONE;
+               tmpinfo.rop = 0;
+               GCDBG(GCZONE_FILTER, "tmp angle = %d\n", tmpinfo.angle);
+
+               /* Transfer blending parameters from the source to the
+                * temporary buffer so that the blending would happen
+                * on the second pass. */
+               tmpinfo.gca = srcinfo->gca;
+               srcinfo->gca = NULL;
+
+               /* Determine temporary surface format. */
+               if (srcinfo->format.type == BVFMT_YUV) {
+                       if (tmpinfo.angle == ROT_ANGLE_0) {
+                               GCDBG(GCZONE_FILTER,
+                                     "tmp format = 4:2:2\n");
+                               parse_format(bvbltparams, OCDFMT_YUYV,
+                                            &tmpinfo.format);
+                       } else {
+                               GCDBG(GCZONE_FILTER,
+                                     "tmp format = dst format\n");
+                               tmpinfo.format = dstinfo->format;
+                       }
+               } else {
+                       GCDBG(GCZONE_FILTER,
+                             "tmp format = src format\n");
+                       tmpinfo.format = srcinfo->format;
+               }
+
+               /* Determine pixel alignment masks. */
+               tmpalignmask = GC_BITS_PER_CACHELINE
+                            / tmpinfo.format.bitspp - 1;
+               dstalignmask = GC_BITS_PER_CACHELINE
+                            / dstinfo->format.bitspp - 1;
+
+               /* In partial filter blit cases, the vertical pass has to render
+                * more pixel information to the left and to the right of the
+                * temporary image so that the next pass has its necessary
+                * kernel information on the edges of the image. */
+               horkernelhalf = gcfilter->horkernelsize >> 1;
+
+               leftextra  = srcdelta.left >> 16;
+               rightextra = srcwidth - (srcdelta.right >> 16);
+
+               if (leftextra > horkernelhalf)
+                       leftextra = horkernelhalf;
+
+               if (rightextra > horkernelhalf)
+                       rightextra = horkernelhalf;
+
+               GCDBG(GCZONE_FILTER, "leftextra = %d, rightextra = %d\n",
+                     leftextra, rightextra);
+
+               /* Determine the source origin. */
+               srcx = ((srcorig->left - leftextra) << 16) + srcdelta.left;
+               srcy =  (srcorig->top << 16) + srcdelta.top;
+               GCDBG(GCZONE_SRC, "src origin: 0x%08X,0x%08X\n", srcx, srcy);
+               GCDBG(GCZONE_SRC, "src origin (int): %d,%d\n",
+                     srcx >> 16, srcy >> 16);
+
+               /* Determine the size of the temporary rectangle. */
+               tmprectwidth = leftextra + rightextra
+                            + ((srcdelta.right >> 16) - (srcdelta.left >> 16));
+               tmprectheight = dstadj->bottom - dstadj->top;
+               GCDBG(GCZONE_FILTER, "tmp rect size: %dx%d\n",
+                     tmprectwidth, tmprectheight);
+
+               /* Shortcut to temporary rectangle. */
+               tmporig = &tmpinfo.rect.orig;
+
+               /* Determine the temporary destination coordinates. */
+               switch (angle) {
+               case ROT_ANGLE_0:
+               case ROT_ANGLE_180:
+                       tmporig->left   = (srcx >> 16) & tmpalignmask;
+                       tmporig->top    = 0;
+                       tmporig->right  = tmporig->left + tmprectwidth;
+                       tmporig->bottom = tmprectheight;
+
+                       tmpinfo.width  = (tmporig->right + tmpalignmask)
+                                      & ~tmpalignmask;
+                       tmpinfo.height = tmprectheight;
+
+                       tmpinfo.physwidth  = tmpinfo.width;
+                       tmpinfo.physheight = tmpinfo.height;
+                       break;
+
+               case ROT_ANGLE_90:
+                       tmporig->left   = 0;
+                       tmporig->top    = dstadj->left & dstalignmask;
+                       tmporig->right  = tmprectwidth;
+                       tmporig->bottom = tmporig->top  + tmprectheight;
+
+                       tmpinfo.width  = tmprectwidth;
+                       tmpinfo.height = (tmporig->bottom + tmpalignmask)
+                                      & ~tmpalignmask;
+
+                       tmpinfo.physwidth  = tmpinfo.height;
+                       tmpinfo.physheight = tmpinfo.width;
+                       break;
+
+               case ROT_ANGLE_270:
+                       tmporig->left   = 0;
+                       tmporig->right  = tmprectwidth;
+                       tmporig->bottom = dstadj->left & dstalignmask;
+
+                       tmpinfo.width  = tmprectwidth;
+                       tmpinfo.height = (tmporig->bottom + tmprectheight
+                                      + tmpalignmask) & ~tmpalignmask;
+
+                       tmporig->bottom = tmpinfo.height - tmporig->bottom;
+                       tmporig->top    = tmporig->bottom - tmprectheight;
+
+                       tmpinfo.physwidth  = tmpinfo.height;
+                       tmpinfo.physheight = tmpinfo.width;
+                       break;
+               }
+
+               GCPRINT_RECT(GCZONE_DEST, "tmp dest", tmporig);
+               GCDBG(GCZONE_FILTER, "tmp geometry size = %dx%d\n",
+                     tmpinfo.width, tmpinfo.height);
+               GCDBG(GCZONE_FILTER, "tmp physical size = %dx%d\n",
+                     tmpinfo.physwidth, tmpinfo.physheight);
+
+               /* Determine the size of the temporaty surface. */
+               tmpinfo.stride1 = (tmpinfo.physwidth
+                               *  tmpinfo.format.bitspp) / 8;
+               tmpsize = tmpinfo.stride1 * tmpinfo.physheight;
+               tmpsize += GC_BYTES_PER_CACHELINE;
+               tmpsize = (tmpsize + ~PAGE_MASK) & PAGE_MASK;
+               GCDBG(GCZONE_FILTER, "tmp stride = %d\n", tmpinfo.stride1);
+               GCDBG(GCZONE_FILTER, "tmp size (bytes) = %d\n", tmpsize);
+
+               /* Allocate the temporary buffer. */
+               bverror = allocate_temp(bvbltparams, tmpsize);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Map the temporary buffer. */
+               tmpinfo.buf.desc = gccontext->tmpbuffdesc;
+               bverror = do_map(tmpinfo.buf.desc, batch, &tmpmap);
+               if (bverror != BVERR_NONE) {
+                       bvbltparams->errdesc = gccontext->bverrorstr;
+                       goto exit;
+               }
+
+               /* Compute the temp buffer alignments needed to compensate
+                * for the surface base address misalignment if any. */
+               tmpinfo.xpixalign = 0;
+               tmpinfo.ypixalign = 0;
+               tmpinfo.bytealign1 = (get_pixel_offset(&tmpinfo, 0)
+                                  * (int) tmpinfo.format.bitspp) / 8;
+               GCDBG(GCZONE_SRC, "tmp offset (pixels) = %d,%d\n",
+                       tmpinfo.xpixalign, tmpinfo.ypixalign);
+               GCDBG(GCZONE_SRC, "tmp offset (bytes) = %d\n",
+                       tmpinfo.bytealign1);
+
+               /* Load the vertical filter. */
+               bverror = load_filter(bvbltparams, batch,
+                                     GC_FILTER_SYNC,
+                                     gcfilter->verkernelsize,
+                                     gcfilter->verscalefactor,
+                                     srcheight, dstheight,
+                                     gcmofilterkernel_shared_ldst);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Start the operation. */
+               GCDBG(GCZONE_TYPE, "vertical pass\n");
+               bverror = startvr(bvbltparams, batch,
+                                 srcmap, tmpmap, srcinfo, &tmpinfo,
+                                 srcx, srcy, tmporig,
+                                 ROT_ANGLE_0, angle,
+                                 GC_SCALE_VER);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Rotation is done in the first pass. */
+               tmpinfo.adjangle = (4 - angle) % 4;
+               adjust_angle(&tmpinfo, dstinfo);
+
+               /* Determine the source origin. */
+               switch (angle) {
+               case ROT_ANGLE_0:
+                       srcx = ((tmporig->left + leftextra) << 16)
+                            + (srcdelta.left & 0xFFFF);
+                       srcy = tmporig->top << 16;
+                       break;
+
+               case ROT_ANGLE_90:
+                       srcx = tmporig->left << 16;
+                       srcy = ((tmporig->top + rightextra) << 16)
+                            + ((~srcdelta.right + 1) & 0xFFFF);
+                       break;
+
+               case ROT_ANGLE_180:
+                       srcx = ((tmporig->left + rightextra) << 16)
+                            + ((~srcdelta.right + 1) & 0xFFFF);
+                       srcy = tmporig->top << 16;
+                       break;
+
+               case ROT_ANGLE_270:
+                       srcx = tmporig->left << 16;
+                       srcy = ((tmporig->top + leftextra) << 16)
+                            + (srcdelta.left & 0xFFFF);
+                       break;
+               }
+
+               GCDBG(GCZONE_SRC, "src origin: 0x%08X,0x%08X\n", srcx, srcy);
+
+               /* Load the horizontal filter. */
+               bverror = load_filter(bvbltparams, batch,
+                                     GC_FILTER_SYNC,
+                                     gcfilter->horkernelsize,
+                                     gcfilter->horscalefactor,
+                                     srcwidth, dstwidth,
+                                     gcmofilterkernel_shared_ldst);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+
+               /* Start the operation. */
+               GCDBG(GCZONE_TYPE, "horizontal pass\n");
+               bverror = startvr(bvbltparams, batch,
+                                 tmpmap, dstmap, &tmpinfo, dstinfo,
+                                 srcx, srcy, dstadj,
+                                 ROT_ANGLE_0, ROT_ANGLE_0,
+                                 ((angle % 2) == 0)
+                                       ? GC_SCALE_HOR
+                                       : GC_SCALE_HOR_FLIPPED);
+               if (bverror != BVERR_NONE)
+                       goto exit;
+       } else {
+               GCDBG(GCZONE_TYPE, "two pass (%s pass config).\n",
+                     scalex ? "horizontal" : "vertical");
+
+               /* Setup single pass. */
+               srcx = (srcorig->left << 16) + srcdelta.left;
+               srcy = (srcorig->top  << 16) + srcdelta.top;
+               GCDBG(GCZONE_SRC, "src origin: 0x%08X,0x%08X\n", srcx, srcy);
+
+               if (scalex) {
+                       /* Load the horizontal filter. */
+                       bverror = load_filter(bvbltparams, batch,
+                                             GC_FILTER_SYNC,
+                                             gcfilter->horkernelsize,
+                                             gcfilter->horscalefactor,
+                                             srcwidth, dstwidth,
+                                             gcmofilterkernel_shared_ldst);
+                       if (bverror != BVERR_NONE)
+                               goto exit;
+
+                       /* Start the operation. */
+                       bverror = startvr(bvbltparams, batch,
+                                         srcmap, dstmap, srcinfo, dstinfo,
+                                         srcx, srcy, dstadj,
+                                         ROT_ANGLE_0, angle,
+                                         GC_SCALE_HOR);
+                       if (bverror != BVERR_NONE)
+                               goto exit;
+               } else {
+                       /* Load the vertical filter. */
+                       bverror = load_filter(bvbltparams, batch,
+                                             GC_FILTER_SYNC,
+                                             gcfilter->verkernelsize,
+                                             gcfilter->verscalefactor,
+                                             srcheight, dstheight,
+                                             gcmofilterkernel_shared_ldst);
+                       if (bverror != BVERR_NONE)
+                               goto exit;
+
+                       /* Start the operation. */
+                       bverror = startvr(bvbltparams, batch,
+                                         srcmap, dstmap, srcinfo, dstinfo,
+                                         srcx, srcy, dstadj,
+                                         ROT_ANGLE_0, angle,
+                                         GC_SCALE_VER);
+                       if (bverror != BVERR_NONE)
+                               goto exit;
+               }
+       }
+
+exit:
+       GCEXITARG(GCZONE_FILTER, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
diff --git a/drivers/misc/gcx/gcbv/gcmain.c b/drivers/misc/gcx/gcbv/gcmain.c
new file mode 100644 (file)
index 0000000..aa8be66
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * gcmain.c
+ *
+ * Copyright (C) 2010-2011 Vivante Corporation.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "gcbv.h"
+#include <plat/cpu.h>
+#include <plat/omap_hwmod.h>
+
+
+/*******************************************************************************
+ * BLTsville interface exposure.
+ */
+
+static struct bventry ops = {
+       .structsize = sizeof(struct bventry),
+};
+
+static void gcbv_clear(void)
+{
+       ops.bv_map = NULL;
+       ops.bv_unmap = NULL;
+       ops.bv_blt = NULL;
+       ops.bv_cache = NULL;
+}
+
+static void gcbv_assign(void)
+{
+       ops.bv_map = bv_map;
+       ops.bv_unmap = bv_unmap;
+       ops.bv_blt = bv_blt;
+       ops.bv_cache = bv_cache;
+}
+
+void gcbv_init(struct bventry *entry)
+{
+       *entry = ops;
+}
+EXPORT_SYMBOL(gcbv_init);
+
+
+/*******************************************************************************
+ * Convert floating point in 0..1 range to an 8-bit value in range 0..255.
+ */
+
+union gcfp {
+       struct {
+               unsigned int mantissa:23;
+               unsigned int exponent:8;
+               unsigned int sign:1;
+       } comp;
+
+       float value;
+};
+
+unsigned char gcfp2norm8(float value)
+{
+       union gcfp gcfp;
+       int exponent;
+       unsigned int mantissa;
+       int shift;
+
+       /* Get access to components. */
+       gcfp.value = value;
+
+       /* Clamp negatives. */
+       if (gcfp.comp.sign)
+               return 0;
+
+       /* Get unbiased exponent. */
+       exponent = (int) gcfp.comp.exponent - 127;
+
+       /* Clamp if too large. */
+       if (exponent >= 0)
+               return 255;
+
+       /* Clamp if too small. */
+       if (exponent < -8)
+               return 0;
+
+       /* Determine the shift value. */
+       shift = (23 - 8) - exponent;
+
+       /* Compute the mantissa. */
+       mantissa = (gcfp.comp.mantissa | 0x00800000) >> shift;
+
+       /* Normalize. */
+       mantissa = (mantissa * 255) >> 8;
+
+       return (unsigned char) mantissa;
+}
+
+
+/*******************************************************************************
+ * Surface allocation.
+ */
+
+enum bverror allocate_surface(struct bvbuffdesc **bvbuffdesc,
+                             void **buffer,
+                             unsigned int size)
+{
+       enum bverror bverror;
+       struct bvbuffdesc *tempbuffdesc = NULL;
+       void *tempbuff = NULL;
+       unsigned long *temparray = NULL;
+       struct bvphysdesc *tempphysdesc = NULL;
+       unsigned char *pageaddr;
+       unsigned int i;
+
+       /* Allocate surface buffer descriptor. */
+       tempbuffdesc = vmalloc(sizeof(struct bvbuffdesc));
+       if (tempbuffdesc == NULL) {
+               BVSETERROR(BVERR_OOM, "failed to allocate surface");
+               goto exit;
+       }
+
+       /* Initialize buffer descriptor. */
+       tempbuffdesc->structsize = sizeof(struct bvbuffdesc);
+       tempbuffdesc->virtaddr = NULL;
+       tempbuffdesc->length = size;
+       tempbuffdesc->map = NULL;
+       tempbuffdesc->auxtype = BVAT_PHYSDESC;
+       tempbuffdesc->auxptr = NULL;
+
+       /* Allocate the surface. */
+       tempbuff = vmalloc(size);
+       if (tempbuff == NULL) {
+               BVSETERROR(BVERR_OOM, "failed to allocate surface");
+               goto exit;
+       }
+       tempbuffdesc->virtaddr = tempbuff;
+
+       /* Allocate the physical descriptor. */
+       tempphysdesc = vmalloc(sizeof(struct bvphysdesc));
+       if (tempphysdesc == NULL) {
+               BVSETERROR(BVERR_OOM, "failed to allocate surface");
+               goto exit;
+       }
+       tempbuffdesc->auxptr = tempphysdesc;
+
+       /* Initialize physical descriptor. */
+       tempphysdesc->structsize = sizeof(struct bvphysdesc);
+       tempphysdesc->pagesize = PAGE_SIZE;
+       tempphysdesc->pagearray = NULL;
+       tempphysdesc->pagecount = (size + PAGE_SIZE - 1) / PAGE_SIZE;
+       tempphysdesc->pageoffset = 0;
+
+       /* Allocate array of pages. */
+       temparray = vmalloc(tempphysdesc->pagecount * sizeof(unsigned long));
+       if (temparray == NULL) {
+               BVSETERROR(BVERR_OOM, "failed to allocate surface");
+               goto exit;
+       }
+       tempphysdesc->pagearray = temparray;
+
+       /* Initialize the array. */
+       pageaddr = (unsigned char *) tempbuff;
+       for (i = 0; i < tempphysdesc->pagecount; i += 1) {
+               temparray[i] = PFN_PHYS(vmalloc_to_pfn(pageaddr));
+               pageaddr += PAGE_SIZE;
+       }
+
+       /* Set return pointers. */
+       *bvbuffdesc = tempbuffdesc;
+       *buffer = tempbuff;
+       return BVERR_NONE;
+
+exit:
+       free_surface(tempbuffdesc, tempbuff);
+       return bverror;
+}
+
+void free_surface(struct bvbuffdesc *bvbuffdesc,
+                 void *buffer)
+{
+       if (bvbuffdesc != NULL) {
+               if (bvbuffdesc->virtaddr != NULL)
+                       vfree(bvbuffdesc->virtaddr);
+
+               if (bvbuffdesc->auxptr != NULL) {
+                       struct bvphysdesc *bvphysdesc;
+
+                       bvphysdesc = (struct bvphysdesc *) bvbuffdesc->auxptr;
+                       if (bvphysdesc->pagearray != NULL)
+                               vfree(bvphysdesc->pagearray);
+
+                       vfree(bvphysdesc);
+               }
+
+               vfree(bvbuffdesc);
+       }
+}
+
+
+/*******************************************************************************
+ * Cache operation wrapper.
+ */
+
+enum bverror gcbvcacheop(int count, struct c2dmrgn rgn[],
+                        enum bvcacheop cacheop)
+{
+       enum bverror err = BVERR_NONE;
+
+       switch (cacheop) {
+
+       case DMA_FROM_DEVICE:
+               c2dm_l2cache(count, rgn, cacheop);
+               c2dm_l1cache(count, rgn, cacheop);
+               break;
+
+       case DMA_TO_DEVICE:
+               c2dm_l1cache(count, rgn, cacheop);
+               c2dm_l2cache(count, rgn, cacheop);
+               break;
+
+       case DMA_BIDIRECTIONAL:
+               c2dm_l1cache(count, rgn, cacheop);
+               c2dm_l2cache(count, rgn, cacheop);
+               break;
+
+       default:
+               err = BVERR_CACHEOP;
+               break;
+       }
+
+       return err;
+}
+
+
+/*******************************************************************************
+ * Device init/cleanup.
+ */
+
+static int __init mod_init(void)
+{
+       bv_init();
+
+       /* Assign BV function parameters only if SoC contains a GC core */
+       if (cpu_is_omap447x())
+               gcbv_assign();
+
+       return 0;
+}
+
+static void __exit mod_exit(void)
+{
+       gcbv_clear();
+       bv_exit();
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("www.vivantecorp.com");
+MODULE_AUTHOR("www.ti.com");
+module_init(mod_init);
+module_exit(mod_exit);
diff --git a/drivers/misc/gcx/gcbv/gcmain.h b/drivers/misc/gcx/gcbv/gcmain.h
new file mode 100644 (file)
index 0000000..8032b32
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * gcmain.h
+ *
+ * Copyright (C) 2010-2011 Vivante Corporation.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef GCMAIN_H
+#define GCMAIN_H
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/gcx.h>
+#include <linux/gcioctl.h>
+#include <linux/gccore.h>
+#include <linux/bltsville.h>
+#include <linux/bvinternal.h>
+#include "gcbvdebug.h"
+
+#define GC_DEV_NAME    "gc2d"
+
+
+/*******************************************************************************
+ * Miscellaneous macros.
+ */
+
+#define gcalloc(type, size) \
+       kmalloc(size, GFP_KERNEL)
+
+#define gcfree(ptr) \
+       kfree(ptr)
+
+
+/*******************************************************************************
+ * Core driver API definitions.
+ */
+
+#define gc_getcaps_wrapper(gcicaps) \
+       gc_caps(gcicaps)
+
+#define gc_commit_wrapper(gcicommit) \
+       gc_commit(gcicommit, false)
+
+#define gc_map_wrapper(gcimap) \
+       gc_map(gcimap, false)
+
+#define gc_unmap_wrapper(gcimap) \
+       gc_unmap(gcimap, false)
+
+#define gc_callback_wrapper(gcicallbackarm) \
+       gc_callback(gcicallbackarm, false)
+
+
+/*******************************************************************************
+ * Surface allocation.
+ */
+
+enum bverror allocate_surface(struct bvbuffdesc **bvbuffdesc,
+                             void **buffer,
+                             unsigned int size);
+
+void free_surface(struct bvbuffdesc *bvbuffdesc,
+                 void *buffer);
+
+
+/*******************************************************************************
+ * Floating point conversions.
+ */
+
+unsigned char gcfp2norm8(float value);
+
+
+/*******************************************************************************
+ * Cache operation wrapper.
+ */
+
+enum bverror gcbvcacheop(int count, struct c2dmrgn rgn[],
+                        enum bvcacheop cacheop);
+
+
+/*******************************************************************************
+ * BLTsville API.
+ */
+
+void bv_init(void);
+void bv_exit(void);
+
+enum bverror bv_map(struct bvbuffdesc *buffdesc);
+enum bverror bv_unmap(struct bvbuffdesc *buffdesc);
+enum bverror bv_blt(struct bvbltparams *bltparams);
+enum bverror bv_cache(struct bvcopparams *copparams);
+
+#endif
diff --git a/drivers/misc/gcx/gcbv/gcmap.c b/drivers/misc/gcx/gcbv/gcmap.c
new file mode 100644 (file)
index 0000000..6b5fcbe
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Vivante Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "gcbv.h"
+
+#define GCZONE_NONE            0
+#define GCZONE_ALL             (~0U)
+#define GCZONE_MAPPING         (1 << 0)
+
+GCDBG_FILTERDEF(map, GCZONE_NONE,
+               "mapping")
+
+
+/*******************************************************************************
+ * Memory management.
+ */
+
+enum bverror do_map(struct bvbuffdesc *bvbuffdesc,
+                   struct gcbatch *batch,
+                   struct bvbuffmap **map)
+{
+       static const int mapsize
+               = sizeof(struct bvbuffmap)
+               + sizeof(struct bvbuffmapinfo);
+
+       enum bverror bverror;
+       struct gccontext *gccontext = get_context();
+       struct bvbuffmap *bvbuffmap;
+       struct bvbuffmapinfo *bvbuffmapinfo;
+       struct bvphysdesc *bvphysdesc;
+       bool mappedbyothers;
+       struct gcimap gcimap;
+       struct gcschedunmap *gcschedunmap;
+
+       GCENTERARG(GCZONE_MAPPING, "bvbuffdesc = 0x%08X\n",
+                  (unsigned int) bvbuffdesc);
+
+       /* Lock access to the mapping list. */
+       GCLOCK(&gccontext->maplock);
+
+       /* Try to find existing mapping. */
+       bvbuffmap = bvbuffdesc->map;
+       while (bvbuffmap != NULL) {
+               if (bvbuffmap->bv_unmap == bv_unmap)
+                       break;
+               bvbuffmap = bvbuffmap->nextmap;
+       }
+
+       /* Not mapped yet? */
+       if (bvbuffmap == NULL) {
+               /* New mapping, allocate a record. */
+               if (gccontext->buffmapvac == NULL) {
+                       bvbuffmap = gcalloc(struct bvbuffmap, mapsize);
+                       if (bvbuffmap == NULL) {
+                               BVSETERROR(BVERR_OOM,
+                                          "failed to allocate mapping record");
+                               goto fail;
+                       }
+
+                       bvbuffmap->structsize = sizeof(struct bvbuffmap);
+                       bvbuffmap->bv_unmap = bv_unmap;
+                       bvbuffmap->handle = (unsigned long) (bvbuffmap + 1);
+               } else {
+                       bvbuffmap = gccontext->buffmapvac;
+                       gccontext->buffmapvac = bvbuffmap->nextmap;
+               }
+
+               /* Setup buffer mapping. Here we need to check and make sure
+                * that the buffer starts at a location that is supported by
+                * the hw. If it is not, offset is computed and the buffer is
+                * extended by the value of the offset. */
+               gcimap.gcerror = GCERR_NONE;
+               gcimap.handle = 0;
+
+               if (bvbuffdesc->auxtype == BVAT_PHYSDESC) {
+                       bvphysdesc = (struct bvphysdesc *) bvbuffdesc->auxptr;
+
+                       if (bvphysdesc->structsize <
+                           STRUCTSIZE(bvphysdesc, pageoffset)) {
+                               BVSETERROR(BVERR_BUFFERDESC_VERS,
+                                          "unsupported bvphysdesc version");
+                               goto fail;
+                       }
+
+                       gcimap.buf.offset = bvphysdesc->pageoffset;
+                       gcimap.pagesize = bvphysdesc->pagesize;
+                       gcimap.pagearray = bvphysdesc->pagearray;
+                       gcimap.size = bvbuffdesc->length;
+
+                       GCDBG(GCZONE_MAPPING, "new mapping (%s):\n",
+                             (batch == NULL) ? "explicit" : "implicit");
+                       GCDBG(GCZONE_MAPPING, "pagesize = %lu\n",
+                             bvphysdesc->pagesize);
+                       GCDBG(GCZONE_MAPPING, "pagearray = 0x%08X\n",
+                             (unsigned int) bvphysdesc->pagearray);
+                       GCDBG(GCZONE_MAPPING, "pageoffset = %lu\n",
+                             bvphysdesc->pageoffset);
+                       GCDBG(GCZONE_MAPPING, "mapping size = %d\n",
+                             gcimap.size);
+               } else {
+                       gcimap.buf.logical = bvbuffdesc->virtaddr;
+                       gcimap.pagesize = 0;
+                       gcimap.pagearray = NULL;
+                       gcimap.size = bvbuffdesc->length;
+
+                       GCDBG(GCZONE_MAPPING, "new mapping (%s):\n",
+                             (batch == NULL) ? "explicit" : "implicit");
+                       GCDBG(GCZONE_MAPPING, "specified virtaddr = 0x%08X\n",
+                             (unsigned int) bvbuffdesc->virtaddr);
+                       GCDBG(GCZONE_MAPPING, "aligned virtaddr = 0x%08X\n",
+                             (unsigned int) gcimap.buf.logical);
+                       GCDBG(GCZONE_MAPPING, "mapping size = %d\n",
+                             gcimap.size);
+               }
+
+               gc_map_wrapper(&gcimap);
+               if (gcimap.gcerror != GCERR_NONE) {
+                       BVSETERROR(BVERR_OOM,
+                                  "unable to allocate gccore memory");
+                       goto fail;
+               }
+
+               /* Set map handle. */
+               bvbuffmapinfo = (struct bvbuffmapinfo *) bvbuffmap->handle;
+               bvbuffmapinfo->handle = gcimap.handle;
+
+               /* Initialize reference counters. */
+               if (batch == NULL) {
+                       /* Explicit mapping. */
+                       bvbuffmapinfo->usermap = 1;
+                       bvbuffmapinfo->automap = 0;
+               } else {
+                       /* Implicit mapping; if there are existing mappings
+                        * from other implementations, mark this an explicit
+                        * mapping as well. */
+                       mappedbyothers = (bvbuffdesc->map != NULL);
+                       GCDBG(GCZONE_MAPPING, "%smapped by others.\n",
+                             mappedbyothers ? "" : "not ");
+
+                       bvbuffmapinfo->usermap = mappedbyothers ? 1 : 0;
+                       bvbuffmapinfo->automap = 1;
+               }
+
+               /* Add the record to the list of mappings. */
+               bvbuffmap->nextmap = bvbuffdesc->map;
+               bvbuffdesc->map = bvbuffmap;
+       } else {
+               /* Mapping already exists. */
+               bvbuffmapinfo = (struct bvbuffmapinfo *) bvbuffmap->handle;
+
+               /* Advance reference counters. */
+               if (batch == NULL) {
+                       /* Explicit mapping. */
+                       GCDBG(GCZONE_MAPPING, "explicit map.\n");
+                       bvbuffmapinfo->usermap += 1;
+               } else {
+                       /* Implicit mapping. */
+                       GCDBG(GCZONE_MAPPING, "implicit map.\n");
+                       bvbuffmapinfo->automap += 1;
+               }
+
+               GCDBG(GCZONE_MAPPING, "mapping exists.\n");
+       }
+
+       GCDBG(GCZONE_MAPPING, "bvbuffmap = 0x%08X\n",
+               (unsigned int) bvbuffmap);
+       GCDBG(GCZONE_MAPPING, "explicit count = %d\n",
+               bvbuffmapinfo->usermap);
+       GCDBG(GCZONE_MAPPING, "implicit count = %d\n",
+               bvbuffmapinfo->automap);
+
+       /* Schedule for unmapping. */
+       if (batch != NULL) {
+               if (list_empty(&gccontext->unmapvac)) {
+                       gcschedunmap = gcalloc(struct gcschedunmap,
+                                              sizeof(struct gcschedunmap));
+                       if (gcschedunmap == NULL) {
+                               BVSETERROR(BVERR_OOM,
+                                          "failed to schedule unmapping");
+                               goto fail;
+                       }
+                       list_add(&gcschedunmap->link, &batch->unmap);
+               } else {
+                       struct list_head *head;
+                       head = gccontext->unmapvac.next;
+                       gcschedunmap = list_entry(head,
+                                                 struct gcschedunmap,
+                                                 link);
+                       list_move(&gcschedunmap->link, &batch->unmap);
+               }
+
+               gcschedunmap->handle = (unsigned long) bvbuffdesc;
+
+               GCDBG(GCZONE_MAPPING, "scheduled for unmapping.\n");
+       }
+
+       /* Set the map pointer. */
+       *map = bvbuffmap;
+
+       /* Unlock access to the mapping list. */
+       GCUNLOCK(&gccontext->maplock);
+
+       GCEXITARG(GCZONE_MAPPING, "handle = 0x%08X\n",
+                 bvbuffmapinfo->handle);
+       return BVERR_NONE;
+
+fail:
+       if (bvbuffmap != NULL) {
+               bvbuffmap->nextmap = gccontext->buffmapvac;
+               gccontext->buffmapvac = bvbuffmap;
+       }
+
+       /* Unlock access to the mapping list. */
+       GCUNLOCK(&gccontext->maplock);
+
+       GCEXITARG(GCZONE_MAPPING, "bverror = %d\n", bverror);
+       return bverror;
+}
+
+void do_unmap_implicit(struct gcbatch *batch)
+{
+       struct gccontext *gccontext = get_context();
+       struct list_head *head, *temphead;
+       struct gcschedunmap *gcschedunmap;
+       struct bvbuffdesc *bvbuffdesc;
+       struct bvbuffmap *prev, *bvbuffmap;
+       struct bvbuffmapinfo *bvbuffmapinfo;
+
+       GCENTER(GCZONE_MAPPING);
+
+       /* Lock access to the mapping list. */
+       GCLOCK(&gccontext->maplock);
+
+       /* Scan scheduled unmappings and remove the ones that are still
+        * in use. */
+       list_for_each_safe(head, temphead, &batch->unmap) {
+               gcschedunmap = list_entry(head, struct gcschedunmap, link);
+
+               /* Cast the handle. */
+               bvbuffdesc = (struct bvbuffdesc *) gcschedunmap->handle;
+
+               /* Find our mapping. */
+               prev = NULL;
+               bvbuffmap = bvbuffdesc->map;
+               while (bvbuffmap != NULL) {
+                       if (bvbuffmap->bv_unmap == bv_unmap)
+                               break;
+                       prev = bvbuffmap;
+                       bvbuffmap = bvbuffmap->nextmap;
+               }
+
+               /* Not found? */
+               if (bvbuffmap == NULL) {
+                       GCERR("mapping not found for bvbuffdesc 0x%08X.\n",
+                             (unsigned int) bvbuffdesc);
+
+                       /* Remove scheduled unmapping. */
+                       list_move(head, &gccontext->unmapvac);
+                       continue;
+               }
+
+               /* Get the info structure. */
+               bvbuffmapinfo = (struct bvbuffmapinfo *) bvbuffmap->handle;
+
+               GCDBG(GCZONE_MAPPING, "head = 0x%08X\n",
+                     (unsigned int) gcschedunmap);
+               GCDBG(GCZONE_MAPPING, "  bvbuffmap = 0x%08X\n",
+                     (unsigned int) bvbuffmap);
+               GCDBG(GCZONE_MAPPING, "  handle = 0x%08X\n",
+                     bvbuffmapinfo->handle);
+
+               /* Implicit unmapping. */
+               bvbuffmapinfo->automap -= 1;
+               if (bvbuffmapinfo->automap < 0) {
+                       GCERR("implicit count negative.\n");
+                       bvbuffmapinfo->automap = 0;
+               }
+
+               GCDBG(GCZONE_MAPPING, "  explicit count = %d\n",
+                     bvbuffmapinfo->usermap);
+               GCDBG(GCZONE_MAPPING, "  implicit count = %d\n",
+                     bvbuffmapinfo->automap);
+
+               /* Still referenced? */
+               if (bvbuffmapinfo->usermap || bvbuffmapinfo->automap) {
+                       GCDBG(GCZONE_MAPPING, "  still referenced.\n");
+
+                       /* Remove scheduled unmapping. */
+                       list_move(head, &gccontext->unmapvac);
+                       continue;
+               }
+
+               GCDBG(GCZONE_MAPPING, "  ready for unmapping.\n");
+
+               /* Set the handle. */
+               gcschedunmap->handle = bvbuffmapinfo->handle;
+
+               /* Remove from the buffer descriptor. */
+               if (prev == NULL)
+                       bvbuffdesc->map = bvbuffmap->nextmap;
+               else
+                       prev->nextmap = bvbuffmap->nextmap;
+
+               /* Add to the vacant list. */
+               bvbuffmap->nextmap = gccontext->buffmapvac;
+               gccontext->buffmapvac = bvbuffmap;
+       }
+
+       /* Unlock access to the mapping list. */
+       GCUNLOCK(&gccontext->maplock);
+
+       GCEXIT(GCZONE_MAPPING);
+}
diff --git a/drivers/misc/gcx/gcbv/gcparser.c b/drivers/misc/gcx/gcbv/gcparser.c
new file mode 100644 (file)
index 0000000..b90d398
--- /dev/null
@@ -0,0 +1,2416 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 Vivante Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Vivante Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "gcbv.h"
+
+#define GCZONE_NONE            0
+#define GCZONE_ALL             (~0U)
+#define GCZONE_FORMAT          (1 << 0)
+#define GCZONE_FORMAT_VERBOSE  (1 << 1)
+#define GCZONE_BLEND           (1 << 2)
+#define GCZONE_OFFSET          (1 << 3)
+#define GCZONE_DEST            (1 << 4)
+#define GCZONE_SRC             (1 << 5)
+#define GCZONE_SCALING         (1 << 6)
+#define GCZONE_SURF            (1 << 7)
+
+GCDBG_FILTERDEF(parser, GCZONE_NONE,
+               "format",
+               "formatverbose",
+               "blend",
+               "offset",
+               "dest",
+               "src",
+               "scaling",
+               "surface")
+
+
+/*******************************************************************************
+ * Internal macros.
+ */
+
+#define GCCONVERT_RECT(zone, name, bvrect, gcrect) \
+{ \
+       (gcrect)->left = (bvrect)->left; \
+       (gcrect)->top = (bvrect)->top; \
+       (gcrect)->right = (bvrect)->left + (bvrect)->width; \
+       (gcrect)->bottom = (bvrect)->top + (bvrect)->height; \
+       \
+       GCPRINT_RECT(zone, name, gcrect); \
+}
+
+
+/*******************************************************************************
+ * Pixel format parser.
+ */
+
+#define OCDFMTDEF_PLACEMENT_SHIFT 9
+#define OCDFMTDEF_PLACEMENT_MASK (3 << OCDFMTDEF_PLACEMENT_SHIFT)
+
+#define BVCOMP(Shift, Size) \
+       { Shift, Size, ((1 << Size) - 1) << Shift }
+
+#define BVRED(Shift, Size) \
+       BVCOMP(Shift, Size)
+
+#define BVGREEN(Shift, Size) \
+       BVCOMP(Shift, Size)
+
+#define BVBLUE(Shift, Size) \
+       BVCOMP(Shift, Size)
+
+#define BVALPHA(Shift, Size) \
+       BVCOMP(Shift, Size)
+
+static const unsigned int rgba16swizzle[] = {
+       GCREG_DE_SWIZZLE_ARGB,
+       GCREG_DE_SWIZZLE_RGBA,
+       GCREG_DE_SWIZZLE_ABGR,
+       GCREG_DE_SWIZZLE_BGRA
+};
+
+static const unsigned int rgb16swizzle[] = {
+       GCREG_DE_SWIZZLE_ARGB,
+       GCREG_DE_SWIZZLE_ARGB,
+       GCREG_DE_SWIZZLE_ABGR,
+       GCREG_DE_SWIZZLE_ABGR
+};
+
+static const unsigned int rgba32swizzle[] = {
+       GCREG_DE_SWIZZLE_ARGB,
+       GCREG_DE_SWIZZLE_ABGR,
+       GCREG_DE_SWIZZLE_ABGR,
+       GCREG_DE_SWIZZLE_ARGB
+};
+
+static const struct bvcsrgb xrgb4444_bits[] = {
+       { BVRED(8,  4), BVGREEN(4, 4), BVBLUE(0,  4), BVALPHA(12, 0) },
+       { BVRED(12, 4), BVGREEN(8, 4), BVBLUE(4,  4), BVALPHA(0,  0) },
+       { BVRED(0,  4), BVGREEN(4, 4), BVBLUE(8,  4), BVALPHA(12, 0) },
+       { BVRED(4,  4), BVGREEN(8, 4), BVBLUE(12, 4), BVALPHA(0,  0) }
+};
+
+static const struct bvcsrgb argb4444_bits[] = {
+       { BVRED(8,  4), BVGREEN(4, 4), BVBLUE(0,  4), BVALPHA(12, 4) },
+       { BVRED(12, 4), BVGREEN(8, 4), BVBLUE(4,  4), BVALPHA(0,  4) },
+       { BVRED(0,  4), BVGREEN(4, 4), BVBLUE(8,  4), BVALPHA(12, 4) },
+       { BVRED(4,  4), BVGREEN(8, 4), BVBLUE(12, 4), BVALPHA(0,  4) }
+};
+
+static const struct bvcsrgb xrgb1555_bits[] = {
+       { BVRED(10, 5), BVGREEN(5, 5), BVBLUE(0,  5), BVALPHA(15, 0) },
+       { BVRED(11, 5), BVGREEN(6, 5), BVBLUE(1,  5), BVALPHA(0,  0) },
+       { BVRED(0,  5), BVGREEN(5, 5), BVBLUE(10, 5), BVALPHA(15, 0) },
+       { BVRED(1,  5), BVGREEN(6, 5), BVBLUE(11, 5), BVALPHA(0,  0) }
+};
+
+static const struct bvcsrgb argb1555_bits[] = {
+       { BVRED(10, 5), BVGREEN(5, 5), BVBLUE(0,  5), BVALPHA(15, 1) },
+       { BVRED(11, 5), BVGREEN(6, 5), BVBLUE(1,  5), BVALPHA(0,  1) },
+       { BVRED(0,  5), BVGREEN(5, 5), BVBLUE(10, 5), BVALPHA(15, 1) },
+       { BVRED(1,  5), BVGREEN(6, 5), BVBLUE(11, 5), BVALPHA(0,  1) }
+};
+
+static const struct bvcsrgb rgb565_bits[] = {
+       { BVRED(11, 5), BVGREEN(5, 6), BVBLUE(0,  5), BVALPHA(0, 0) },
+       { BVRED(11, 5), BVGREEN(5, 6), BVBLUE(0,  5), BVALPHA(0, 0) },
+       { BVRED(0,  5), BVGREEN(5, 6), BVBLUE(11, 5), BVALPHA(0, 0) },
+       { BVRED(0,  5), BVGREEN(5, 6), BVBLUE(11, 5), BVALPHA(0, 0) }
+};
+
+static const struct bvcsrgb xrgb8888_bits[] = {
+       { BVRED(8,  8), BVGREEN(16, 8), BVBLUE(24, 8), BVALPHA(0,  0) },
+       { BVRED(0,  8), BVGREEN(8,  8), BVBLUE(16, 8), BVALPHA(24, 0) },
+       { BVRED(24, 8), BVGREEN(16, 8), BVBLUE(8,  8), BVALPHA(0,  0) },
+       { BVRED(16, 8), BVGREEN(8,  8), BVBLUE(0,  8), BVALPHA(24, 0) }
+};
+
+static const struct bvcsrgb argb8888_bits[] = {
+       { BVRED(8,  8), BVGREEN(16, 8), BVBLUE(24, 8), BVALPHA(0,  8) },
+       { BVRED(0,  8), BVGREEN(8,  8), BVBLUE(16, 8), BVALPHA(24, 8) },
+       { BVRED(24, 8), BVGREEN(16, 8), BVBLUE(8,  8), BVALPHA(0,  8) },
+       { BVRED(16, 8), BVGREEN(8,  8), BVBLUE(0,  8), BVALPHA(24, 8) }
+};
+
+static const unsigned int container[] = {
+         8,    /* OCDFMTDEF_CONTAINER_8BIT */
+        16,    /* OCDFMTDEF_CONTAINER_16BIT */
+        24,    /* OCDFMTDEF_CONTAINER_24BIT */
+        32,    /* OCDFMTDEF_CONTAINER_32BIT */
+       ~0U,    /* reserved */
+        48,    /* OCDFMTDEF_CONTAINER_48BIT */
+       ~0U,    /* reserved */
+        64     /* OCDFMTDEF_CONTAINER_64BIT */
+};
+
+enum bverror parse_format(struct bvbltparams *bvbltparams,
+                         enum ocdformat ocdformat,
+                         struct bvformatxlate *format)
+{
+       enum bverror bverror = BVERR_NONE;
+       struct gccontext *gccontext = get_context();
+       unsigned int cs, std, alpha, subsample, layout;
+       unsigned int reversed, leftjust, swizzle, cont, bits;
+
+       GCENTERARG(GCZONE_FORMAT, "ocdformat = 0x%08X\n", ocdformat);
+
+       cs = (ocdformat & OCDFMTDEF_CS_MASK)
+               >> OCDFMTDEF_CS_SHIFT;
+       std = (ocdformat & OCDFMTDEF_STD_MASK)
+               >> OCDFMTDEF_STD_SHIFT;
+       alpha = ocdformat & OCDFMTDEF_ALPHA;
+       subsample = (ocdformat & OCDFMTDEF_SUBSAMPLE_MASK)
+               >> OCDFMTDEF_SUBSAMPLE_SHIFT;
+       layout = (ocdformat & OCDFMTDEF_LAYOUT_MASK)
+               >> OCDFMTDEF_LAYOUT_SHIFT;
+       cont = (ocdformat & OCDFMTDEF_CONTAINER_MASK)
+               >> OCDFMTDEF_CONTAINER_SHIFT;
+       bits = ((ocdformat & OCDFMTDEF_COMPONENTSIZEMINUS1_MASK)
+               >> OCDFMTDEF_COMPONENTSIZEMINUS1_SHIFT) + 1;
+
+       GCDBG(GCZONE_FORMAT_VERBOSE, "std = %d\n", std);
+       GCDBG(GCZONE_FORMAT_VERBOSE, "cs = %d\n", cs);
+       GCDBG(GCZONE_FORMAT_VERBOSE, "alpha = %d\n", alpha ? 1 : 0);
+       GCDBG(GCZONE_FORMAT_VERBOSE, "subsample = %d\n", subsample);
+       GCDBG(GCZONE_FORMAT_VERBOSE, "layout = %d\n", layout);
+       GCDBG(GCZONE_FORMAT_VERBOSE, "cont = %d\n", cont);
+       GCDBG(GCZONE_FORMAT_VERBOSE, "bits = %d\n", bits);
+
+       format->endian
+               = GCREG_DEST_CONFIG_ENDIAN_CONTROL_NO_SWAP;
+
+       switch (cs) {
+       case (OCDFMTDEF_CS_RGB >> OCDFMTDEF_CS_SHIFT):
+               GCDBG(GCZONE_FORMAT, "OCDFMTDEF_CS_RGB\n");
+
+               /* Determine the swizzle. */
+               swizzle = (ocdformat & OCDFMTDEF_PLACEMENT_MASK)
+                       >> OCDFMTDEF_PLACEMENT_SHIFT;
+               GCDBG(GCZONE_FORMAT, "swizzle = %d\n", swizzle);
+
+               /* RGB color space. */
+               format->type = BVFMT_RGB;
+
+               /* Has to be 0 for RGB. */
+               if (std != 0) {
+                       BVSETBLTERROR(BVERR_UNK,
+                                     "unsupported standard");
+                       goto exit;
+               }
+
+               /* Determine premultuplied or not. */
+               if (alpha == OCDFMTDEF_ALPHA) {
+                       format->premultiplied
+                               = ((ocdformat & OCDFMTDEF_NON_PREMULT) == 0);
+                       format->zerofill = false;
+               } else {
+                       format->premultiplied = true;
+                       format->zerofill
+                               = ((ocdformat & OCDFMTDEF_FILL_EMPTY_0) != 0);
+               }
+               GCDBG(GCZONE_FORMAT, "premultiplied = %d\n",
+                     format->premultiplied);
+
+               /* No subsample support. */
+               if (subsample !=
+                   (OCDFMTDEF_SUBSAMPLE_NONE >> OCDFMTDEF_SUBSAMPLE_SHIFT)) {
+                       BVSETBLTERROR(BVERR_UNK,
+                                       "subsampling for RGB is not supported");
+                       goto exit;
+               }
+
+               /* Only packed RGB is supported. */
+               if (layout !=
+                   (OCDFMTDEF_PACKED >> OCDFMTDEF_LAYOUT_SHIFT)) {
+                       BVSETBLTERROR(BVERR_UNK,
+                                     "only packed RGBA formats are supported");
+                       goto exit;
+               }
+
+               /* Determine the format. */
+               switch (bits) {
+               case 12:
+                       format->bitspp = 16;
+                       format->allocbitspp = 16;
+                       format->swizzle = rgba16swizzle[swizzle];
+
+                       if (alpha == OCDFMTDEF_ALPHA) {
+                               format->format = GCREG_DE_FORMAT_A4R4G4B4;
+                               format->cs.rgb.comp = &argb4444_bits[swizzle];
+                       } else {
+                               format->format = gccontext->gccaps.swizzlefixed
+                                       ? GCREG_DE_FORMAT_X4R4G4B4
+                                       : GCREG_DE_FORMAT_A4R4G4B4;
+                               format->cs.rgb.comp = &xrgb4444_bits[swizzle];
+                       }
+                       break;
+
+               case 15:
+                       format->bitspp = 16;
+                       format->allocbitspp = 16;
+                       format->swizzle = rgba16swizzle[swizzle];
+
+                       if (alpha == OCDFMTDEF_ALPHA) {
+                               format->format = GCREG_DE_FORMAT_A1R5G5B5;
+                               format->cs.rgb.comp = &argb1555_bits[swizzle];
+                       } else {
+                               format->format = GCREG_DE_FORMAT_X1R5G5B5;
+                               format->cs.rgb.comp = &xrgb1555_bits[swizzle];
+                       }
+                       break;
+
+               case 16:
+                       if (alpha == OCDFMTDEF_ALPHA) {
+                               BVSETBLTERROR(BVERR_UNK,
+                                             "alpha component is not supported"
+                                             "for this format.");
+                               goto exit;
+                       }
+
+                       format->bitspp = 16;
+                       format->allocbitspp = 16;
+                       format->swizzle = rgb16swizzle[swizzle];
+                       format->format = GCREG_DE_FORMAT_R5G6B5;
+                       format->cs.rgb.comp = &rgb565_bits[swizzle];
+                       break;
+
+               case 24:
+                       format->bitspp = 32;
+                       format->allocbitspp = 32;
+                       format->swizzle = rgba32swizzle[swizzle];
+
+                       if (alpha == OCDFMTDEF_ALPHA) {
+                               format->format = GCREG_DE_FORMAT_A8R8G8B8;
+                               format->cs.rgb.comp = &argb8888_bits[swizzle];
+                       } else {
+                               format->format = GCREG_DE_FORMAT_X8R8G8B8;
+                               format->cs.rgb.comp = &xrgb8888_bits[swizzle];
+                       }
+
+                       format->endian = ((swizzle & 1) == 0)
+                               ? GCREG_DEST_CONFIG_ENDIAN_CONTROL_SWAP_DWORD
+                               : GCREG_DEST_CONFIG_ENDIAN_CONTROL_NO_SWAP;
+                       break;
+
+               default:
+                       BVSETBLTERROR(BVERR_UNK,
+                                     "unsupported bit width %d", bits);
+                       goto exit;
+               }
+
+               if (format->allocbitspp != container[cont]) {
+                       BVSETBLTERROR(BVERR_UNK,
+                                     "unsupported container");
+                       goto exit;
+               }
+               break;
+
+       case (OCDFMTDEF_CS_YCbCr >> OCDFMTDEF_CS_SHIFT):
+               GCDBG(GCZONE_FORMAT, "OCDFMTDEF_CS_YCbCr\n");
+
+               /* YUV color space. */
+               format->type = BVFMT_YUV;
+
+               /* Determine the swizzle. */
+               reversed = ocdformat & OCDFMTDEF_REVERSED;
+               leftjust = ocdformat & OCDFMTDEF_LEFT_JUSTIFIED;
+               GCDBG(GCZONE_FORMAT_VERBOSE, "reversed = %d\n",
+                     reversed ? 1 : 0);
+               GCDBG(GCZONE_FORMAT_VERBOSE, "leftjust = %d\n",
+                     leftjust ? 1 : 0);
+
+               /* Parse the standard. */
+               switch (std) {
+               case OCDFMTDEF_STD_ITUR_601_YCbCr >> OCDFMTDEF_STD_SHIFT:
+                       GCDBG(GCZONE_FORMAT, "OCDFMTDEF_STD_ITUR_601_YCbCr\n");
+                       format->cs.yuv.std = GCREG_PE_CONTROL_YUV_601;
+                       break;
+
+               case OCDFMTDEF_STD_ITUR_709_YCbCr >> OCDFMTDEF_STD_SHIFT:
+                       GCDBG(GCZONE_FORMAT, "OCDFMTDEF_STD_ITUR_709_YCbCr\n");
+                       format->cs.yuv.std = GCREG_PE_CONTROL_YUV_709;
+                       break;
+
+               default:
+                       BVSETBLTERROR(BVERR_UNK,
+                                     "unsupported color standard");
+                       goto exit;
+               }
+
+               /* Alpha is not supported. */
+               if (alpha == OCDFMTDEF_ALPHA) {
+                       BVSETBLTERROR(BVERR_UNK,
+                                     "alpha channel is not supported");
+                       goto exit;
+               }
+
+               format->premultiplied = true;
+               format->zerofill = false;
+
+               /* Parse subsampling. */
+               switch (subsample) {
+               case OCDFMTDEF_SUBSAMPLE_422_YCbCr >> OCDFMTDEF_SUBSAMPLE_SHIFT:
+                       GCDBG(GCZONE_FORMAT, "OCDFMTDEF_SUBSAMPLE_422_YCbCr\n");
+
+                       /* Parse layout. */
+                       switch (layout) {
+                       case OCDFMTDEF_PACKED >> OCDFMTDEF_LAYOUT_SHIFT:
+                               GCDBG(GCZONE_FORMAT, "OCDFMTDEF_PACKED\n");
+
+                               if (container[cont] != 32) {
+                                       BVSETBLTERROR(BVERR_UNK,
+                                                     "unsupported container");
+                                       goto exit;
+                               }
+
+                               format->bitspp = 16;
+                               format->allocbitspp = 16;
+                               format->format = leftjust
+                                       ? GCREG_DE_FORMAT_YUY2
+                                       : GCREG_DE_FORMAT_UYVY;
+                               format->swizzle = reversed
+                                       ? GCREG_PE_CONTROL_UV_SWIZZLE_VU
+                                       : GCREG_PE_CONTROL_UV_SWIZZLE_UV;
+                               format->cs.yuv.planecount = 1;
+                               format->cs.yuv.xsample = 2;
+                               format->cs.yuv.ysample = 1;
+                               break;
+
+                       default:
+                               BVSETBLTERROR(BVERR_UNK,
+                                             "specified 4:2:2 layout "
+                                             "is not supported");
+                               goto exit;
+                       }
+                       break;
+
+               case OCDFMTDEF_SUBSAMPLE_420_YCbCr >> OCDFMTDEF_SUBSAMPLE_SHIFT:
+
+                       /* Parse layout. */
+                       switch (layout) {
+                       case OCDFMTDEF_2_PLANE_YCbCr
+                                               >> OCDFMTDEF_LAYOUT_SHIFT:
+                               GCDBG(GCZONE_FORMAT,
+                                     "OCDFMTDEF_2_PLANE_YCbCr\n");
+
+                               if (container[cont] != 48) {
+                                       BVSETBLTERROR(BVERR_UNK,
+                                                     "unsupported container");
+                                       goto exit;
+                               }
+
+                               format->bitspp = 8;
+                               format->allocbitspp = 12;
+                               format->format = GCREG_DE_FORMAT_NV12;
+                               format->swizzle = reversed
+                                       ? GCREG_PE_CONTROL_UV_SWIZZLE_VU
+                                       : GCREG_PE_CONTROL_UV_SWIZZLE_UV;
+                               format->cs.yuv.planecount = 2;
+                               format->cs.yuv.xsample = 2;
+                               format->cs.yuv.ysample = 2;
+                               break;
+
+                       case OCDFMTDEF_3_PLANE_STACKED
+                                               >> OCDFMTDEF_LAYOUT_SHIFT:
+                               GCDBG(GCZONE_FORMAT,
+                                     "OCDFMTDEF_3_PLANE_STACKED\n");
+
+                               if (container[cont] != 48) {
+                                       BVSETBLTERROR(BVERR_UNK,
+                                                     "unsupported container");
+                                       goto exit;
+                               }
+
+                               format->bitspp = 8;
+                               format->allocbitspp = 12;
+                               format->format = GCREG_DE_FORMAT_YV12;
+                               format->swizzle = reversed
+                                       ? GCREG_PE_CONTROL_UV_SWIZZLE_VU
+                                       : GCREG_PE_CONTROL_UV_SWIZZLE_UV;
+                               format->cs.yuv.planecount = 3;
+                               format->cs.yuv.xsample = 2;
+                               format->cs.yuv.ysample = 2;
+                               break;
+
+                       default:
+                               BVSETBLTERROR(BVERR_UNK,
+                                             "specified 4:2:2 layout "
+                                             "is not supported");
+                               goto exit;
+                       }
+                       break;
+
+               default:
+                       BVSETBLTERROR(BVERR_UNK,
+                                     "specified subsampling is not supported");
+                       goto exit;
+               }
+
+               if (format->allocbitspp != bits) {
+                       BVSETBLTERROR(BVERR_UNK,
+                                     "unsupported bit width %d", bits);
+                       goto exit;
+               }
+               break;
+
+       default:
+               BVSETBLTERROR(BVERR_UNK,
+                             "unsupported color space %d", cs);
+               goto exit;
+       }
+
+       GCDBG(GCZONE_FORMAT, "bpp = %d\n", format->bitspp);
+       GCDBG(GCZONE_FORMAT, "gcformat = %d\n", format->format);
+       GCDBG(GCZONE_FORMAT, "gcswizzle = %d\n", format->swizzle);
+
+       bverror = BVERR_NONE;
+
+exit:
+       GCEXITARG(GCZONE_FORMAT, "bv%s = %d\n",
+                 (bverror == BVERR_NONE) ? "result" : "error", bverror);
+       return bverror;
+}
+
+
+/*******************************************************************************
+ * Alpha blending parser.
+ */
+
+#define BVBLENDMATCH(Mode, Inverse, Normal) \
+( \
+       BVBLENDDEF_ ## Mode | \
+       BVBLENDDEF_ ## Inverse | \
+       BVBLENDDEF_ ## Normal \
+)
+
+#define BVSRC1USE(Use) \
+       Use
+
+#define BVSRC2USE(Use) \
+       Use
+
+#define BVBLENDUNDEFINED() \
+       { ~0, ~0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }
+
+struct bvblendxlate {
+       unsigned char match1;
+       unsigned char match2;
+
+       struct gcblendconfig k1;
+       struct gcblendconfig k2;
+};
+
+static struct bvblendxlate blendxlate[64] = {
+       /**********************************************************************/
+       /* #0: color factor: 00 00 00 A:(1-C1,C1)=zero
+              alpha factor: zero ==> 00 00 00 */
+       {
+               0x00,
+               0x00,
+
+               {
+                       GCREG_BLENDING_MODE_ZERO,
+                       GCREG_FACTOR_INVERSE_DISABLE,
+                       BVSRC1USE(false), BVSRC2USE(false)
+               },
+
+               {
+                       GCREG_BLENDING_MODE_ZERO,
+                       GCREG_FACTOR_INVERSE_DISABLE,
+                       BVSRC1USE(false), BVSRC2USE(false)
+               }
+       },
+
+       /* #1: color factor: 00 00 01 A:(1-C1,A1)=A1
+              alpha factor: A1 ==> 00 00 01 or 00 10 01 */
+       {
+               BVBLENDMATCH(ONLY_A, INV_C1, NORM_A1),
+               BVBLENDMATCH(ONLY_A, INV_C2, NORM_A1),
+
+               {
+                       GCREG_BLENDING_MODE_NORMAL,
+                       GCREG_FACTOR_INVERSE_ENABLE,
+                       BVSRC1USE(true), BVSRC2USE(false)
+               },
+
+               {
+                       GCREG_BLENDING_MODE_NORMAL,
+                       GCREG_FACTOR_INVERSE_DISABLE,
+                       BVSRC1USE(true), BVSRC2USE(true)
+               }
+       },
+
+       /* #2: color factor: 00 00 10 A:(1-C1,C2)=undefined
+              alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #3: color factor: 00 00 11 A:(1-C1,A2)=A2
+              alpha factor: A2 ==> 00 00 11 or 00 10 11 */
+       {
+               BVBLENDMATCH(ONLY_A, INV_C1, NORM_A2),
+               BVBLENDMATCH(ONLY_A, INV_C2, NORM_A2),
+
+               {
+                       GCREG_BLENDING_MODE_NORMAL,
+                       GCREG_FACTOR_INVERSE_DISABLE,
+                       BVSRC1USE(true), BVSRC2USE(true)
+               },
+
+               {
+                       GCREG_BLENDING_MODE_NORMAL,
+                       GCREG_FACTOR_INVERSE_ENABLE,
+                       BVSRC1USE(false), BVSRC2USE(true)
+               }
+       },
+
+       /* #4: color factor: 00 01 00 A:(1-A1,C1)=1-A1
+              alpha factor: 1-A1 ==> 00 01 00 or 00 01 10 */
+       {
+               BVBLENDMATCH(ONLY_A, INV_A1, NORM_C1),
+               BVBLENDMATCH(ONLY_A, INV_A1, NORM_C2),
+
+               {
+                       GCREG_BLENDING_MODE_INVERSED,
+                       GCREG_FACTOR_INVERSE_ENABLE,
+                       BVSRC1USE(true), BVSRC2USE(false)
+               },
+
+               {
+                       GCREG_BLENDING_MODE_INVERSED,
+                       GCREG_FACTOR_INVERSE_DISABLE,
+                       BVSRC1USE(true), BVSRC2USE(true)
+               }
+       },
+
+       /* #5: color factor: 00 01 01 A:(1-A1,A1)=undefined
+              alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #6: color factor: 00 01 10 A:(1-A1,C2)=1-A1
+              alpha factor: 1-A1 ==> 00 01 00 or 00 01 10 */
+       {
+               BVBLENDMATCH(ONLY_A, INV_A1, NORM_C1),
+               BVBLENDMATCH(ONLY_A, INV_A1, NORM_C2),
+
+               {
+                       GCREG_BLENDING_MODE_INVERSED,
+                       GCREG_FACTOR_INVERSE_ENABLE,
+                       BVSRC1USE(true), BVSRC2USE(false)
+               },
+
+               {
+                       GCREG_BLENDING_MODE_INVERSED,
+                       GCREG_FACTOR_INVERSE_DISABLE,
+                       BVSRC1USE(true), BVSRC2USE(true)
+               }
+       },
+
+       /* #7: color factor: 00 01 11 A:(1-A1,A2)=undefined
+              alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #8: color factor: 00 10 00 A:(1-C2,C1)=undefined
+              alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #9: color factor: 00 10 01 A:(1-C2,A1)=A1
+              alpha factor: A1 ==> 00 00 01 or 00 10 01 */
+       {
+               BVBLENDMATCH(ONLY_A, INV_C1, NORM_A1),
+               BVBLENDMATCH(ONLY_A, INV_C2, NORM_A1),
+
+               {
+                       GCREG_BLENDING_MODE_NORMAL,
+                       GCREG_FACTOR_INVERSE_ENABLE,
+                       BVSRC1USE(true), BVSRC2USE(false)
+               },
+
+               {
+                       GCREG_BLENDING_MODE_NORMAL,
+                       GCREG_FACTOR_INVERSE_DISABLE,
+                       BVSRC1USE(true), BVSRC2USE(true)
+               }
+       },
+
+       /* #10: color factor: 00 10 10 A:(1-C2,C2)=undefined
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #11: color factor: 00 10 11 A:(1-C2,A2)=A2
+               alpha factor: A2 ==> 00 00 11 or 00 10 11 */
+       {
+               BVBLENDMATCH(ONLY_A, INV_C1, NORM_A2),
+               BVBLENDMATCH(ONLY_A, INV_C2, NORM_A2),
+
+               {
+                       GCREG_BLENDING_MODE_NORMAL,
+                       GCREG_FACTOR_INVERSE_DISABLE,
+                       BVSRC1USE(true), BVSRC2USE(true)
+               },
+
+               {
+                       GCREG_BLENDING_MODE_NORMAL,
+                       GCREG_FACTOR_INVERSE_ENABLE,
+                       BVSRC1USE(false), BVSRC2USE(true)
+               }
+       },
+
+       /* #12: color factor: 00 11 00 A:(1-A2,C1)=1-A2
+               alpha factor: 1-A2 ==> 00 11 00 or 00 11 10 */
+       {
+               BVBLENDMATCH(ONLY_A, INV_A2, NORM_C1),
+               BVBLENDMATCH(ONLY_A, INV_A2, NORM_C2),
+
+               {
+                       GCREG_BLENDING_MODE_INVERSED,
+                       GCREG_FACTOR_INVERSE_DISABLE,
+                       BVSRC1USE(true), BVSRC2USE(true)
+               },
+
+               {
+                       GCREG_BLENDING_MODE_INVERSED,
+                       GCREG_FACTOR_INVERSE_ENABLE,
+                       BVSRC1USE(false), BVSRC2USE(true)
+               }
+       },
+
+       /* #13: color factor: 00 11 01 A:(1-A2,A1)=undefined
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #14: color factor: 00 11 10 A:(1-A2,C2)=1-A2
+               alpha factor: 1-A2 ==> 00 11 00 or 00 11 10 */
+       {
+               BVBLENDMATCH(ONLY_A, INV_A2, NORM_C1),
+               BVBLENDMATCH(ONLY_A, INV_A2, NORM_C2),
+
+               {
+                       GCREG_BLENDING_MODE_INVERSED,
+                       GCREG_FACTOR_INVERSE_DISABLE,
+                       BVSRC1USE(true), BVSRC2USE(true)
+               },
+
+               {
+                       GCREG_BLENDING_MODE_INVERSED,
+                       GCREG_FACTOR_INVERSE_ENABLE,
+                       BVSRC1USE(false), BVSRC2USE(true)
+               }
+       },
+
+       /* #15: color factor: 00 11 11 A:(1-A2,A2)=undefined
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /**********************************************************************/
+       /* #16: color factor: 01 00 00 MIN:(1-C1,C1) ==> not supported
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #17: color factor: 01 00 01 MIN:(1-C1,A1) ==> not supported
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #18: color factor: 01 00 10 MIN:(1-C1,C2) ==> not supported
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #19: color factor: 01 00 11 MIN:(1-C1,A2) ==> not supported
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #20: color factor: 01 01 00 MIN:(1-A1,C1) ==> not supported
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #21: color factor: 01 01 01 MIN:(1-A1,A1) ==> not supported
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #22: color factor: 01 01 10 MIN:(1-A1,C2) ==> not supported
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #23: color factor: 01 01 11 MIN:(1-A1,A2)
+               alpha factor: one ==> 11 11 11 */
+       {
+               0x3F,
+               0x3F,
+
+               {
+                       GCREG_BLENDING_MODE_SATURATED_DEST_ALPHA,
+                       GCREG_FACTOR_INVERSE_DISABLE,
+                       BVSRC1USE(true), BVSRC2USE(true)
+               },
+
+               {
+                       GCREG_BLENDING_MODE_SATURATED_ALPHA,
+                       GCREG_FACTOR_INVERSE_DISABLE,
+                       BVSRC1USE(true), BVSRC2USE(true)
+               }
+       },
+
+       /* #24: color factor: 01 10 00 MIN:(1-C2,C1) ==> not supported
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #25: color factor: 01 10 01 MIN:(1-C2,A1) ==> not supported
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #26: color factor: 01 10 10 MIN:(1-C2,C2) ==> not supported
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #27: color factor: 01 10 11 MIN:(1-C2,A2) ==> not supported
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #28: color factor: 01 11 00 MIN:(1-A2,C1) ==> not supported
+               alpha factor: N/A */
+       BVBLENDUNDEFINED(),
+
+       /* #29: color factor: 01 11 01 MIN:(1-A2,A1)
+               alpha factor: one ==> 11 11 11 */
+       {
+               0x3F,
+               0x3F,
+
+