summaryrefslogtreecommitdiffstats
path: root/bltsville/gcbv/mirror/gcbv.c
diff options
context:
space:
mode:
Diffstat (limited to 'bltsville/gcbv/mirror/gcbv.c')
-rw-r--r--bltsville/gcbv/mirror/gcbv.c2008
1 files changed, 2008 insertions, 0 deletions
diff --git a/bltsville/gcbv/mirror/gcbv.c b/bltsville/gcbv/mirror/gcbv.c
new file mode 100644
index 0000000..86d656e
--- /dev/null
+++ b/bltsville/gcbv/mirror/gcbv.c
@@ -0,0 +1,2008 @@
+/*
+ * Copyright(c) 2012,
+ * Texas Instruments, Inc. and 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)
+#define GCZONE_BUFFER (1 << 1)
+#define GCZONE_DEST (1 << 2)
+#define GCZONE_SRC (1 << 3)
+#define GCZONE_MASK (1 << 4)
+#define GCZONE_BATCH (1 << 5)
+#define GCZONE_BLIT (1 << 6)
+#define GCZONE_CACHE (1 << 7)
+#define GCZONE_CALLBACK (1 << 8)
+#define GCZONE_TEMP (1 << 9)
+#define GCZONE_BLEND (1 << 10)
+
+GCDBG_FILTERDEF(bv, GCZONE_NONE,
+ "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 gcmodst *gcmodst;
+
+ GCENTER(GCZONE_DEST);
+
+ /* Did destination surface change? */
+ if ((batch->batchflags & BVBATCH_DST) != 0) {
+ /* 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,
+ batch->dstbyteshift);
+
+ /* Set surface parameters. */
+ gcmodst->config_ldst = gcmodst_config_ldst;
+ gcmodst->address = GET_MAP_HANDLE(dstmap);
+ gcmodst->stride = bvbltparams->dstgeom->virtstride;
+
+ /* Set surface width and height. */
+ gcmodst->rotation.raw = 0;
+ gcmodst->rotation.reg.surf_width = batch->dstphyswidth;
+ gcmodst->rotationheight_ldst = gcmodst_rotationheight_ldst;
+ gcmodst->rotationheight.raw = 0;
+ gcmodst->rotationheight.reg.height = batch->dstphysheight;
+
+ /* 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 surfaceinfo *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
+ = gca->src_global_alpha_mode;
+ gcmoalpha->mode.reg.dst_global_alpha_mode
+ = gca->dst_global_alpha_mode;
+
+ 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 ((gca->src_global_alpha_mode
+ != GCREG_GLOBAL_ALPHA_MODE_NORMAL) ||
+ (gca->dst_global_alpha_mode
+ != GCREG_GLOBAL_ALPHA_MODE_NORMAL)) {
+ 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 = gca->src_global_color;
+ gcmoglobal->dstglobal.raw = gca->dst_global_color;
+ }
+ }
+
+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 surfaceinfo *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
+ = gca->src_global_alpha_mode;
+ gcmoxsrcalpha->mode.reg.dst_global_alpha_mode
+ = gca->dst_global_alpha_mode;
+
+ 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 ((gca->src_global_alpha_mode
+ != GCREG_GLOBAL_ALPHA_MODE_NORMAL) ||
+ (gca->dst_global_alpha_mode
+ != GCREG_GLOBAL_ALPHA_MODE_NORMAL)) {
+ 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 = gca->src_global_color;
+
+ gcmoxsrcglobal->dstglobal_ldst
+ = gcmoxsrcglobal_dstglobal_ldst[index];
+ gcmoxsrcglobal->dstglobal.raw = gca->dst_global_color;
+ }
+ }
+
+exit:
+ GCEXITARG(GCZONE_BLEND, "bv%s = %d\n",
+ (bverror == BVERR_NONE) ? "result" : "error", bverror);
+ return bverror;
+}
+
+/*******************************************************************************
+ * Program YUV source.
+ */
+
+void set_computeyuv(struct surfaceinfo *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->geom->height
+ : srcinfo->geom->width;
+
+ /* Determine the size of the first plane. */
+ size1 = srcinfo->geom->virtstride * height1;
+
+ /* Determine the stride of the second plane. */
+ srcinfo->stride2 = srcinfo->geom->virtstride
+ / 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 surfaceinfo *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 surfaceinfo *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;
+}
+
+
+/*******************************************************************************
+ * Library constructor and destructor.
+ */
+
+void bv_init(void)
+{
+ struct gccontext *gccontext = get_context();
+ struct gcicaps gcicaps;
+ unsigned i, j;
+
+ GCDBG_REGISTER(bv);
+ GCDBG_REGISTER(parser);
+ GCDBG_REGISTER(map);
+ GCDBG_REGISTER(buffer);
+ GCDBG_REGISTER(fill);
+ GCDBG_REGISTER(blit);
+ GCDBG_REGISTER(filter);
+
+ 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->gcfeatures = gcicaps.gcfeatures;
+ gccontext->gcfeatures0 = gcicaps.gcfeatures0;
+ gccontext->gcfeatures1 = gcicaps.gcfeatures1;
+ gccontext->gcfeatures2 = gcicaps.gcfeatures2;
+ gccontext->gcfeatures3 = gcicaps.gcfeatures3;
+ }
+}
+
+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);
+}
+
+
+/*******************************************************************************
+ * 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 bvrect *dstrect;
+ int src1used, src2used, maskused;
+ struct surfaceinfo srcinfo[2];
+ struct bvrect *srcrect[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;
+ src1used = ((rop & 0xCCCC) >> 2)
+ ^ (rop & 0x3333);
+ src2used = ((rop & 0xF0F0) >> 4)
+ ^ (rop & 0x0F0F);
+ maskused = ((rop & 0xFF00) >> 8)
+ ^ (rop & 0x00FF);
+ break;
+
+ case (BVFLAG_BLEND >> BVFLAG_OP_SHIFT):
+ GCDBG(GCZONE_BLIT, "BVFLAG_BLEND\n");
+
+ blend = bvbltparams->op.blend;
+ format = (blend & BVBLENDDEF_FORMAT_MASK)
+ >> BVBLENDDEF_FORMAT_SHIFT;
+
+ bverror = parse_blend(bvbltparams, blend, &_gca);
+ if (bverror != BVERR_NONE)
+ goto exit;
+
+ gca = &_gca;
+
+ switch (format) {
+ case (BVBLENDDEF_FORMAT_CLASSIC
+ >> BVBLENDDEF_FORMAT_SHIFT):
+ src1used = gca->src1used;
+ src2used = gca->src2used;
+ maskused = blend & BVBLENDDEF_REMOTE;
+ break;
+
+ default:
+ BVSETBLTERROR(BVERR_BLEND,
+ "unrecognized blend format");
+ goto exit;
+ }
+ 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;
+
+ /* 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, " same as destination\n");
+ } else {
+ srcinfo[srccount].index = 0;
+ srcinfo[srccount].buf = bvbltparams->src1;
+ srcinfo[srccount].geom = bvbltparams->src1geom;
+ srcinfo[srccount].newgeom
+ = gcbatch->batchflags
+ & BVBATCH_SRC1;
+ srcinfo[srccount].newrect
+ = gcbatch->batchflags
+ & (BVBATCH_SRC1RECT_ORIGIN |
+ BVBATCH_SRC1RECT_SIZE);
+ srcrect[srccount] = &bvbltparams->src1rect;
+
+ bverror = parse_source(bvbltparams, gcbatch,
+ &bvbltparams->src1rect,
+ &srcinfo[srccount]);
+ 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, " same as destination\n");
+ } else {
+ srcinfo[srccount].index = 1;
+ srcinfo[srccount].buf = bvbltparams->src2;
+ srcinfo[srccount].geom = bvbltparams->src2geom;
+ srcinfo[srccount].newgeom
+ = gcbatch->batchflags
+ & BVBATCH_SRC2;
+ srcinfo[srccount].newrect
+ = gcbatch->batchflags
+ & (BVBATCH_SRC2RECT_ORIGIN |
+ BVBATCH_SRC2RECT_SIZE);
+ srcrect[srccount] = &bvbltparams->src2rect;
+
+ bverror = parse_source(bvbltparams, gcbatch,
+ &bvbltparams->src2rect,
+ &srcinfo[srccount]);
+ 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;
+ }
+
+ GCDBG(GCZONE_BLIT, "srccount = %d\n", srccount);
+
+ if (srccount == 0) {
+ BVSETBLTERROR(BVERR_OP,
+ "operation not supported");
+ goto exit;
+ } else {
+ for (i = 0; i < srccount; i += 1) {
+ int srcw, srch;
+ GCDBG(GCZONE_BLIT,
+ "processing source %d.\n",
+ srcinfo[i].index + 1);
+
+ if (gca == NULL) {
+ GCDBG(GCZONE_BLIT,
+ " blending disabled.\n");
+ srcinfo[i].rop = bvbltparams->op.rop;
+ srcinfo[i].gca = NULL;
+ } else if ((i + 1) != srccount) {
+ GCDBG(GCZONE_BLIT,
+ " disabling blending for "
+ "the first source.\n");
+ srcinfo[i].rop = 0xCC;
+ srcinfo[i].gca = NULL;
+ } else {
+ GCDBG(GCZONE_BLIT,
+ " enabling blending.\n");
+ srcinfo[i].rop = 0xCC;
+ srcinfo[i].gca = gca;
+
+ if (srccount == 1) {
+ gca->srcconfig = gca->k1;
+ gca->dstconfig = gca->k2;
+ } else {
+ gca->srcconfig = gca->k2;
+ gca->dstconfig = gca->k1;
+ }
+ }
+
+ GCDBG(GCZONE_BLIT, " srcsize %dx%d.\n",
+ srcrect[i]->width, srcrect[i]->height);
+ GCDBG(GCZONE_BLIT, " dstsize %dx%d.\n",
+ dstrect->width, dstrect->height);
+
+ srcw = srcrect[i]->width;
+ srch = srcrect[i]->height;
+ if ((srcw == 1) && (srch == 1) &&
+ (bvbltparams->src1.desc->virtaddr)) {
+ GCDBG(GCZONE_BLIT, " op: fill.\n");
+ bverror = do_fill(bvbltparams,
+ gcbatch,
+ &srcinfo[i]);
+ } else if ((srcw == dstrect->width) &&
+ (srch == dstrect->height)) {
+ GCDBG(GCZONE_BLIT, " op: bitblit.\n");
+ bverror = do_blit(bvbltparams,
+ gcbatch,
+ &srcinfo[i]);
+ } else {
+ GCDBG(GCZONE_BLIT, " op: filter.\n");
+ bverror = do_filter(bvbltparams,
+ gcbatch,
+ &srcinfo[i]);
+ }
+
+ 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;
+}