summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk@arm.linux.org.uk>2014-07-22 01:15:30 +0100
committerRussell King <rmk@arm.linux.org.uk>2015-05-22 15:55:16 +0100
commit2a3cd6a1d37e467934a8b1c9b59890e52db84e75 (patch)
tree18e2d4b6f5c9914c0e5b6d9c26866a241837021e
parent93722c152c4d7bb4a00b57b8ac37021dfe46fde5 (diff)
etnaviv: add initial support
Add initial etnaviv support. This is a copy of the vivante code, adapted to the etnaviv library, and is at feature parity with the vivante code. Signed-off-by: Russell King <rmk@arm.linux.org.uk>
-rw-r--r--Makefile.am3
-rw-r--r--README16
-rw-r--r--common/utils.h2
-rw-r--r--configure.ac55
-rw-r--r--etnaviv/Makefile.am39
-rw-r--r--etnaviv/etnaviv.c887
-rw-r--r--etnaviv/etnaviv_accel.c1511
-rw-r--r--etnaviv/etnaviv_accel.h273
-rw-r--r--etnaviv/etnaviv_compat.h22
-rw-r--r--etnaviv/etnaviv_dri2.c375
-rw-r--r--etnaviv/etnaviv_dri2.h15
-rw-r--r--etnaviv/etnaviv_emit.c23
-rw-r--r--etnaviv/etnaviv_module.c74
-rw-r--r--etnaviv/etnaviv_op.c192
-rw-r--r--etnaviv/etnaviv_op.h76
-rw-r--r--etnaviv/etnaviv_utils.c395
-rw-r--r--etnaviv/etnaviv_utils.h43
-rw-r--r--src/armada_drm.c3
18 files changed, 4003 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index 74a3fc1..9aabdf3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,6 +21,9 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = common man src
+if HAVE_ACCEL_ETNAVIV
+SUBDIRS += etnaviv
+endif
if HAVE_ACCEL_GALCORE
SUBDIRS += vivante
endif
diff --git a/README b/README
index b28c035..8a2bb03 100644
--- a/README
+++ b/README
@@ -12,20 +12,32 @@ System as implemented by X.org, supporting these DRM KMS drivers:
and GPUs:
Vivnate libGAL (Armada only)
+ Etnaviv (Armada and i.MX) with galcore kernel driver
Build requirements
------------------
xf86-video-armada can be built as a stand-alone KMS driver, or with
-Vivante GPU support.
+Vivante and/or Etnaviv GPU support.
When building with Vivante libGAL support, the configure options
--with-libgal-include= and --with-libgal-lib= should indicate the
location of the libGAL headers and library respectively.
+When building Etnaviv support, the configure options
+--with-etnaviv-include= and --with-etnaviv-lib= should indicate the
+location of the etnaviv headers and library respectively.
+Alternatively, --with-etnaviv-source= can be used to indicate the
+location of the built Etnaviv tree.
+
The following packages are required by this driver:
- libdrm-armada git://ftp.arm.linux.org.uk/~rmk/libdrm-armada.git/
+The following packages are optional, but may be required for certain
+features:
+
+- etnaviv https://github.com/laanwj/etna_viv.git
+
What operations are accelerated?
--------------------------------
- Copies using all alu operations.
@@ -34,3 +46,5 @@ What operations are accelerated?
- Solid fills without stipples or partial plane mask.
- FillSpans without stipples or partial plane mask.
- Xrender compositing without component alpha or alpha maps.
+- Xrender glyph caching and assembling of glyphs (with or without
+ component alpha.) - etnaviv only.
diff --git a/common/utils.h b/common/utils.h
index 1913322..219ff75 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -28,4 +28,6 @@
#define maxt(x,y) __max(typeof(x), typeof(y), x, y)
#define max_t(t,x,y) __max(t, t, x, y)
+#define ALIGN(v,a) (((v) + ((a) - 1)) & ~((a) - 1))
+
#endif
diff --git a/configure.ac b/configure.ac
index 12e85ae..edb7528 100644
--- a/configure.ac
+++ b/configure.ac
@@ -118,6 +118,58 @@ AM_CONDITIONAL(HAVE_ACCEL_GALCORE, test x$ACCEL_GALCORE = xyes)
AC_MSG_RESULT([$ACCEL_GALCORE])
+# Etnaviv GPU acceleration
+AC_ARG_ENABLE(etnaviv,
+ AC_HELP_STRING([--disable-etnaviv],
+ [Disable Etnaviv acceleration support [[default=enable]]]),
+ [ACCEL_ETNAVIV="$enableval"],
+ [ACCEL_ETNAVIV=auto])
+
+AC_ARG_WITH(etnaviv-source,
+ AC_HELP_STRING([--with-etnaviv-source=PATH],
+ [specify directory for etnaviv source tree [[default=unset]]]),
+ [etnaviv_source="$withval"])
+
+AC_ARG_WITH(etnaviv-include,
+ AC_HELP_STRING([--with-etnaviv-include=PATH],
+ [specify directory for installed etnaviv include files [[default=/usr/include]]]),
+ [etnaviv_include="$withval"],
+ [etnaviv_include="/usr/include"])
+
+AC_ARG_WITH(etnaviv-lib,
+ AC_HELP_STRING([--with-etnaviv-lib=PATH],
+ [specify directory for installed etnaviv library [[default=/usr/lib]]]),
+ [etnaviv_lib="$withval"],
+ [etnaviv_lib="/usr/lib"])
+
+AS_IF([test x$ACCEL_ETNAVIV != xno],
+ [
+ AS_IF([test x$etnaviv_source != x],
+ [
+ ETNAVIV_CFLAGS="-I$etnaviv_source/src"
+ ETNAVIV_LIBS="-L$etnaviv_source/src/etnaviv"
+ ],
+ [
+ ETNAVIV_CFLAGS="-I$etnaviv_include"
+ ETNAVIV_LIBS="-L$etnaviv_lib"
+ ])
+ saved_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $ETNAVIV_LIBS"
+ AC_CHECK_LIB([etnaviv], [viv_open],
+ [ETNAVIV_LIBS+=" -letnaviv" ACCEL_ETNAVIV=yes],
+ [ACCEL_ETNAVIV=no])
+ AC_CHECK_LIB([etnaviv], [etna_bo_from_dmabuf],
+ [DUMMY=y],
+ [AC_MSG_ERROR([etnaviv support requires etna_bo_from_dmabuf()])])
+ LDFLAGS="$saved_LDFLAGS"
+])
+AC_MSG_CHECKING([whether to build Etnaviv acceleration support])
+AS_IF([test x$ACCEL_ETNAVIV = xyes],
+ [AC_DEFINE(HAVE_ACCEL_ETNAVIV,1,[Enable Etnaviv acceleration support])])
+AM_CONDITIONAL(HAVE_ACCEL_ETNAVIV, test x$ACCEL_ETNAVIV = xyes)
+AC_MSG_RESULT([$ACCEL_ETNAVIV])
+
+
AC_ARG_ENABLE(dri2, AC_HELP_STRING([--disable-dri2],
[Disable DRI support [[default=auto]]]),
[DRI2="$enableval"],
@@ -158,11 +210,14 @@ AC_SUBST([XORG_CFLAGS])
AC_SUBST([LIBGAL_CFLAGS])
AC_SUBST([LIBGAL_LIBS])
AC_SUBST([DRIVER_NAME])
+AC_SUBST([ETNAVIV_CFLAGS])
+AC_SUBST([ETNAVIV_LIBS])
AC_SUBST([moduledir])
AC_OUTPUT([
Makefile
common/Makefile
+ etnaviv/Makefile
man/Makefile
src/Makefile
vivante/Makefile
diff --git a/etnaviv/Makefile.am b/etnaviv/Makefile.am
new file mode 100644
index 0000000..70730de
--- /dev/null
+++ b/etnaviv/Makefile.am
@@ -0,0 +1,39 @@
+#
+# Marvell Armada DRM-based driver
+#
+# Written by Russell King, 2012, derived in part from the
+# Intel xorg X server driver.
+#
+
+# Turn off -Wnested-externs - these are a good thing because it allows
+# information hiding and helps prevent misuse of private externs.
+# Turn off -Wcast-qual - this makes stuff like string assignment
+# too noisy.
+# Turn off -Wredundant-decls - Xorg headers seem to contain a lot
+# of this, so why it's in xorg-macros.m4... maybe more of a wish?
+# Turn off -Wshadow - Xorg headers seem to declare a lot of globals
+# which can conflict - index, range, etc.
+AM_CFLAGS = $(filter-out -Wnested-externs -Wcast-qual -Wredundant-decls \
+ -Werror=write-strings -Wshadow,$(CWARNFLAGS)) \
+ $(XORG_CFLAGS) $(ETNAVIV_CFLAGS) $(DRMARMADA_CFLAGS) $(UDEV_CFLAGS) \
+ -I$(top_srcdir)/src -I$(top_srcdir)/common
+
+etnaviv_gpu_la_LTLIBRARIES = etnaviv_gpu.la
+etnaviv_gpu_la_LDFLAGS = -module -avoid-version
+etnaviv_gpu_la_LIBADD = \
+ $(ETNAVIV_LIBS) \
+ $(DRMARMADA_LIBS) \
+ $(top_builddir)/common/libcommon.la
+etnaviv_gpu_ladir = @moduledir@/drivers
+etnaviv_gpu_la_SOURCES = \
+ etnaviv.c \
+ etnaviv_accel.c \
+ etnaviv_emit.c \
+ etnaviv_module.c \
+ etnaviv_op.c \
+ etnaviv_utils.c
+
+if HAVE_DRI2
+etnaviv_gpu_la_SOURCES += etnaviv_dri2.c
+etnaviv_gpu_la_LIBADD += $(DRI_LIBS)
+endif
diff --git a/etnaviv/etnaviv.c b/etnaviv/etnaviv.c
new file mode 100644
index 0000000..feac438
--- /dev/null
+++ b/etnaviv/etnaviv.c
@@ -0,0 +1,887 @@
+/*
+ * Vivante GPU Acceleration Xorg driver
+ *
+ * Written by Russell King, 2012, derived in part from the
+ * Intel xorg X server driver.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_DIX_CONFIG_H
+#include "dix-config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <armada_bufmgr.h>
+
+#include "armada_accel.h"
+#include "common_drm_dri2.h"
+
+#include "fb.h"
+#include "gcstruct.h"
+#include "xf86.h"
+#include "compat-api.h"
+
+#include <etnaviv/etna.h>
+#include <etnaviv/state_2d.xml.h>
+
+#include "cpu_access.h"
+#include "fbutil.h"
+#include "gal_extension.h"
+#include "mark.h"
+#include "pixmaputil.h"
+#include "unaccel.h"
+
+#include "etnaviv_accel.h"
+#include "etnaviv_dri2.h"
+#include "etnaviv_utils.h"
+
+etnaviv_Key etnaviv_pixmap_index;
+etnaviv_Key etnaviv_screen_index;
+int etnaviv_private_index = -1;
+
+enum {
+ OPTION_DRI,
+};
+
+const OptionInfoRec etnaviv_options[] = {
+ { OPTION_DRI, "DRI", OPTV_BOOLEAN, {0}, TRUE },
+ { -1, NULL, OPTV_NONE, {0}, FALSE }
+};
+
+static void etnaviv_free_pixmap(PixmapPtr pixmap)
+{
+ struct etnaviv_pixmap *vPix = etnaviv_get_pixmap_priv(pixmap);
+
+ if (vPix) {
+ struct etnaviv *etnaviv;
+
+ etnaviv = etnaviv_get_screen_priv(pixmap->drawable.pScreen);
+ etnaviv_batch_wait_commit(etnaviv, vPix);
+
+ if (vPix->etna_bo) {
+ struct etna_bo *etna_bo = vPix->etna_bo;
+
+ if (!vPix->bo && vPix->owner == CPU)
+ etna_bo_cpu_fini(etna_bo);
+ etna_bo_del(etnaviv->conn, etna_bo, NULL);
+ }
+ if (vPix->bo)
+ drm_armada_bo_put(vPix->bo);
+ free(vPix);
+ }
+}
+
+/*
+ * We are about to respond to a client. Ensure that all pending rendering
+ * is flushed to the GPU prior to the response being delivered.
+ */
+static void etnaviv_flush_callback(CallbackListPtr *list, pointer user_data,
+ pointer call_data)
+{
+ ScrnInfoPtr pScrn = user_data;
+ struct etnaviv *etnaviv = pScrn->privates[etnaviv_private_index].ptr;
+
+ if (pScrn->vtSema && etnaviv->need_commit)
+ etnaviv_commit(etnaviv, FALSE);
+}
+
+static struct etnaviv_pixmap *etnaviv_alloc_pixmap(PixmapPtr pixmap,
+ struct etnaviv_format fmt)
+{
+ struct etnaviv_pixmap *vpix;
+
+ vpix = calloc(1, sizeof *vpix);
+ if (vpix) {
+ vpix->width = pixmap->drawable.width;
+ vpix->height = pixmap->drawable.height;
+ vpix->pitch = pixmap->devKind;
+ vpix->format = fmt;
+ xorg_list_init(&vpix->pending_node);
+ }
+ return vpix;
+}
+
+
+/* Determine whether this GC and target Drawable can be accelerated */
+static Bool etnaviv_GC_can_accel(GCPtr pGC, DrawablePtr pDrawable)
+{
+ PixmapPtr pixmap = drawable_pixmap(pDrawable);
+ if (!etnaviv_get_pixmap_priv(pixmap))
+ return FALSE;
+
+ /* Must be full-planes */
+ return !pGC || fb_full_planemask(pDrawable, pGC->planemask);
+}
+
+static Bool etnaviv_GCfill_can_accel(GCPtr pGC, DrawablePtr pDrawable)
+{
+ switch (pGC->fillStyle) {
+ case FillSolid:
+ return TRUE;
+
+ case FillTiled:
+ /* Single pixel tiles are just solid colours */
+ if (pGC->tileIsPixel)
+ return TRUE;
+
+ /* If the tile pixmap is a single pixel, it's also a solid fill */
+ if (pGC->tile.pixmap->drawable.width == 1 &&
+ pGC->tile.pixmap->drawable.height == 1)
+ return TRUE;
+
+ /* In theory, we could do !tileIsPixel as well, which means
+ * copying the tile (possibly) multiple times to the drawable.
+ * This is something we should do, especially if the size of
+ * the tile matches the size of the drawable and the tile
+ * offsets are zero (iow, it's a plain copy.)
+ */
+ return FALSE;
+
+ default:
+ return FALSE;
+ }
+}
+
+
+static void
+etnaviv_FillSpans(DrawablePtr pDrawable, GCPtr pGC, int n, DDXPointPtr ppt,
+ int *pwidth, int fSorted)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDrawable->pScreen);
+
+ assert(etnaviv_GC_can_accel(pGC, pDrawable));
+
+ if (etnaviv->force_fallback ||
+ !etnaviv_GCfill_can_accel(pGC, pDrawable) ||
+ !etnaviv_accel_FillSpans(pDrawable, pGC, n, ppt, pwidth, fSorted))
+ unaccel_FillSpans(pDrawable, pGC, n, ppt, pwidth, fSorted);
+}
+
+static void
+etnaviv_PutImage(DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y,
+ int w, int h, int leftPad, int format, char *bits)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDrawable->pScreen);
+
+ assert(etnaviv_GC_can_accel(pGC, pDrawable));
+
+ if (etnaviv->force_fallback ||
+ !etnaviv_accel_PutImage(pDrawable, pGC, depth, x, y, w, h, leftPad,
+ format, bits))
+ unaccel_PutImage(pDrawable, pGC, depth, x, y, w, h, leftPad,
+ format, bits);
+}
+
+static RegionPtr
+etnaviv_CopyArea(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC,
+ int srcx, int srcy, int w, int h, int dstx, int dsty)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDst->pScreen);
+
+ assert(etnaviv_GC_can_accel(pGC, pDst));
+
+ if (etnaviv->force_fallback)
+ return unaccel_CopyArea(pSrc, pDst, pGC, srcx, srcy, w, h,
+ dstx, dsty);
+
+ return miDoCopy(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty,
+ etnaviv_accel_CopyNtoN, 0, NULL);
+}
+
+static void
+etnaviv_PolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt,
+ DDXPointPtr ppt)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDrawable->pScreen);
+
+ assert(etnaviv_GC_can_accel(pGC, pDrawable));
+
+ if (etnaviv->force_fallback ||
+ !etnaviv_GCfill_can_accel(pGC, pDrawable) ||
+ !etnaviv_accel_PolyPoint(pDrawable, pGC, mode, npt, ppt))
+ unaccel_PolyPoint(pDrawable, pGC, mode, npt, ppt);
+}
+
+static void
+etnaviv_PolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrect,
+ xRectangle * prect)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDrawable->pScreen);
+ PixmapPtr pPix = drawable_pixmap(pDrawable);
+
+ if (etnaviv->force_fallback ||
+ (pPix->drawable.width == 1 && pPix->drawable.height == 1))
+ goto fallback;
+
+ assert(etnaviv_GC_can_accel(pGC, pDrawable));
+
+ if (etnaviv_GCfill_can_accel(pGC, pDrawable)) {
+ if (etnaviv_accel_PolyFillRectSolid(pDrawable, pGC, nrect, prect))
+ return;
+ } else if (pGC->fillStyle == FillTiled) {
+ if (etnaviv_accel_PolyFillRectTiled(pDrawable, pGC, nrect, prect))
+ return;
+ }
+
+ fallback:
+ unaccel_PolyFillRect(pDrawable, pGC, nrect, prect);
+}
+
+static GCOps etnaviv_GCOps = {
+ etnaviv_FillSpans,
+ unaccel_SetSpans,
+ etnaviv_PutImage,
+ etnaviv_CopyArea,
+ unaccel_CopyPlane,
+ etnaviv_PolyPoint,
+ unaccel_PolyLines,
+ unaccel_PolySegment,
+ miPolyRectangle,
+ miPolyArc,
+ miFillPolygon,
+ etnaviv_PolyFillRect,
+ miPolyFillArc,
+ miPolyText8,
+ miPolyText16,
+ miImageText8,
+ miImageText16,
+ unaccel_ImageGlyphBlt,
+ unaccel_PolyGlyphBlt,
+ unaccel_PushPixels
+};
+
+static GCOps etnaviv_unaccel_GCOps = {
+ unaccel_FillSpans,
+ unaccel_SetSpans,
+ unaccel_PutImage,
+ unaccel_CopyArea,
+ unaccel_CopyPlane,
+ unaccel_PolyPoint,
+ unaccel_PolyLines,
+ unaccel_PolySegment,
+ miPolyRectangle,
+ miPolyArc,
+ miFillPolygon,
+ unaccel_PolyFillRect,
+ miPolyFillArc,
+ miPolyText8,
+ miPolyText16,
+ miImageText8,
+ miImageText16,
+ unaccel_ImageGlyphBlt,
+ unaccel_PolyGlyphBlt,
+ unaccel_PushPixels
+};
+
+static void
+etnaviv_ValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDrawable->pScreen);
+
+#ifdef FB_24_32BIT
+ if (changes & GCTile && fbGetRotatedPixmap(pGC)) {
+ pGC->pScreen->DestroyPixmap(fbGetRotatedPixmap(pGC));
+ fbGetRotatedPixmap(pGC) = NULL;
+ }
+ if (pGC->fillStyle == FillTiled) {
+ PixmapPtr pOldTile = pGC->tile.pixmap;
+ PixmapPtr pNewTile;
+
+ if (pOldTile->drawable.bitsPerPixel != pDrawable->bitsPerPixel) {
+ pNewTile = fbGetRotatedPixmap(pGC);
+ if (!pNewTile || pNewTile->drawable.bitsPerPixel != pDrawable->bitsPerPixel) {
+ if (pNewTile)
+ pGC->pScreen->DestroyPixmap(pNewTile);
+ prepare_cpu_drawable(&pOldTile->drawable, CPU_ACCESS_RO);
+ pNewTile = fb24_32ReformatTile(pOldTile, pDrawable->bitsPerPixel);
+ finish_cpu_drawable(&pOldTile->drawable, CPU_ACCESS_RO);
+ }
+ if (pNewTile) {
+ fbGetRotatedPixmap(pGC) = pOldTile;
+ pGC->tile.pixmap = pNewTile;
+ changes |= GCTile;
+ }
+ }
+ }
+#endif
+ if (changes & GCTile) {
+ if (!pGC->tileIsPixel &&
+ FbEvenTile(pGC->tile.pixmap->drawable.width *
+ pDrawable->bitsPerPixel)) {
+ prepare_cpu_drawable(&pGC->tile.pixmap->drawable, CPU_ACCESS_RW);
+ fbPadPixmap(pGC->tile.pixmap);
+ finish_cpu_drawable(&pGC->tile.pixmap->drawable, CPU_ACCESS_RW);
+ }
+ /* mask out gctile changes now that we've done the work */
+ changes &= ~GCTile;
+ }
+ if (changes & GCStipple && pGC->stipple) {
+ prepare_cpu_drawable(&pGC->stipple->drawable, CPU_ACCESS_RW);
+ fbValidateGC(pGC, changes, pDrawable);
+ finish_cpu_drawable(&pGC->stipple->drawable, CPU_ACCESS_RW);
+ } else {
+ fbValidateGC(pGC, changes, pDrawable);
+ }
+
+ /*
+ * Select the GC ops depending on whether we have any
+ * chance to accelerate with this GC.
+ */
+ if (!etnaviv->force_fallback && etnaviv_GC_can_accel(pGC, pDrawable))
+ pGC->ops = &etnaviv_GCOps;
+ else
+ pGC->ops = &etnaviv_unaccel_GCOps;
+}
+
+static GCFuncs etnaviv_GCFuncs = {
+ etnaviv_ValidateGC,
+ miChangeGC,
+ miCopyGC,
+ miDestroyGC,
+ miChangeClip,
+ miDestroyClip,
+ miCopyClip
+};
+
+
+static Bool etnaviv_CloseScreen(CLOSE_SCREEN_ARGS_DECL)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pScreen);
+#ifdef RENDER
+ PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);
+#endif
+ PixmapPtr pixmap;
+
+ DeleteCallback(&FlushCallback, etnaviv_flush_callback, pScrn);
+
+#ifdef RENDER
+ /* Restore the Pointers */
+ ps->Composite = etnaviv->Composite;
+ ps->Glyphs = etnaviv->Glyphs;
+ ps->UnrealizeGlyph = etnaviv->UnrealizeGlyph;
+ ps->Triangles = etnaviv->Triangles;
+ ps->Trapezoids = etnaviv->Trapezoids;
+ ps->AddTriangles = etnaviv->AddTriangles;
+ ps->AddTraps = etnaviv->AddTraps;
+#endif
+
+ pScreen->CloseScreen = etnaviv->CloseScreen;
+ pScreen->GetImage = etnaviv->GetImage;
+ pScreen->GetSpans = etnaviv->GetSpans;
+ pScreen->ChangeWindowAttributes = etnaviv->ChangeWindowAttributes;
+ pScreen->CopyWindow = etnaviv->CopyWindow;
+ pScreen->CreatePixmap = etnaviv->CreatePixmap;
+ pScreen->DestroyPixmap = etnaviv->DestroyPixmap;
+ pScreen->CreateGC = etnaviv->CreateGC;
+ pScreen->BitmapToRegion = etnaviv->BitmapToRegion;
+ pScreen->BlockHandler = etnaviv->BlockHandler;
+
+#ifdef HAVE_DRI2
+ etnaviv_dri2_CloseScreen(CLOSE_SCREEN_ARGS);
+#endif
+
+ pixmap = pScreen->GetScreenPixmap(pScreen);
+ etnaviv_free_pixmap(pixmap);
+ etnaviv_set_pixmap_priv(pixmap, NULL);
+
+ etnaviv_accel_shutdown(etnaviv);
+
+ return pScreen->CloseScreen(CLOSE_SCREEN_ARGS);
+}
+
+static void
+etnaviv_CopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc)
+{
+ PixmapPtr pPixmap = pWin->drawable.pScreen->GetWindowPixmap(pWin);
+ RegionRec rgnDst;
+ int dx, dy;
+
+ dx = ptOldOrg.x - pWin->drawable.x;
+ dy = ptOldOrg.y - pWin->drawable.y;
+ RegionTranslate(prgnSrc, -dx, -dy);
+ RegionInit(&rgnDst, NullBox, 0);
+ RegionIntersect(&rgnDst, &pWin->borderClip, prgnSrc);
+
+#ifdef COMPOSITE
+ if (pPixmap->screen_x || pPixmap->screen_y)
+ RegionTranslate(&rgnDst, -pPixmap->screen_x,
+ -pPixmap->screen_y);
+#endif
+
+ miCopyRegion(&pPixmap->drawable, &pPixmap->drawable, NULL,
+ &rgnDst, dx, dy, etnaviv_accel_CopyNtoN, 0, NULL);
+
+ RegionUninit(&rgnDst);
+}
+
+#ifdef HAVE_DRI2
+Bool etnaviv_pixmap_flink(PixmapPtr pixmap, uint32_t *name)
+{
+ struct etnaviv_pixmap *vpix = etnaviv_get_pixmap_priv(pixmap);
+ Bool ret = FALSE;
+
+ if (!vpix)
+ return FALSE;
+
+ if (vpix->name) {
+ *name = vpix->name;
+ ret = TRUE;
+ } else if (vpix->bo && !drm_armada_bo_flink(vpix->bo, name)) {
+ ret = TRUE;
+ }
+
+ return ret;
+}
+#endif
+
+static Bool etnaviv_alloc_armada_bo(ScreenPtr pScreen, struct etnaviv *etnaviv,
+ PixmapPtr pixmap, int w, int h, struct etnaviv_format fmt,
+ unsigned usage_hint)
+{
+ struct etnaviv_pixmap *vpix;
+ struct drm_armada_bo *bo;
+
+ bo = drm_armada_bo_create(etnaviv->bufmgr, w, h,
+ pixmap->drawable.bitsPerPixel);
+ if (!bo) {
+ xf86DrvMsg(etnaviv->scrnIndex, X_ERROR,
+ "etnaviv: failed to allocate armada bo for %dx%d %dbpp\n",
+ w, h, pixmap->drawable.bitsPerPixel);
+ return FALSE;
+ }
+
+ if (drm_armada_bo_map(bo))
+ goto free_bo;
+
+ /*
+ * Do not store our data pointer in the pixmap - only do so (via
+ * prepare_cpu_drawable()) when required to directly access the
+ * pixmap. This provides us a way to validate that we do not have
+ * any spurious unchecked accesses to the pixmap data while the GPU
+ * has ownership of the pixmap.
+ */
+ pScreen->ModifyPixmapHeader(pixmap, w, h, 0, 0, bo->pitch, NULL);
+
+ vpix = etnaviv_alloc_pixmap(pixmap, fmt);
+ if (!vpix)
+ goto free_bo;
+
+ vpix->bo = bo;
+
+ etnaviv_set_pixmap_priv(pixmap, vpix);
+
+#ifdef DEBUG_PIXMAP
+ dbg("Pixmap %p: vPix=%p armada_bo=%p format=%u/%u/%u\n",
+ pixmap, vPix, bo, fmt.format, fmt.swizzle, fmt.tile);
+#endif
+
+ return TRUE;
+
+ free_bo:
+ drm_armada_bo_put(bo);
+ return FALSE;
+}
+
+static Bool etnaviv_alloc_etna_bo(ScreenPtr pScreen, struct etnaviv *etnaviv,
+ PixmapPtr pixmap, int w, int h, struct etnaviv_format fmt,
+ unsigned usage_hint)
+{
+ struct etnaviv_pixmap *vpix;
+ struct etna_bo *etna_bo;
+ unsigned pitch, bpp = pixmap->drawable.bitsPerPixel;
+
+ pitch = etnaviv_pitch(w, bpp);
+
+ etna_bo = etna_bo_new(etnaviv->conn, pitch * h,
+ DRM_ETNA_GEM_TYPE_BMP | DRM_ETNA_GEM_CACHE_WBACK);
+ if (!etna_bo) {
+ xf86DrvMsg(etnaviv->scrnIndex, X_ERROR,
+ "etnaviv: failed to allocate bo for %dx%d %dbpp\n",
+ w, h, bpp);
+ return FALSE;
+ }
+
+ /*
+ * Do not store our data pointer in the pixmap - only do so (via
+ * prepare_cpu_drawable()) when required to directly access the
+ * pixmap. This provides us a way to validate that we do not have
+ * any spurious unchecked accesses to the pixmap data while the GPU
+ * has ownership of the pixmap.
+ */
+ pScreen->ModifyPixmapHeader(pixmap, w, h, 0, 0, pitch, NULL);
+
+ vpix = etnaviv_alloc_pixmap(pixmap, fmt);
+ if (!vpix)
+ goto free_bo;
+
+ vpix->etna_bo = etna_bo;
+
+ etnaviv_set_pixmap_priv(pixmap, vpix);
+
+#ifdef DEBUG_PIXMAP
+ dbg("Pixmap %p: vPix=%p etna_bo=%p format=%u/%u/%u\n",
+ pixmap, vPix, etna_bo, fmt.format, fmt.swizzle, fmt.tile);
+#endif
+
+ return TRUE;
+
+ free_bo:
+ etna_bo_del(etnaviv->conn, etna_bo, NULL);
+ return FALSE;
+}
+
+static PixmapPtr etnaviv_CreatePixmap(ScreenPtr pScreen, int w, int h,
+ int depth, unsigned usage_hint)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pScreen);
+ struct etnaviv_format fmt = { .swizzle = DE_SWIZZLE_ARGB, };
+ PixmapPtr pixmap;
+
+ if (w > 32768 || h > 32768)
+ return NullPixmap;
+
+ if (depth == 1 || etnaviv->force_fallback)
+ goto fallback;
+
+ if (usage_hint == CREATE_PIXMAP_USAGE_GLYPH_PICTURE &&
+ w <= 32 && h <= 32)
+ goto fallback;
+
+ pixmap = etnaviv->CreatePixmap(pScreen, 0, 0, depth, usage_hint);
+ if (pixmap == NullPixmap || w == 0 || h == 0)
+ return pixmap;
+
+ /* Create the appropriate format for this pixmap */
+ switch (pixmap->drawable.bitsPerPixel) {
+ case 16:
+ if (pixmap->drawable.depth == 15)
+ fmt.format = DE_FORMAT_A1R5G5B5;
+ else
+ fmt.format = DE_FORMAT_R5G6B5;
+ break;
+
+ case 32:
+ fmt.format = DE_FORMAT_A8R8G8B8;
+ break;
+
+ default:
+ goto fallback_free_pix;
+ }
+
+ if (etnaviv->bufmgr) {
+ if (!etnaviv_alloc_armada_bo(pScreen, etnaviv, pixmap,
+ w, h, fmt, usage_hint))
+ goto fallback_free_pix;
+ } else {
+ if (!etnaviv_alloc_etna_bo(pScreen, etnaviv, pixmap,
+ w, h, fmt, usage_hint))
+ goto fallback_free_pix;
+ }
+ goto out;
+
+ fallback_free_pix:
+ etnaviv->DestroyPixmap(pixmap);
+ fallback:
+ pixmap = etnaviv->CreatePixmap(pScreen, w, h, depth, usage_hint);
+
+ out:
+#ifdef DEBUG_PIXMAP
+ dbg("Created pixmap %p %dx%d %d %d %x\n",
+ pixmap, w, h, depth, pixmap->drawable.bitsPerPixel, usage);
+#endif
+
+ return pixmap;
+}
+
+static Bool etnaviv_DestroyPixmap(PixmapPtr pixmap)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pixmap->drawable.pScreen);
+ if (pixmap->refcnt == 1) {
+#ifdef DEBUG_PIXMAP
+ dbg("Destroying pixmap %p\n", pixmap);
+#endif
+ etnaviv_free_pixmap(pixmap);
+ etnaviv_set_pixmap_priv(pixmap, NULL);
+ }
+ return etnaviv->DestroyPixmap(pixmap);
+}
+
+static Bool etnaviv_CreateGC(GCPtr pGC)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pGC->pScreen);
+ Bool ret;
+
+ ret = etnaviv->CreateGC(pGC);
+ if (ret)
+ pGC->funcs = &etnaviv_GCFuncs;
+
+ return ret;
+}
+
+/* Commit any pending GPU operations */
+static void
+etnaviv_BlockHandler(BLOCKHANDLER_ARGS_DECL)
+{
+ SCREEN_PTR(arg);
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pScreen);
+
+ if (etnaviv->need_commit)
+ etnaviv_commit(etnaviv, FALSE);
+
+ mark_flush();
+
+ pScreen->BlockHandler = etnaviv->BlockHandler;
+ pScreen->BlockHandler(BLOCKHANDLER_ARGS);
+ etnaviv->BlockHandler = pScreen->BlockHandler;
+ pScreen->BlockHandler = etnaviv_BlockHandler;
+}
+
+#ifdef RENDER
+static void
+etnaviv_Composite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst,
+ INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst,
+ CARD16 width, CARD16 height)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDst->pDrawable->pScreen);
+ Bool ret;
+
+ if (!etnaviv->force_fallback) {
+ unsigned src_repeat, mask_repeat;
+
+ src_repeat = pSrc->repeat;
+ if (pMask)
+ mask_repeat = pMask->repeat;
+
+ ret = etnaviv_accel_Composite(op, pSrc, pMask, pDst,
+ xSrc, ySrc,
+ xMask, yMask,
+ xDst, yDst, width, height);
+ pSrc->repeat = src_repeat;
+ if (pMask)
+ pMask->repeat = mask_repeat;
+
+ if (ret)
+ return;
+ }
+ unaccel_Composite(op, pSrc, pMask, pDst, xSrc, ySrc,
+ xMask, yMask, xDst, yDst, width, height);
+}
+#endif
+
+static Bool etnaviv_pre_init(ScrnInfoPtr pScrn, int drm_fd)
+{
+ struct etnaviv *etnaviv;
+ OptionInfoPtr options;
+
+ etnaviv = calloc(1, sizeof *etnaviv);
+ if (!etnaviv)
+ return FALSE;
+
+ options = malloc(sizeof(etnaviv_options));
+ if (!options) {
+ free(etnaviv);
+ return FALSE;
+ }
+
+ memcpy(options, etnaviv_options, sizeof(etnaviv_options));
+ xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, options);
+
+#ifdef HAVE_DRI2
+ etnaviv->dri2_enabled = xf86ReturnOptValBool(options, OPTION_DRI, TRUE);
+#endif
+
+ etnaviv->scrnIndex = pScrn->scrnIndex;
+
+ if (etnaviv_private_index == -1)
+ etnaviv_private_index = xf86AllocateScrnInfoPrivateIndex();
+
+ pScrn->privates[etnaviv_private_index].ptr = etnaviv;
+
+ free(options);
+
+ return TRUE;
+}
+
+static Bool etnaviv_ScreenInit(ScreenPtr pScreen, struct drm_armada_bufmgr *mgr)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+#ifdef RENDER
+ PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);
+#endif
+ struct etnaviv *etnaviv = pScrn->privates[etnaviv_private_index].ptr;
+
+ if (!etnaviv_CreateKey(&etnaviv_pixmap_index, PRIVATE_PIXMAP) ||
+ !etnaviv_CreateKey(&etnaviv_screen_index, PRIVATE_SCREEN))
+ return FALSE;
+
+ etnaviv->bufmgr = mgr;
+
+ if (!etnaviv_accel_init(etnaviv))
+ goto fail_accel;
+
+ xorg_list_init(&etnaviv->pending_list);
+
+ etnaviv_set_screen_priv(pScreen, etnaviv);
+
+ if (!AddCallback(&FlushCallback, etnaviv_flush_callback, pScrn)) {
+ etnaviv_accel_shutdown(etnaviv);
+ goto fail_accel;
+ }
+
+#ifdef HAVE_DRI2
+ if (!etnaviv->dri2_enabled) {
+ xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
+ "direct rendering: disabled\n");
+ } else {
+ int dri_fd = -1;
+
+ if (mgr) {
+ /* armada fd, armada buffer management */
+ dri_fd = GET_DRM_INFO(pScrn)->fd;
+ etnaviv->dri2_armada = TRUE;
+ }
+
+ if (dri_fd == -1) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "direct rendering: unusuable devices\n");
+ } else if (!etnaviv_dri2_ScreenInit(pScreen, dri_fd)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "direct rendering: failed\n");
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "direct rendering: DRI2 enabled\n");
+ }
+ }
+#endif
+
+ etnaviv->CloseScreen = pScreen->CloseScreen;
+ pScreen->CloseScreen = etnaviv_CloseScreen;
+ etnaviv->GetImage = pScreen->GetImage;
+ pScreen->GetImage = unaccel_GetImage;
+ etnaviv->GetSpans = pScreen->GetSpans;
+ pScreen->GetSpans = unaccel_GetSpans;
+ etnaviv->ChangeWindowAttributes = pScreen->ChangeWindowAttributes;
+ pScreen->ChangeWindowAttributes = unaccel_ChangeWindowAttributes;
+ etnaviv->CopyWindow = pScreen->CopyWindow;
+ pScreen->CopyWindow = etnaviv_CopyWindow;
+ etnaviv->CreatePixmap = pScreen->CreatePixmap;
+ pScreen->CreatePixmap = etnaviv_CreatePixmap;
+ etnaviv->DestroyPixmap = pScreen->DestroyPixmap;
+ pScreen->DestroyPixmap = etnaviv_DestroyPixmap;
+ etnaviv->CreateGC = pScreen->CreateGC;
+ pScreen->CreateGC = etnaviv_CreateGC;
+ etnaviv->BitmapToRegion = pScreen->BitmapToRegion;
+ pScreen->BitmapToRegion = unaccel_BitmapToRegion;
+ etnaviv->BlockHandler = pScreen->BlockHandler;
+ pScreen->BlockHandler = etnaviv_BlockHandler;
+
+#ifdef RENDER
+ etnaviv->Composite = ps->Composite;
+ ps->Composite = etnaviv_Composite;
+ etnaviv->Glyphs = ps->Glyphs;
+ ps->Glyphs = unaccel_Glyphs;
+ etnaviv->UnrealizeGlyph = ps->UnrealizeGlyph;
+ etnaviv->Triangles = ps->Triangles;
+ ps->Triangles = unaccel_Triangles;
+ etnaviv->Trapezoids = ps->Trapezoids;
+ ps->Trapezoids = unaccel_Trapezoids;
+ etnaviv->AddTriangles = ps->AddTriangles;
+ ps->AddTriangles = unaccel_AddTriangles;
+ etnaviv->AddTraps = ps->AddTraps;
+ ps->AddTraps = unaccel_AddTraps;
+#endif
+ return TRUE;
+
+fail_accel:
+ free(etnaviv);
+ return FALSE;
+}
+
+/* Scanout pixmaps are never tiled. */
+static Bool etnaviv_import_dmabuf(ScreenPtr pScreen, PixmapPtr pPixmap, int fd)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pScreen);
+ struct etnaviv_format fmt = { .swizzle = DE_SWIZZLE_ARGB, };
+ struct etnaviv_pixmap *vpix = etnaviv_get_pixmap_priv(pPixmap);
+ struct etna_bo *bo;
+
+ if (vpix) {
+ etnaviv_free_pixmap(pPixmap);
+ etnaviv_set_pixmap_priv(pPixmap, NULL);
+ }
+
+ switch (pPixmap->drawable.bitsPerPixel) {
+ case 16:
+ if (pPixmap->drawable.depth == 15)
+ fmt.format = DE_FORMAT_A1R5G5B5;
+ else
+ fmt.format = DE_FORMAT_R5G6B5;
+ break;
+
+ case 32:
+ fmt.format = DE_FORMAT_A8R8G8B8;
+ break;
+
+ default:
+ return TRUE;
+ }
+
+ bo = etna_bo_from_dmabuf(etnaviv->conn, fd, PROT_READ | PROT_WRITE);
+ if (!bo) {
+ xf86DrvMsg(etnaviv->scrnIndex, X_ERROR,
+ "etnaviv: gpu dmabuf map failed: %s\n",
+ strerror(errno));
+ return FALSE;
+ }
+
+ vpix = etnaviv_alloc_pixmap(pPixmap, fmt);
+ if (!vpix) {
+ etna_bo_del(etnaviv->conn, bo, NULL);
+ return FALSE;
+ }
+
+ vpix->etna_bo = bo;
+
+ /*
+ * Pixmaps imported via dmabuf are write-combining, so don't
+ * need CPU cache state tracking. We still need to track
+ * whether we have operations outstanding on the GPU.
+ */
+ vpix->state |= ST_DMABUF;
+
+ etnaviv_set_pixmap_priv(pPixmap, vpix);
+
+#ifdef DEBUG_PIXMAP
+ dbg("Pixmap %p: vPix=%p etna_bo=%p format=%u/%u/%u\n",
+ pixmap, vPix, bo, fmt.format, fmt.swizzle, fmt.tile);
+#endif
+
+ return TRUE;
+}
+
+static void etnaviv_attach_name(ScreenPtr pScreen, PixmapPtr pPixmap,
+ uint32_t name)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pScreen);
+ struct etnaviv_pixmap *vPix = etnaviv_get_pixmap_priv(pPixmap);
+
+ /* If we are using our KMS DRM for buffer management, save its name */
+ if (etnaviv->dri2_armada && vPix)
+ vPix->name = name;
+}
+
+const struct armada_accel_ops etnaviv_ops = {
+ .pre_init = etnaviv_pre_init,
+ .screen_init = etnaviv_ScreenInit,
+ .import_dmabuf = etnaviv_import_dmabuf,
+ .attach_name = etnaviv_attach_name,
+ .free_pixmap = etnaviv_free_pixmap,
+};
diff --git a/etnaviv/etnaviv_accel.c b/etnaviv/etnaviv_accel.c
new file mode 100644
index 0000000..c4a79ab
--- /dev/null
+++ b/etnaviv/etnaviv_accel.c
@@ -0,0 +1,1511 @@
+/*
+ * Vivante GPU Acceleration Xorg driver
+ *
+ * Written by Russell King, 2012, derived in part from the
+ * Intel xorg X server driver.
+ *
+ * Notes:
+ * * For a window, the drawable inside the window structure has an
+ * x and y position for the underlying pixmap.
+ * * Composite clips have the drawable position already included.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#ifdef HAVE_DIX_CONFIG_H
+#include "dix-config.h"
+#endif
+#include "fb.h"
+#include "gcstruct.h"
+#include "xf86.h"
+
+#include "boxutil.h"
+#include "pixmaputil.h"
+#include "prefetch.h"
+#include "unaccel.h"
+#include "utils.h"
+
+#include "etnaviv_accel.h"
+#include "etnaviv_op.h"
+#include "etnaviv_utils.h"
+
+#include <etnaviv/etna.h>
+#include <etnaviv/state.xml.h>
+#include <etnaviv/state_2d.xml.h>
+#include "etnaviv_compat.h"
+
+static inline uint32_t scale16(uint32_t val, int bits)
+{
+ val <<= (16 - bits);
+ while (bits < 16) {
+ val |= val >> bits;
+ bits <<= 1;
+ }
+ return val >> 8;
+}
+
+void etnaviv_batch_wait_commit(struct etnaviv *etnaviv,
+ struct etnaviv_pixmap *vPix)
+{
+ if (vPix->need_stall && etnaviv->need_stall) {
+ etnaviv_commit(etnaviv, TRUE);
+ etnaviv->need_stall = FALSE;
+ }
+}
+
+static void etnaviv_batch_add(struct etnaviv *etnaviv,
+ struct etnaviv_pixmap *vPix)
+{
+ if (xorg_list_is_empty(&vPix->pending_node))
+ xorg_list_append(&vPix->pending_node, &etnaviv->pending_list);
+
+ etnaviv->need_stall = TRUE;
+ etnaviv->need_commit = TRUE;
+}
+
+
+static Bool gal_prepare_gpu(struct etnaviv *etnaviv, struct etnaviv_pixmap *vPix)
+{
+#ifdef DEBUG_CHECK_DRAWABLE_USE
+ if (vPix->in_use) {
+ fprintf(stderr, "Trying to accelerate: %p %p %u\n",
+ vPix,
+ vPix->etna_bo ? (void *)vPix->etna_bo :
+ (void *)vPix->bo,
+ vPix->in_use);
+ return FALSE;
+ }
+#endif
+
+ if (!etnaviv_map_gpu(etnaviv, vPix))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void etnaviv_blit_complete(struct etnaviv *etnaviv)
+{
+ etnaviv_de_end(etnaviv);
+}
+
+static void etnaviv_blit_start(struct etnaviv *etnaviv,
+ const struct etnaviv_de_op *op)
+{
+ if (op->src.pixmap)
+ etnaviv_batch_add(etnaviv, op->src.pixmap);
+
+ etnaviv_batch_add(etnaviv, op->dst.pixmap);
+
+ etnaviv_de_start(etnaviv, op);
+}
+
+static void etnaviv_blit(struct etnaviv *etnaviv,
+ const struct etnaviv_de_op *op, const BoxRec *pBox, size_t nBox)
+{
+ while (nBox) {
+ size_t n = nBox;
+
+ if (n > VIVANTE_MAX_2D_RECTS)
+ n = VIVANTE_MAX_2D_RECTS;
+
+ etnaviv_de_op(etnaviv, op, pBox, n);
+
+ pBox += n;
+ nBox -= n;
+ }
+}
+
+static void etnaviv_blit_clipped(struct etnaviv *etnaviv,
+ struct etnaviv_de_op *op, const BoxRec *pbox, size_t nbox)
+{
+ const BoxRec *clip = op->clip;
+ BoxRec boxes[VIVANTE_MAX_2D_RECTS];
+ size_t n;
+
+ for (n = 0; nbox; nbox--, pbox++) {
+ if (__box_intersect(&boxes[n], clip, pbox))
+ continue;
+
+ if (++n >= VIVANTE_MAX_2D_RECTS) {
+ etnaviv_de_op(etnaviv, op, boxes, n);
+ n = 0;
+ }
+ }
+
+ if (n)
+ etnaviv_de_op(etnaviv, op, boxes, n);
+}
+
+static void etnaviv_blit_srcdst(struct etnaviv *etnaviv,
+ struct etnaviv_de_op *op,
+ int src_x, int src_y, int dst_x, int dst_y, int width, int height)
+{
+ BoxRec box;
+
+ op->src.offset.x = src_x - (dst_x + op->dst.offset.x);
+ op->src.offset.y = src_y - (dst_y + op->dst.offset.y);
+
+ box.x1 = dst_x;
+ box.y1 = dst_y;
+ box.x2 = dst_x + width;
+ box.y2 = dst_y + height;
+
+ etnaviv_blit_start(etnaviv, op);
+ etnaviv_blit(etnaviv, op, &box, 1);
+ etnaviv_blit_complete(etnaviv);
+}
+
+static Bool etnaviv_init_dst_drawable(struct etnaviv *etnaviv,
+ struct etnaviv_de_op *op, DrawablePtr pDrawable)
+{
+ op->dst.pixmap = etnaviv_drawable_offset(pDrawable, &op->dst.offset);
+ if (!op->dst.pixmap)
+ return FALSE;
+
+ if (!gal_prepare_gpu(etnaviv, op->dst.pixmap))
+ return FALSE;
+
+ op->dst.bo = op->dst.pixmap->etna_bo;
+ op->dst.pitch = op->dst.pixmap->pitch;
+ op->dst.format = op->dst.pixmap->format;
+
+ return TRUE;
+}
+
+static Bool etnaviv_init_dstsrc_drawable(struct etnaviv *etnaviv,
+ struct etnaviv_de_op *op, DrawablePtr pDst, DrawablePtr pSrc)
+{
+ op->dst.pixmap = etnaviv_drawable_offset(pDst, &op->dst.offset);
+ op->src.pixmap = etnaviv_drawable_offset(pSrc, &op->src.offset);
+ if (!op->dst.pixmap || !op->src.pixmap)
+ return FALSE;
+
+ if (!gal_prepare_gpu(etnaviv, op->dst.pixmap) ||
+ !gal_prepare_gpu(etnaviv, op->src.pixmap))
+ return FALSE;
+
+ op->dst.bo = op->dst.pixmap->etna_bo;
+ op->dst.pitch = op->dst.pixmap->pitch;
+ op->dst.format = op->dst.pixmap->format;
+ op->src.bo = op->src.pixmap->etna_bo;
+ op->src.pitch = op->src.pixmap->pitch;
+ op->src.format = op->src.pixmap->format;
+
+ return TRUE;
+}
+
+static Bool etnaviv_init_src_pixmap(struct etnaviv *etnaviv,
+ struct etnaviv_de_op *op, PixmapPtr pix)
+{
+ op->src.pixmap = etnaviv_get_pixmap_priv(pix);
+ if (!op->src.pixmap)
+ return FALSE;
+
+ if (!gal_prepare_gpu(etnaviv, op->src.pixmap))
+ return FALSE;
+
+ op->src.bo = op->src.pixmap->etna_bo;
+ op->src.pitch = op->src.pixmap->pitch;
+ op->src.format = op->src.pixmap->format;
+ op->src.offset = ZERO_OFFSET;
+
+ return TRUE;
+}
+
+void etnaviv_commit(struct etnaviv *etnaviv, Bool stall)
+{
+ struct etna_ctx *ctx = etnaviv->ctx;
+ int ret;
+
+ if (stall)
+ ret = etna_finish(ctx);
+ else
+ ret = etna_flush(ctx, NULL);
+
+ if (ret) {
+ etnaviv_error(etnaviv, "etna_flush", ret);
+ return;
+ }
+
+ if (stall) {
+ struct etnaviv_pixmap *i, *n;
+
+ /*
+ * After these operations are committed with a stall,
+ * these pixmaps on the pending list will no longer be
+ * in-use by the GPU.
+ */
+ xorg_list_for_each_entry_safe(i, n, &etnaviv->pending_list,
+ pending_node) {
+ xorg_list_del(&i->pending_node);
+ xorg_list_init(&i->pending_node);
+
+ i->need_stall = FALSE;
+ }
+ }
+
+ etnaviv->need_commit = FALSE;
+}
+
+
+
+
+
+/*
+ * All operations must respect clips and planemask
+ * Colors: fgcolor and bgcolor are indexes into the colormap
+ * PolyLine, PolySegment, PolyRect, PolyArc:
+ * line width (pixels, 0=1pix), line style, cap style, join style
+ * FillPolygon, PolyFillRect, PolyFillArc:
+ * fill rule, fill style
+ * fill style:
+ * a solid foreground color, a transparent stipple,a n opaque stipple,
+ * or a tile.
+ * Stipples are bitmaps where the 1 bits represent that the foreground
+ * color is written, and 0 bits represent that either the pixel is left
+ * alone (transparent) or that the background color is written (opaque).
+ * A tile is a pixmap of the full depth of the GC that is applied in its
+ * full glory to all areas.
+ *
+ * The stipple and tile patterns can be any rectangular size, although
+ * some implementations will be faster for certain sizes such as 8x8
+ * or 32x32.
+ *
+ *
+ * 0 = Black, 1 = !src & !dst, 2 = !src & dst, 3 = !src
+ * 4 = src & !dst, 5 = !dst, 6 = src ^ dst, 7 = !src | !dst
+ * 8 = src & dst, 9 = !src ^ dst, a = dst, b = !src | dst
+ * c = src, d = src | !dst, e = src | dst, f = White
+ *
+ * high nibble: brush color bit is 1
+ * low nibble: brush color bit is 0
+ *
+ * fgrop: used when mask bit is 1
+ * bgrop: used when mask bit is 0
+ * mask (in brush): is an 8x8 mask: LSB is top line, LS bit righthand-most
+ */
+static const uint8_t etnaviv_fill_rop[] = {
+ /* GXclear */ 0x00, // ROP_BLACK,
+ /* GXand */ 0xa0, // ROP_BRUSH_AND_DST,
+ /* GXandReverse */ 0x50, // ROP_BRUSH_AND_NOT_DST,
+ /* GXcopy */ 0xf0, // ROP_BRUSH,
+ /* GXandInverted */ 0x0a, // ROP_NOT_BRUSH_AND_DST,
+ /* GXnoop */ 0xaa, // ROP_DST,
+ /* GXxor */ 0x5a, // ROP_BRUSH_XOR_DST,
+ /* GXor */ 0xfa, // ROP_BRUSH_OR_DST,
+ /* GXnor */ 0x05, // ROP_NOT_BRUSH_AND_NOT_DST,
+ /* GXequiv */ 0xa5, // ROP_NOT_BRUSH_XOR_DST,
+ /* GXinvert */ 0x55, // ROP_NOT_DST,
+ /* GXorReverse */ 0xf5, // ROP_BRUSH_OR_NOT_DST,
+ /* GXcopyInverted */ 0x0f, // ROP_NOT_BRUSH,
+ /* GXorInverted */ 0xaf, // ROP_NOT_BRUSH_OR_DST,
+ /* GXnand */ 0x5f, // ROP_NOT_BRUSH_OR_NOT_DST,
+ /* GXset */ 0xff // ROP_WHITE
+};
+
+static uint32_t etnaviv_fg_col(struct etnaviv *etnaviv, GCPtr pGC)
+{
+ uint32_t pixel, colour;
+
+ if (pGC->fillStyle == FillTiled)
+ pixel = pGC->tileIsPixel ? pGC->tile.pixel :
+ get_first_pixel(&pGC->tile.pixmap->drawable);
+ else
+ pixel = pGC->fgPixel;
+
+ /* With PE1.0, this is the pixel value, but PE2.0, it must be ARGB */
+ if (!VIV_FEATURE(etnaviv->conn, chipMinorFeatures0, 2DPE20))
+ return pixel;
+
+ /*
+ * These match the 2D drawable formats for non-composite ops.
+ * The aim here is to generate an A8R8G8B8 format colour which
+ * results in a destination pixel value of 'pixel'.
+ */
+ switch (pGC->depth) {
+ case 15: /* A1R5G5B5 */
+ colour = (pixel & 0x8000 ? 0xff000000 : 0) |
+ scale16((pixel & 0x7c00) >> 10, 5) << 16 |
+ scale16((pixel & 0x03e0) >> 5, 5) << 8 |
+ scale16((pixel & 0x001f), 5);
+ break;
+ case 16: /* R5G6B5 */
+ colour = 0xff000000 |
+ scale16((pixel & 0xf800) >> 11, 5) << 16 |
+ scale16((pixel & 0x07e0) >> 5, 6) << 8 |
+ scale16((pixel & 0x001f), 5);
+ break;
+ case 24: /* A8R8G8B8 */
+ default:
+ colour = pixel;
+ break;
+ }
+
+ return colour;
+}
+
+static void etnaviv_init_fill(struct etnaviv *etnaviv,
+ struct etnaviv_de_op *op, GCPtr pGC)
+{
+ op->src = INIT_BLIT_NULL;
+ op->blend_op = NULL;
+ op->rop = etnaviv_fill_rop[pGC->alu];
+ op->brush = TRUE;
+ op->fg_colour = etnaviv_fg_col(etnaviv, pGC);
+}
+
+static const uint8_t etnaviv_copy_rop[] = {
+ /* GXclear */ 0x00, // ROP_BLACK,
+ /* GXand */ 0x88, // ROP_DST_AND_SRC,
+ /* GXandReverse */ 0x44, // ROP_SRC_AND_NOT_DST,
+ /* GXcopy */ 0xcc, // ROP_SRC,
+ /* GXandInverted */ 0x22, // ROP_NOT_SRC_AND_DST,
+ /* GXnoop */ 0xaa, // ROP_DST,
+ /* GXxor */ 0x66, // ROP_DST_XOR_SRC,
+ /* GXor */ 0xee, // ROP_DST_OR_SRC,
+ /* GXnor */ 0x11, // ROP_NOT_SRC_AND_NOT_DST,
+ /* GXequiv */ 0x99, // ROP_NOT_SRC_XOR_DST,
+ /* GXinvert */ 0x55, // ROP_NOT_DST,
+ /* GXorReverse */ 0xdd, // ROP_SRC_OR_NOT_DST,
+ /* GXcopyInverted */ 0x33, // ROP_NOT_SRC,
+ /* GXorInverted */ 0xbb, // ROP_NOT_SRC_OR_DST,
+ /* GXnand */ 0x77, // ROP_NOT_SRC_OR_NOT_DST,
+ /* GXset */ 0xff // ROP_WHITE
+};
+
+Bool etnaviv_accel_FillSpans(DrawablePtr pDrawable, GCPtr pGC, int n,
+ DDXPointPtr ppt, int *pwidth, int fSorted)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDrawable->pScreen);
+ struct etnaviv_de_op op;
+ RegionPtr clip;
+ int nclip;
+ BoxRec *boxes, *b;
+ size_t sz;
+
+ assert(pGC->miTranslate);
+
+ if (!etnaviv_init_dst_drawable(etnaviv, &op, pDrawable))
+ return FALSE;
+
+ clip = fbGetCompositeClip(pGC);
+ nclip = RegionNumRects(clip);
+ if (nclip == 0)
+ return TRUE;
+
+ etnaviv_init_fill(etnaviv, &op, pGC);
+ op.clip = RegionExtents(clip);
+ op.cmd = VIVS_DE_DEST_CONFIG_COMMAND_LINE;
+
+ sz = sizeof(BoxRec) * n * nclip;
+
+ /* If we overflow, fallback. We could do better here. */
+ if (sz / nclip != sizeof(BoxRec) * n)
+ return FALSE;
+
+ boxes = malloc(sz);
+ if (!boxes)
+ return FALSE;
+
+ prefetch(ppt);
+ prefetch(ppt + 8);
+ prefetch(pwidth);
+ prefetch(pwidth + 8);
+
+ b = boxes;
+
+ while (n--) {
+ BoxPtr pBox;
+ int nBox, x, y, w;
+
+ prefetch(ppt + 16);
+ prefetch(pwidth + 16);
+
+ nBox = nclip;
+ pBox = RegionRects(clip);
+
+ y = ppt->y;
+ x = ppt->x;
+ w = *pwidth++;
+
+ do {
+ if (pBox->y1 <= y && pBox->y2 > y) {
+ int l, r;
+
+ l = x;
+ if (l < pBox->x1)
+ l = pBox->x1;
+ r = x + w;
+ if (r > pBox->x2)
+ r = pBox->x2;
+
+ if (l < r) {
+ b->x1 = l;
+ b->y1 = y;
+ b->x2 = r;
+ b->y2 = y;
+ b++;
+ }
+ }
+ pBox++;
+ } while (--nBox);
+ ppt++;
+ }
+
+ etnaviv_blit_start(etnaviv, &op);
+ etnaviv_blit(etnaviv, &op, boxes, b - boxes);
+ etnaviv_blit_complete(etnaviv);
+
+ free(boxes);
+
+ return TRUE;
+}
+
+Bool etnaviv_accel_PutImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+ int x, int y, int w, int h, int leftPad, int format, char *bits)
+{
+ ScreenPtr pScreen = pDrawable->pScreen;
+ PixmapPtr pPix, pTemp;
+ GCPtr gc;
+
+ if (format != ZPixmap)
+ return FALSE;
+
+ pPix = drawable_pixmap(pDrawable);
+
+ x += pDrawable->x;
+ y += pDrawable->y;
+
+ pTemp = pScreen->CreatePixmap(pScreen, w, h, pPix->drawable.depth, 0);
+ if (!pTemp)
+ return FALSE;
+
+ gc = GetScratchGC(pTemp->drawable.depth, pScreen);
+ if (!gc) {
+ pScreen->DestroyPixmap(pTemp);
+ return FALSE;
+ }
+
+ ValidateGC(&pTemp->drawable, gc);
+ unaccel_PutImage(&pTemp->drawable, gc, depth, 0, 0, w, h, leftPad,
+ format, bits);
+ FreeScratchGC(gc);
+
+ pGC->ops->CopyArea(&pTemp->drawable, &pPix->drawable, pGC,
+ 0, 0, w, h, x, y);
+ pScreen->DestroyPixmap(pTemp);
+ return TRUE;
+}
+
+void etnaviv_accel_CopyNtoN(DrawablePtr pSrc, DrawablePtr pDst,
+ GCPtr pGC, BoxPtr pBox, int nBox, int dx, int dy, Bool reverse,
+ Bool upsidedown, Pixel bitPlane, void *closure)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDst->pScreen);
+ struct etnaviv_de_op op;
+ BoxRec extent;
+
+ if (!nBox)
+ return;
+
+ if (etnaviv->force_fallback)
+ goto fallback;
+
+ if (!etnaviv_init_dstsrc_drawable(etnaviv, &op, pDst, pSrc))
+ goto fallback;
+
+ /* Include the copy delta on the source */
+ op.src.offset.x += dx - op.dst.offset.x;
+ op.src.offset.y += dy - op.dst.offset.y;
+
+ /* Calculate the overall extent */
+ extent.x1 = max_t(short, pDst->x, pSrc->x - dx);
+ extent.y1 = max_t(short, pDst->y, pSrc->y - dy);
+ extent.x2 = min_t(short, pDst->x + pDst->width,
+ pSrc->x + pSrc->width - dx);
+ extent.y2 = min_t(short, pDst->y + pDst->height,
+ pSrc->y + pSrc->height - dy);
+
+ if (extent.x1 < 0)
+ extent.x1 = 0;
+ if (extent.y1 < 0)
+ extent.y1 = 0;
+
+ op.blend_op = NULL;
+ op.clip = &extent;
+ op.rop = etnaviv_copy_rop[pGC ? pGC->alu : GXcopy];
+ op.cmd = VIVS_DE_DEST_CONFIG_COMMAND_BIT_BLT;
+ op.brush = FALSE;
+
+ etnaviv_blit_start(etnaviv, &op);
+ etnaviv_blit_clipped(etnaviv, &op, pBox, nBox);
+ etnaviv_blit_complete(etnaviv);
+
+ return;
+
+ fallback:
+ unaccel_CopyNtoN(pSrc, pDst, pGC, pBox, nBox, dx, dy, reverse,
+ upsidedown, bitPlane, closure);
+}
+
+Bool etnaviv_accel_PolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode,
+ int npt, DDXPointPtr ppt)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDrawable->pScreen);
+ struct etnaviv_de_op op;
+ BoxPtr pBox;
+ RegionRec region;
+ int i;
+ Bool overlap;
+
+ if (!etnaviv_init_dst_drawable(etnaviv, &op, pDrawable))
+ return FALSE;
+
+ etnaviv_init_fill(etnaviv, &op, pGC);
+ op.cmd = VIVS_DE_DEST_CONFIG_COMMAND_BIT_BLT;
+
+ pBox = malloc(npt * sizeof *pBox);
+ if (!pBox)
+ return FALSE;
+
+ if (mode == CoordModePrevious) {
+ int x, y;
+
+ x = y = 0;
+ for (i = 0; i < npt; i++) {
+ x += ppt[i].x;
+ y += ppt[i].y;
+ pBox[i].x1 = x + pDrawable->x;
+ pBox[i].y1 = y + pDrawable->y;
+ pBox[i].x2 = pBox[i].x1 + 1;
+ pBox[i].y2 = pBox[i].y1 + 1;
+ }
+ } else {
+ for (i = 0; i < npt; i++) {
+ pBox[i].x1 = ppt[i].x + pDrawable->x;
+ pBox[i].y1 = ppt[i].y + pDrawable->y;
+ pBox[i].x2 = pBox[i].x1 + 1;
+ pBox[i].y2 = pBox[i].y1 + 1;
+ }
+ }
+
+ /* Convert the boxes to a region */
+ RegionInitBoxes(&region, pBox, npt);
+ free(pBox);
+
+ RegionValidate(&region, &overlap);
+
+ /* Intersect them with the clipping region */
+ RegionIntersect(&region, &region, fbGetCompositeClip(pGC));
+
+ op.clip = RegionExtents(&region);
+
+ etnaviv_blit_start(etnaviv, &op);
+ etnaviv_blit(etnaviv, &op, RegionRects(&region),
+ RegionNumRects(&region));
+ etnaviv_blit_complete(etnaviv);
+
+ RegionUninit(&region);
+
+ return TRUE;
+}
+
+Bool etnaviv_accel_PolyFillRectSolid(DrawablePtr pDrawable, GCPtr pGC, int n,
+ xRectangle * prect)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDrawable->pScreen);
+ struct etnaviv_de_op op;
+ RegionPtr clip;
+ BoxRec boxes[VIVANTE_MAX_2D_RECTS], *box;
+ int nclip, nb, chunk;
+
+ if (!etnaviv_init_dst_drawable(etnaviv, &op, pDrawable))
+ return FALSE;
+
+ prefetch(prect);
+ prefetch(prect + 4);
+
+ clip = fbGetCompositeClip(pGC);
+
+ etnaviv_init_fill(etnaviv, &op, pGC);
+ op.clip = RegionExtents(clip);
+ op.cmd = VIVS_DE_DEST_CONFIG_COMMAND_BIT_BLT;
+
+ etnaviv_blit_start(etnaviv, &op);
+
+ chunk = VIVANTE_MAX_2D_RECTS;
+ nb = 0;
+ while (n--) {
+ BoxRec full_rect;
+
+ prefetch (prect + 8);
+
+ full_rect.x1 = prect->x + pDrawable->x;
+ full_rect.y1 = prect->y + pDrawable->y;
+ full_rect.x2 = full_rect.x1 + prect->width;
+ full_rect.y2 = full_rect.y1 + prect->height;
+
+ prect++;
+
+ for (box = RegionRects(clip), nclip = RegionNumRects(clip);
+ nclip; nclip--, box++) {
+ if (__box_intersect(&boxes[nb], &full_rect, box))
+ continue;
+
+ if (++nb >= chunk) {
+ etnaviv_blit(etnaviv, &op, boxes, nb);
+ nb = 0;
+ }
+ }
+ }
+ if (nb)
+ etnaviv_blit(etnaviv, &op, boxes, nb);
+ etnaviv_blit_complete(etnaviv);
+
+ return TRUE;
+}
+
+Bool etnaviv_accel_PolyFillRectTiled(DrawablePtr pDrawable, GCPtr pGC, int n,
+ xRectangle * prect)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDrawable->pScreen);
+ struct etnaviv_de_op op;
+ PixmapPtr pTile = pGC->tile.pixmap;
+ RegionPtr rects;
+ int nbox;
+ Bool ret;
+
+ if (!etnaviv_init_dst_drawable(etnaviv, &op, pDrawable) ||
+ !etnaviv_init_src_pixmap(etnaviv, &op, pTile))
+ return FALSE;
+
+ op.blend_op = NULL;
+ op.rop = etnaviv_copy_rop[pGC ? pGC->alu : GXcopy];
+ op.cmd = VIVS_DE_DEST_CONFIG_COMMAND_BIT_BLT;
+ op.brush = FALSE;
+
+ /* Convert the rectangles to a region */
+ rects = RegionFromRects(n, prect, CT_UNSORTED);
+
+ /* Translate them for the drawable position */
+ RegionTranslate(rects, pDrawable->x, pDrawable->y);
+
+ /* Intersect them with the clipping region */
+ RegionIntersect(rects, rects, fbGetCompositeClip(pGC));
+
+ nbox = RegionNumRects(rects);
+ if (nbox) {
+ int tile_w, tile_h, tile_off_x, tile_off_y;
+ BoxPtr pBox;
+
+ ret = FALSE;
+
+ /* Calculate the tile offset from the rect coords */
+ tile_off_x = op.dst.offset.x + pDrawable->x + pGC->patOrg.x;
+ tile_off_y = op.dst.offset.y + pDrawable->y + pGC->patOrg.y;
+
+ tile_w = pTile->drawable.width;
+ tile_h = pTile->drawable.height;
+
+ pBox = RegionRects(rects);
+ while (nbox--) {
+ int dst_y, height, tile_y;
+
+ op.clip = pBox;
+
+ dst_y = pBox->y1;
+ height = pBox->y2 - dst_y;
+ modulus(dst_y - tile_off_y, tile_h, tile_y);
+
+ while (height > 0) {
+ int dst_x, width, tile_x, h;
+
+ dst_x = pBox->x1;
+ width = pBox->x2 - dst_x;
+ modulus(dst_x - tile_off_x, tile_w, tile_x);
+
+ h = tile_h - tile_y;
+ if (h > height)
+ h = height;
+ height -= h;
+
+ while (width > 0) {
+ int w;
+
+ w = tile_w - tile_x;
+ if (w > width)
+ w = width;
+ width -= w;
+
+ etnaviv_blit_srcdst(etnaviv, &op,
+ tile_x, tile_y,
+ dst_x, dst_y,
+ w, h);
+
+ dst_x += w;
+ tile_x = 0;
+ }
+ dst_y += h;
+ tile_y = 0;
+ }
+ pBox++;
+ }
+ ret = TRUE;
+ } else {
+ ret = TRUE;
+ }
+
+ RegionUninit(rects);
+ RegionDestroy(rects);
+
+ return ret;
+}
+
+#ifdef RENDER
+#include "mipict.h"
+#include "fbpict.h"
+#include "pictureutil.h"
+
+static void adjust_repeat(PicturePtr pPict, int x, int y, unsigned w, unsigned h)
+{
+ int tx, ty;
+
+ if (pPict->pDrawable &&
+ pPict->repeat &&
+ pPict->filter != PictFilterConvolution &&
+ transform_is_integer_translation(pPict->transform, &tx, &ty) &&
+ (pPict->pDrawable->width > 1 || pPict->pDrawable->height > 1) &&
+ drawable_contains(pPict->pDrawable, x + tx, y + ty, w, h)) {
+//fprintf(stderr, "%s: removing repeat on %p\n", __FUNCTION__, pPict);
+ pPict->repeat = 0;
+ }
+}
+
+static const struct etnaviv_blend_op etnaviv_composite_op[] = {
+#define OP(op,s,d) \
+ [PictOp##op] = { \
+ .alpha_mode = \
+ VIVS_DE_ALPHA_MODES_GLOBAL_SRC_ALPHA_MODE_NORMAL | \
+ VIVS_DE_ALPHA_MODES_GLOBAL_DST_ALPHA_MODE_NORMAL | \
+ VIVS_DE_ALPHA_MODES_SRC_BLENDING_MODE(DE_BLENDMODE_##s) | \
+ VIVS_DE_ALPHA_MODES_DST_BLENDING_MODE(DE_BLENDMODE_##d), \
+ }
+ OP(Clear, ZERO, ZERO),
+ OP(Src, ONE, ZERO),
+ OP(Dst, ZERO, ONE),
+ OP(Over, ONE, INVERSED),
+ OP(OverReverse, INVERSED, ONE),
+ OP(In, NORMAL, ZERO),
+ OP(InReverse, ZERO, NORMAL),
+ OP(Out, INVERSED, ZERO),
+ OP(OutReverse, ZERO, INVERSED),
+ OP(Atop, NORMAL, INVERSED),
+ OP(AtopReverse, INVERSED, NORMAL),
+ OP(Xor, INVERSED, INVERSED),
+ OP(Add, ONE, ONE),
+#undef OP
+};
+
+static Bool etnaviv_op_uses_source_alpha(struct etnaviv_blend_op *op)
+{
+ unsigned src_mode;
+
+ src_mode = op->alpha_mode & VIVS_DE_ALPHA_MODES_SRC_BLENDING_MODE__MASK;
+ src_mode >>= VIVS_DE_ALPHA_MODES_SRC_BLENDING_MODE__SHIFT;
+
+ if (src_mode == DE_BLENDMODE_ZERO || src_mode == DE_BLENDMODE_ONE)
+ return FALSE;
+
+ return TRUE;
+}
+
+static Bool etnaviv_fill_single(struct etnaviv *etnaviv,
+ struct etnaviv_pixmap *vPix, const BoxRec *clip, uint32_t colour)
+{
+ struct etnaviv_de_op op = {
+ .clip = clip,
+ .rop = 0xf0,
+ .cmd = VIVS_DE_DEST_CONFIG_COMMAND_BIT_BLT,
+ .brush = TRUE,
+ .fg_colour = colour,
+ };
+
+ if (!gal_prepare_gpu(etnaviv, vPix))
+ return FALSE;
+
+ op.dst = INIT_BLIT_PIX(vPix, vPix->pict_format, ZERO_OFFSET);
+
+ etnaviv_blit_start(etnaviv, &op);
+ etnaviv_blit(etnaviv, &op, clip, 1);
+ etnaviv_blit_complete(etnaviv);
+
+ return TRUE;
+}
+
+static Bool etnaviv_blend(struct etnaviv *etnaviv, const BoxRec *clip,
+ const struct etnaviv_blend_op *blend,
+ struct etnaviv_pixmap *vDst, struct etnaviv_pixmap *vSrc,
+ const BoxRec *pBox, unsigned nBox, xPoint src_offset,
+ xPoint dst_offset)
+{
+ struct etnaviv_de_op op = {
+ .blend_op = blend,
+ .clip = clip,
+ .rop = 0xcc,
+ .cmd = VIVS_DE_DEST_CONFIG_COMMAND_BIT_BLT,
+ .brush = FALSE,
+ };
+
+ if (!gal_prepare_gpu(etnaviv, vDst) || !gal_prepare_gpu(etnaviv, vSrc))
+ return FALSE;
+
+ op.src = INIT_BLIT_PIX(vSrc, vSrc->pict_format, src_offset);
+ op.dst = INIT_BLIT_PIX(vDst, vDst->pict_format, dst_offset);
+
+ etnaviv_blit_start(etnaviv, &op);
+ etnaviv_blit(etnaviv, &op, pBox, nBox);
+ etnaviv_blit_complete(etnaviv);
+
+ return TRUE;
+}
+
+static void etnaviv_set_format(struct etnaviv_pixmap *vpix, PicturePtr pict)
+{
+ vpix->pict_format = etnaviv_pict_format(pict->format, FALSE);
+}
+
+static Bool etnaviv_pict_solid_argb(PicturePtr pict, uint32_t *col)
+{
+ unsigned r, g, b, a, rbits, gbits, bbits, abits;
+ PictFormatPtr pFormat;
+ xRenderColor colour;
+ CARD32 pixel;
+ uint32_t argb;
+
+ if (!picture_is_solid(pict, &pixel))
+ return FALSE;
+
+ pFormat = pict->pFormat;
+ /* If no format (eg, source-only) assume it's the correct format */
+ if (!pFormat || pict->format == PICT_a8r8g8b8) {
+ *col = pixel;
+ return TRUE;
+ }
+
+ switch (pFormat->type) {
+ case PictTypeDirect:
+ r = (pixel >> pFormat->direct.red) & pFormat->direct.redMask;
+ g = (pixel >> pFormat->direct.green) & pFormat->direct.greenMask;
+ b = (pixel >> pFormat->direct.blue) & pFormat->direct.blueMask;
+ a = (pixel >> pFormat->direct.alpha) & pFormat->direct.alphaMask;
+ rbits = Ones(pFormat->direct.redMask);
+ gbits = Ones(pFormat->direct.greenMask);
+ bbits = Ones(pFormat->direct.blueMask);
+ abits = Ones(pFormat->direct.alphaMask);
+ if (abits)
+ argb = scale16(a, abits) << 24;
+ else
+ argb = 0xff000000;
+ if (rbits)
+ argb |= scale16(r, rbits) << 16;
+ if (gbits)
+ argb |= scale16(g, gbits) << 8;
+ if (bbits)
+ argb |= scale16(b, bbits);
+ break;
+ case PictTypeIndexed:
+ miRenderPixelToColor(pFormat, pixel, &colour);
+ argb = (colour.alpha >> 8) << 24;
+ argb |= (colour.red >> 8) << 16;
+ argb |= (colour.green >> 8) << 8;
+ argb |= (colour.blue >> 8);
+ break;
+ default:
+ /* unknown type, just assume pixel value */
+ argb = pixel;
+ break;
+ }
+
+ *col = argb;
+
+ return TRUE;
+}
+
+/*
+ * If we're filling a solid
+ * surface, force it to have alpha; it may be used in combination
+ * with a mask. Otherwise, we ask for the plain source format,
+ * with or without alpha, and convert later when copying.
+ */
+static struct etnaviv_pixmap *etnaviv_acquire_src(struct etnaviv *etnaviv,
+ PicturePtr pict, const BoxRec *clip,
+ PixmapPtr pix, struct etnaviv_pixmap *vTemp, xPoint *src_topleft)
+{
+ struct etnaviv_pixmap *vSrc;
+ DrawablePtr drawable = pict->pDrawable;
+ uint32_t colour;
+ xPoint src_offset;
+ int tx, ty;
+
+ if (etnaviv_pict_solid_argb(pict, &colour)) {
+ src_topleft->x = 0;
+ src_topleft->y = 0;
+ if (!etnaviv_fill_single(etnaviv, vTemp, clip, colour))
+ return NULL;
+
+ return vTemp;
+ }
+
+ vSrc = etnaviv_drawable_offset(pict->pDrawable, &src_offset);
+ if (!vSrc)
+ return NULL;
+
+ etnaviv_set_format(vSrc, pict);
+ if (!pict->repeat &&
+ transform_is_integer_translation(pict->transform, &tx, &ty) &&
+ etnaviv_src_format_valid(etnaviv, vSrc->pict_format)) {
+ src_topleft->x += src_offset.x + tx;
+ src_topleft->y += src_offset.y + ty;
+ } else {
+ PictFormatPtr f;
+ PicturePtr dest;
+ int err;
+ int x = src_topleft->x - drawable->x;
+ int y = src_topleft->y - drawable->y;
+ int w = clip->x2;
+ int h = clip->y2;
+
+ f = PictureMatchFormat(drawable->pScreen, 32, PICT_a8r8g8b8);
+ if (!f)
+ return NULL;
+
+ dest = CreatePicture(0, &pix->drawable, f, 0, 0, serverClient, &err);
+ if (!dest)
+ return NULL;
+ ValidatePicture(dest);
+
+ unaccel_Composite(PictOpSrc, pict, NULL, dest,
+ x, y, 0, 0, 0, 0, w, h);
+ FreePicture(dest, 0);
+ src_topleft->x = 0;
+ src_topleft->y = 0;
+ vSrc = vTemp;
+ }
+
+ return vSrc;
+}
+
+static int etnaviv_accel_final_blend(struct etnaviv *etnaviv,
+ const struct etnaviv_blend_op *blend,
+ xPoint dst_offset, RegionPtr region,
+ PicturePtr pDst, struct etnaviv_pixmap *vDst,
+ PicturePtr pSrc, struct etnaviv_pixmap *vSrc, xPoint src_offset)
+{
+ int rc;
+
+#if 0
+ fprintf(stderr, "%s: dst %d,%d,%d,%d %u (%x) bo %p\n",
+ __FUNCTION__, pDst->pDrawable->x, pDst->pDrawable->y,
+ pDst->pDrawable->x + pDst->pDrawable->width,
+ pDst->pDrawable->y + pDst->pDrawable->height,
+ vDst->pict_format, pDst->format,
+ vDst->etna_bo ? vDst->etna_bo : vDst->bo);
+ etnaviv_batch_wait_commit(etnaviv, vSrc);
+ dump_vPix(buf, etnaviv, vSrc, 1, "A-FSRC%02.2x-%p", op, pSrc);
+ dump_vPix(buf, etnaviv, vDst, 1, "A-FDST%02.2x-%p", op, pDst);
+#endif
+
+ rc = etnaviv_blend(etnaviv, RegionExtents(region), blend, vDst, vSrc,
+ RegionRects(region), RegionNumRects(region),
+ src_offset, dst_offset);
+
+#if 0
+ etnaviv_batch_wait_commit(etnaviv, vDst);
+ dump_vPix(buf, etnaviv, vDst, PICT_FORMAT_A(pDst->format) != 0,
+ "A-DEST%02.2x-%p", op, pDst);
+#endif
+
+ return rc;
+}
+
+/*
+ * There is a bug in the GPU hardware with destinations lacking alpha and
+ * swizzles BGRA/RGBA. Rather than the GPU treating bits 7:0 as alpha, it
+ * continues to treat bits 31:24 as alpha. This results in it replacing
+ * the B or R bits on input to the blend operation with 1.0. However, it
+ * continues to accept the non-existent source alpha from bits 31:24.
+ *
+ * Work around this by switching to the equivalent alpha format, and using
+ * global alpha to replace the alpha channel. The alpha channel subsitution
+ * is performed at this function's callsite.
+ */
+static Bool etnaviv_workaround_nonalpha(struct etnaviv_pixmap *vpix)
+{
+ switch (vpix->pict_format.format) {
+ case DE_FORMAT_X4R4G4B4:
+ vpix->pict_format.format = DE_FORMAT_A4R4G4B4;
+ return TRUE;
+ case DE_FORMAT_X1R5G5B5:
+ vpix->pict_format.format = DE_FORMAT_A1R5G5B5;
+ return TRUE;
+ case DE_FORMAT_X8R8G8B8:
+ vpix->pict_format.format = DE_FORMAT_A8R8G8B8;
+ return TRUE;
+ case DE_FORMAT_R5G6B5:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Perform the simple PictOpClear operation. */
+static Bool etnaviv_Composite_Clear(PicturePtr pSrc, PicturePtr pMask,
+ PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask,
+ INT16 xDst, INT16 yDst, CARD16 width, CARD16 height)
+{
+ ScreenPtr pScreen = pDst->pDrawable->pScreen;
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pScreen);
+ struct etnaviv_pixmap *vDst;
+ RegionRec region;
+ xPoint src_topleft, dst_offset;
+ int rc;
+
+ vDst = etnaviv_drawable_offset(pDst->pDrawable, &dst_offset);
+ if (!vDst)
+ return FALSE;
+
+ etnaviv_set_format(vDst, pDst);
+ etnaviv_workaround_nonalpha(vDst);
+ if (!etnaviv_dst_format_valid(etnaviv, vDst->pict_format))
+ return FALSE;
+
+ /* Include the destination drawable's position on the pixmap */
+ xDst += pDst->pDrawable->x;
+ yDst += pDst->pDrawable->y;
+
+ /*
+ * The picture's pCompositeClip includes the destination drawable
+ * position, so we must adjust the picture position for that prior
+ * to miComputeCompositeRegion().
+ */
+ if (pSrc->pDrawable) {
+ xSrc += pSrc->pDrawable->x;
+ ySrc += pSrc->pDrawable->y;
+ }
+ if (pMask && pMask->pDrawable) {
+ xMask += pMask->pDrawable->x;
+ yMask += pMask->pDrawable->y;
+ }
+
+ memset(&region, 0, sizeof(region));
+ if (!miComputeCompositeRegion(&region, pSrc, pMask, pDst,
+ xSrc, ySrc, xMask, yMask,
+ xDst, yDst, width, height))
+ return TRUE;
+
+ src_topleft.x = 0;
+ src_topleft.y = 0;
+
+ rc = etnaviv_accel_final_blend(etnaviv,
+ &etnaviv_composite_op[PictOpClear],
+ dst_offset, &region,
+ pDst, vDst,
+ pSrc, vDst, src_topleft);
+ RegionUninit(&region);
+
+ return rc ? TRUE : FALSE;
+}
+
+int etnaviv_accel_Composite(CARD8 op, PicturePtr pSrc, PicturePtr pMask,
+ PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask,
+ INT16 xDst, INT16 yDst, CARD16 width, CARD16 height)
+{
+ ScreenPtr pScreen = pDst->pDrawable->pScreen;
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pScreen);
+ struct etnaviv_pixmap *vDst, *vSrc, *vMask, *vTemp = NULL;
+ struct etnaviv_blend_op final_op;
+ PixmapPtr pPixTemp = NULL;
+ RegionRec region;
+ BoxRec clip_temp;
+ xPoint src_topleft, dst_offset;
+ int rc;
+
+ /* If the destination has an alpha map, fallback */
+ if (pDst->alphaMap)
+ return FALSE;
+
+ /* Short-circuit for PictOpClear */
+ if (op == PictOpClear)
+ return etnaviv_Composite_Clear(pSrc, pMask, pDst,
+ xSrc, ySrc, xMask, yMask,
+ xDst, yDst, width, height);
+
+ /* If we can't do the op, there's no point going any further */
+ if (op >= ARRAY_SIZE(etnaviv_composite_op))
+ return FALSE;
+
+ if (pSrc->alphaMap || (pMask && pMask->alphaMap))
+ return FALSE;
+
+ /* If the source has no drawable, and is not solid, fallback */
+ if (!pSrc->pDrawable && !picture_is_solid(pSrc, NULL))
+ return FALSE;
+
+ /* The destination pixmap must have a bo */
+ vDst = etnaviv_drawable_offset(pDst->pDrawable, &dst_offset);
+ if (!vDst)
+ return FALSE;
+
+ etnaviv_set_format(vDst, pDst);
+ if (!etnaviv_dst_format_valid(etnaviv, vDst->pict_format))
+ return FALSE;
+
+ final_op = etnaviv_composite_op[op];
+
+ if (etnaviv_workaround_nonalpha(vDst)) {
+ final_op.alpha_mode |= VIVS_DE_ALPHA_MODES_GLOBAL_DST_ALPHA_MODE_GLOBAL;
+ final_op.dst_alpha = 255;
+
+ /*
+ * PE1.0 hardware contains a bug with destinations
+ * of RGB565, which force src.A to one.
+ */
+ if (vDst->pict_format.format == DE_FORMAT_R5G6B5 &&
+ !VIV_FEATURE(etnaviv->conn, chipMinorFeatures0, 2DPE20) &&
+ etnaviv_op_uses_source_alpha(&final_op))
+ return FALSE;
+ }
+
+ if (pMask) {
+ uint32_t colour;
+
+ if (pMask->componentAlpha)
+ return FALSE;
+
+ /*
+ * A PictOpOver with a mask looks like this:
+ *
+ * dst.A = src.A * mask.A + dst.A * (1 - src.A * mask.A)
+ * dst.C = src.C * mask.A + dst.C * (1 - src.A * mask.A)
+ *
+ * Or, in terms of the generic alpha blend equations:
+ *
+ * dst.A = src.A * Fa + dst.A * Fb
+ * dst.C = src.C * Fa + dst.C * Fb
+ *
+ * with Fa = mask.A, Fb = (1 - src.A * mask.A). With a
+ * solid mask, mask.A is constant.
+ *
+ * Our GPU provides us with the ability to replace or scale
+ * src.A and/or dst.A inputs in the generic alpha blend
+ * equations, and using a PictOpAtop operation, the factors
+ * are Fa = dst.A, Fb = 1 - src.A.
+ *
+ * If we subsitute src.A with src.A * mask.A, and dst.A with
+ * mask.A, then we get pretty close for the colour channels.
+ * However, the alpha channel becomes simply:
+ *
+ * dst.A = mask.A
+ *
+ * and hence will be incorrect. Therefore, the destination
+ * format must not have an alpha channel.
+ */
+ if (op == PictOpOver && !PICT_FORMAT_A(pDst->format) &&
+ etnaviv_pict_solid_argb(pMask, &colour)) {
+ uint32_t src_alpha_mode;
+
+ /* Convert the colour to A8 */
+ colour >>= 24;
+
+ /*
+ * With global scaled alpha and a non-alpha source,
+ * the GPU appears to buggily read and use the X bits
+ * as source alpha. Work around this by using global
+ * source alpha instead for this case.
+ */
+ if (PICT_FORMAT_A(pSrc->format))
+ src_alpha_mode = VIVS_DE_ALPHA_MODES_GLOBAL_SRC_ALPHA_MODE_SCALED;
+ else
+ src_alpha_mode = VIVS_DE_ALPHA_MODES_GLOBAL_SRC_ALPHA_MODE_GLOBAL;
+
+ final_op.alpha_mode = src_alpha_mode |
+ VIVS_DE_ALPHA_MODES_GLOBAL_DST_ALPHA_MODE_GLOBAL |
+ VIVS_DE_ALPHA_MODES_SRC_BLENDING_MODE(DE_BLENDMODE_NORMAL) |
+ VIVS_DE_ALPHA_MODES_DST_BLENDING_MODE(DE_BLENDMODE_INVERSED);
+ final_op.src_alpha =
+ final_op.dst_alpha = colour;
+ pMask = NULL;
+ } else if (pMask->pDrawable) {
+ int tx, ty;
+
+ adjust_repeat(pMask, xMask, yMask, width, height);
+
+ /* We don't handle mask repeats (yet) */
+ if (pMask->repeat)
+ return FALSE;
+
+ if (!transform_is_integer_translation(pMask->transform, &tx, &ty))
+ return FALSE;
+
+ /* FIXME: we should not include the transformation... */
+ xMask += pMask->pDrawable->x + tx;
+ yMask += pMask->pDrawable->y + ty;
+ } else {
+ return FALSE;
+ }
+ }
+
+#if 0
+fprintf(stderr, "%s: i: op 0x%02x src=%p,%d,%d mask=%p,%d,%d dst=%p,%d,%d %ux%u\n",
+ __FUNCTION__, op, pSrc, xSrc, ySrc, pMask, xMask, yMask,
+ pDst, xDst, yDst, width, height);
+#endif
+
+ memset(&region, 0, sizeof(region));
+
+ /* Remove repeat on source or mask if useless */
+ adjust_repeat(pSrc, xSrc, ySrc, width, height);
+
+ /* Include the destination drawable's position on the pixmap */
+ xDst += pDst->pDrawable->x;
+ yDst += pDst->pDrawable->y;
+
+ if (pSrc->pDrawable) {
+ xSrc += pSrc->pDrawable->x;
+ ySrc += pSrc->pDrawable->y;
+ }
+
+ src_topleft.x = xSrc;
+ src_topleft.y = ySrc;
+
+ /*
+ * Compute the regions to be composited. This provides us with the
+ * rectangles which need to be composited at each stage, where the
+ * rectangle coordinates are based on the destination image.
+ *
+ * Clips are interesting. A picture composite clip has the drawable
+ * position included in it. A picture client clip does not.
+ *
+ * The clip region below is calculated by beginning with the box
+ * xDst,yDst,xDst+width,yDst+width, and then intersecting that with
+ * the destination composite clips. Therefore, xDst,yDst must
+ * contain the drawable position.
+ *
+ * The source and mask pictures are then factored in, intersecting
+ * their client clips (which doesn't have a drawable position) with
+ * the current set of clips from the destination, first translating
+ * them by (xDst - xSrc),(yDst - ySrc).
+ *
+ * However, the X unaccelerated fb layer ignores any clips in the
+ * source and mask. So... we ignore them here too.
+ */
+ if (!miComputeCompositeRegion(&region, pSrc, NULL, pDst, xSrc, ySrc,
+ 0, 0, xDst, yDst, width, height))
+ return TRUE;
+
+ /*
+ * Compute the temporary image clipping box, which is the
+ * clipping region extents without the destination offset.
+ */
+ clip_temp = *RegionExtents(&region);
+ clip_temp.x1 -= xDst;
+ clip_temp.y1 -= yDst;
+ clip_temp.x2 -= xDst;
+ clip_temp.y2 -= yDst;
+
+ /*
+ * Get a temporary pixmap. We don't know whether we will need
+ * this at this stage. Its size is the size of the temporary clip
+ * box.
+ */
+ pPixTemp = pScreen->CreatePixmap(pScreen, clip_temp.x2, clip_temp.y2,
+ 32, 0);
+ if (!pPixTemp)
+ goto failed;
+
+ vTemp = etnaviv_get_pixmap_priv(pPixTemp);
+ if (!vTemp)
+ goto failed;
+
+ vTemp->pict_format = etnaviv_pict_format(PICT_a8r8g8b8, FALSE);
+
+ /*
+ * Get the source. The source image will be described by vSrc with
+ * origin src_topleft. This may or may not be the temporary image,
+ * and vSrc->pict_format describes its format, including whether the
+ * alpha channel is valid.
+ */
+ vSrc = etnaviv_acquire_src(etnaviv, pSrc, &clip_temp,
+ pPixTemp, vTemp, &src_topleft);
+ if (!vSrc)
+ goto failed;
+
+ /*
+ * Apply the same work-around for a non-alpha source as for
+ * a non-alpha destination.
+ */
+ if (!pMask && vSrc != vTemp &&
+ (final_op.alpha_mode & VIVS_DE_ALPHA_MODES_GLOBAL_SRC_ALPHA_MODE__MASK)
+ == VIVS_DE_ALPHA_MODES_GLOBAL_SRC_ALPHA_MODE_NORMAL &&
+ etnaviv_workaround_nonalpha(vSrc)) {
+ final_op.alpha_mode |= VIVS_DE_ALPHA_MODES_GLOBAL_SRC_ALPHA_MODE_GLOBAL;
+ final_op.src_alpha = 255;
+ }
+
+//etnaviv_batch_wait_commit(etnaviv, vSrc);
+//dump_vPix(buf, etnaviv, vSrc, 1, "A-ISRC%02.2x-%p", op, pSrc);
+
+#if 0
+#define C(p,e) ((p) ? (p)->e : 0)
+fprintf(stderr, "%s: 0: OP 0x%02x src=%p[%p,%p,%u,%ux%u]x%dy%d mask=%p[%p,%u,%ux%u]x%dy%d dst=%p[%p]x%dy%d %ux%u\n",
+ __FUNCTION__, op,
+ pSrc, pSrc->transform, pSrc->pDrawable, pSrc->repeat, C(pSrc->pDrawable, width), C(pSrc->pDrawable, height), src_topleft.x, src_topleft.y,
+ pMask, C(pMask, pDrawable), C(pMask, repeat), C(C(pMask, pDrawable), width), C(C(pMask, pDrawable), height), xMask, yMask,
+ pDst, pDst->pDrawable, xDst, yDst,
+ width, height);
+}
+#endif
+
+ /*
+ * If we have a mask, handle it. We deal with the mask by doing a
+ * InReverse operation. However, note that the source may already
+ * be in the temporary buffer. Also note that the temporary buffer
+ * must have valid alpha upon completion of this operation for the
+ * subsequent final blend to work.
+ *
+ * If vTemp != vSrc
+ * vTemp <= vSrc (if non-alpha, + max alpha)
+ * vTemp <= vTemp BlendOp(In) vMask
+ * vSrc = vTemp
+ */
+ if (pMask) {
+ xPoint mask_offset, temp_offset;
+
+ vMask = etnaviv_drawable_offset(pMask->pDrawable, &mask_offset);
+ if (!vMask)
+ goto failed;
+
+ etnaviv_set_format(vMask, pMask);
+
+ mask_offset.x += xMask;
+ mask_offset.y += yMask;
+ temp_offset.x = 0;
+ temp_offset.y = 0;
+//dump_vPix(buf, etnaviv, vMask, 1, "A-MASK%02.2x-%p", op, pMask);
+
+ if (vTemp != vSrc) {
+ /*
+ * Copy Source to Temp.
+ * The source may not have alpha, but we need the
+ * temporary pixmap to have alpha. Try to convert
+ * while copying. (If this doesn't work, use OR
+ * in the brush with maximum alpha value.)
+ */
+ if (!etnaviv_blend(etnaviv, &clip_temp, NULL,
+ vTemp, vSrc, &clip_temp, 1,
+ src_topleft, temp_offset))
+ goto failed;
+//etnaviv_batch_wait_commit(etnaviv, vTemp);
+//dump_vPix(buf, etnaviv, vTemp, 1, "A-TMSK%02.2x-%p", op, pMask);
+ }
+
+#if 0
+if (pMask && pMask->pDrawable)
+ fprintf(stderr, "%s: src %d,%d,%d,%d %d,%d %u (%x)\n",
+ __FUNCTION__, pMask->pDrawable->x, pMask->pDrawable->y,
+ pMask->pDrawable->x + pMask->pDrawable->width, pMask->pDrawable->y + pMask->pDrawable->height,
+ xMask, yMask, vMask->pict_format, pMask->format);
+#endif
+
+ if (!etnaviv_blend(etnaviv, &clip_temp, &etnaviv_composite_op[PictOpInReverse],
+ vTemp, vMask, &clip_temp, 1,
+ mask_offset, temp_offset))
+ goto failed;
+
+ vSrc = vTemp;
+ src_topleft = temp_offset;
+ }
+
+ src_topleft.x -= xDst + dst_offset.x;
+ src_topleft.y -= yDst + dst_offset.y;
+
+ rc = etnaviv_accel_final_blend(etnaviv, &final_op,
+ dst_offset, &region,
+ pDst, vDst,
+ pSrc, vSrc, src_topleft);
+ RegionUninit(&region);
+ if (pPixTemp) {
+ ScreenPtr pScreen = pPixTemp->drawable.pScreen;
+ pScreen->DestroyPixmap(pPixTemp);
+ }
+ return !!rc;
+
+ failed:
+ RegionUninit(&region);
+ if (pPixTemp) {
+ ScreenPtr pScreen = pPixTemp->drawable.pScreen;
+ pScreen->DestroyPixmap(pPixTemp);
+ }
+ return FALSE;
+}
+#endif
+
+Bool etnaviv_accel_init(struct etnaviv *etnaviv)
+{
+ Bool pe20;
+ int ret;
+
+ ret = viv_open(VIV_HW_2D, &etnaviv->conn);
+ if (ret) {
+ xf86DrvMsg(etnaviv->scrnIndex, X_ERROR,
+ "etnaviv: unable to open: %s\n",
+ ret == -1 ? strerror(errno) : etnaviv_strerror(ret));
+ return FALSE;
+ }
+
+ pe20 = VIV_FEATURE(etnaviv->conn, chipMinorFeatures0, 2DPE20);
+
+ xf86DrvMsg(etnaviv->scrnIndex, X_PROBED,
+ "Vivante GC%x GPU revision %x (etnaviv) 2d PE%s\n",
+ etnaviv->conn->chip.chip_model,
+ etnaviv->conn->chip.chip_revision,
+ pe20 ? "2.0" : "1.0");
+
+ if (!VIV_FEATURE(etnaviv->conn, chipFeatures, PIPE_2D)) {
+ xf86DrvMsg(etnaviv->scrnIndex, X_ERROR,
+ "No 2D support\n");
+ viv_close(etnaviv->conn);
+ return FALSE;
+ }
+
+ ret = etna_create(etnaviv->conn, &etnaviv->ctx);
+ if (ret != ETNA_OK) {
+ xf86DrvMsg(etnaviv->scrnIndex, X_ERROR,
+ "etnaviv: unable to create context: %s\n",
+ ret == -1 ? strerror(errno) : etnaviv_strerror(ret));
+ viv_close(etnaviv->conn);
+ return FALSE;
+ }
+
+ etna_set_pipe(etnaviv->ctx, ETNA_PIPE_2D);
+
+ /*
+ * The high watermark is the index in our batch buffer at which
+ * we dump the queued operation over to the command buffers.
+ * We need room for a flush (two words.)
+ */
+ etnaviv->batch_de_high_watermark = MAX_BATCH_SIZE - 2;
+
+ return TRUE;
+}
+
+void etnaviv_accel_shutdown(struct etnaviv *etnaviv)
+{
+ etna_finish(etnaviv->ctx);
+ etna_free(etnaviv->ctx);
+ viv_close(etnaviv->conn);
+}
diff --git a/etnaviv/etnaviv_accel.h b/etnaviv/etnaviv_accel.h
new file mode 100644
index 0000000..4590b39
--- /dev/null
+++ b/etnaviv/etnaviv_accel.h
@@ -0,0 +1,273 @@
+/*
+ * Vivante GPU Acceleration Xorg driver
+ *
+ * Written by Russell King, 2012, derived in part from the
+ * Intel xorg X server driver.
+ */
+#ifndef VIVANTE_ACCEL_H
+#define VIVANTE_ACCEL_H
+
+#include <etnaviv/viv.h>
+#include <etnaviv/etna.h>
+#include <etnaviv/etna_bo.h>
+#include "compat-list.h"
+#include "pixmaputil.h"
+#include "etnaviv_compat.h"
+#include "etnaviv_op.h"
+
+struct armada_accel_ops;
+struct drm_armada_bo;
+struct drm_armada_bufmgr;
+struct etnaviv_dri2_info;
+
+#undef DEBUG
+
+/* Debugging options */
+#define DEBUG_CHECK_DRAWABLE_USE
+#undef DEBUG_MAP
+#undef DEBUG_PIXMAP
+
+/* Accelerated operations debugging */
+#undef DEBUG_COPYNTON
+#undef DEBUG_FILLSPANS
+#undef DEBUG_POLYFILLRECT
+#undef DEBUG_PUTIMAGE
+
+
+/* Debugging */
+#define OP_NOP 0
+#define OP_USER_INV 1
+#define OP_USER_CLN 2
+#define OP_USER_FLS 3
+#define OP_KERN_INV 5
+#define OP_KERN_CLN 6
+#define OP_KERN_FLS 7
+
+#define dbg(fmt...) fprintf(stderr, fmt)
+
+/*
+ * The maximum size of an operation in the batch buffer. A 2D draw
+ * operation can contain up to 255 rectangles, which equates to 512
+ * words (including the operation word.) Add to this the states to
+ * be loaded before, and 1024 is a conservative overestimation.
+ */
+#define MAX_BATCH_SIZE 1024
+#define MAX_RELOC_SIZE 8
+
+struct etnaviv {
+ struct viv_conn *conn;
+ struct etna_ctx *ctx;
+ struct xorg_list pending_list;
+ Bool need_stall;
+ Bool need_commit;
+ Bool force_fallback;
+ struct drm_armada_bufmgr *bufmgr;
+ int scrnIndex;
+#ifdef HAVE_DRI2
+ Bool dri2_enabled;
+ Bool dri2_armada;
+ struct etnaviv_dri2_info *dri2;
+#endif
+
+ uint32_t batch[MAX_BATCH_SIZE];
+ unsigned int batch_setup_size;
+ unsigned int batch_size;
+ unsigned int batch_de_high_watermark;
+ struct etnaviv_reloc {
+ struct etna_bo *bo;
+ unsigned int batch_index;
+ Bool write;
+ } reloc[MAX_RELOC_SIZE];
+ unsigned int reloc_size;
+
+ CloseScreenProcPtr CloseScreen;
+ GetImageProcPtr GetImage;
+ GetSpansProcPtr GetSpans;
+ ChangeWindowAttributesProcPtr ChangeWindowAttributes;
+ CopyWindowProcPtr CopyWindow;
+ CreatePixmapProcPtr CreatePixmap;
+ DestroyPixmapProcPtr DestroyPixmap;
+ CreateGCProcPtr CreateGC;
+ BitmapToRegionProcPtr BitmapToRegion;
+ ScreenBlockHandlerProcPtr BlockHandler;
+
+ CompositeProcPtr Composite;
+ GlyphsProcPtr Glyphs;
+ TrapezoidsProcPtr Trapezoids;
+ TrianglesProcPtr Triangles;
+ AddTrianglesProcPtr AddTriangles;
+ AddTrapsProcPtr AddTraps;
+ UnrealizeGlyphProcPtr UnrealizeGlyph;
+};
+
+struct etnaviv_pixmap {
+ uint16_t width;
+ uint16_t height;
+ unsigned pitch;
+ struct etnaviv_format format;
+ struct etnaviv_format pict_format;
+ viv_usermem_t info;
+ struct xorg_list pending_node;
+ Bool need_stall;
+
+ uint8_t state;
+#define ST_DMABUF (1 << 4)
+
+ enum {
+ NONE,
+ CPU,
+ GPU,
+ } owner;
+#ifdef DEBUG_CHECK_DRAWABLE_USE
+ int in_use;
+#endif
+ struct drm_armada_bo *bo;
+ struct etna_bo *etna_bo;
+ uint32_t name;
+};
+
+#define BATCH_SETUP_START(etp) \
+ do { \
+ struct etnaviv *_et = etp; \
+ _et->batch_setup_size = 0; \
+ _et->batch_size = 0; \
+ _et->reloc_size = 0; \
+ } while (0)
+
+#define BATCH_SETUP_END(etp) \
+ do { \
+ struct etnaviv *__et = etp; \
+ __et->batch_setup_size = __et->batch_size; \
+ } while (0)
+
+#define BATCH_OP_START(etp) \
+ do { \
+ struct etnaviv *__et = etp; \
+ __et->batch_size = __et->batch_setup_size; \
+ } while (0)
+
+#define EMIT(etp, val) \
+ do { \
+ struct etnaviv *_et = etp; \
+ assert(_et->batch_size < MAX_BATCH_SIZE - 1); \
+ _et->batch[_et->batch_size++] = val; \
+ } while (0)
+
+#define EMIT_RELOC(etp, _bo, _off, _wr) \
+ do { \
+ struct etnaviv *__et = etp; \
+ struct etnaviv_reloc *r = &__et->reloc[__et->reloc_size++]; \
+ r->bo = _bo; \
+ r->batch_index = __et->batch_size; \
+ r->write = _wr; \
+ EMIT(__et, _off); \
+ } while (0)
+
+#define EMIT_LOADSTATE(etp, st, num) \
+ do { \
+ struct etnaviv *__et = etp; \
+ assert(!(__et->batch_size & 1)); \
+ EMIT(__et, VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE | \
+ VIV_FE_LOAD_STATE_HEADER_COUNT(num) | \
+ VIV_FE_LOAD_STATE_HEADER_OFFSET((st) >> 2)); \
+ } while (0)
+
+#define EMIT_DRAW_2D(etp, count) \
+ do { \
+ struct etnaviv *__et = etp; \
+ assert(!(__et->batch_size & 1)); \
+ EMIT(__et, VIV_FE_DRAW_2D_HEADER_OP_DRAW_2D | \
+ VIV_FE_DRAW_2D_HEADER_COUNT(count)); \
+ /* next word is unused */ \
+ __et->batch_size ++; \
+ } while (0)
+
+#define EMIT_STALL(etp, from, to) \
+ do { \
+ struct etnaviv *__et = etp; \
+ assert(!(__et->batch_size & 1)); \
+ EMIT(__et, VIV_FE_STALL_HEADER_OP_STALL); \
+ EMIT(__et, VIV_FE_STALL_TOKEN_FROM(from) | \
+ VIV_FE_STALL_TOKEN_TO(to)); \
+ } while (0)
+
+#define EMIT_NOP(etp) \
+ do { \
+ struct etnaviv *__et = etp; \
+ assert(!(__et->batch_size & 1)); \
+ EMIT(__et, VIV_FE_NOP_HEADER_OP_NOP); \
+ EMIT(__et, 0); \
+ } while (0)
+
+#define EMIT_ALIGN(etp) \
+ do { \
+ struct etnaviv *__et = etp; \
+ __et->batch_size += __et->batch_size & 1; \
+ } while (0)
+
+/* Addresses must be aligned */
+#define VIVANTE_ALIGN_MASK 63
+
+/* 2D acceleration */
+Bool etnaviv_accel_FillSpans(DrawablePtr pDrawable, GCPtr pGC, int n,
+ DDXPointPtr ppt, int *pwidth, int fSorted);
+Bool etnaviv_accel_PutImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+ int x, int y, int w, int h, int leftPad, int format, char *bits);
+void etnaviv_accel_CopyNtoN(DrawablePtr pSrc, DrawablePtr pDst,
+ GCPtr pGC, BoxPtr pBox, int nBox, int dx, int dy, Bool reverse,
+ Bool upsidedown, Pixel bitPlane, void *closure);
+Bool etnaviv_accel_PolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode,
+ int npt, DDXPointPtr ppt);
+Bool etnaviv_accel_PolyFillRectSolid(DrawablePtr pDrawable, GCPtr pGC, int n,
+ xRectangle * prect);
+Bool etnaviv_accel_PolyFillRectTiled(DrawablePtr pDrawable, GCPtr pGC, int n,
+ xRectangle * prect);
+
+/* 3D acceleration */
+int etnaviv_accel_Composite(CARD8 op, PicturePtr pSrc, PicturePtr pMask,
+ PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask,
+ INT16 xDst, INT16 yDst, CARD16 width, CARD16 height);
+
+void etnaviv_commit(struct etnaviv *etnaviv, Bool stall);
+
+void etnaviv_batch_wait_commit(struct etnaviv *etnaviv, struct etnaviv_pixmap *vPix);
+
+void etnaviv_accel_shutdown(struct etnaviv *);
+Bool etnaviv_accel_init(struct etnaviv *);
+
+static inline struct etnaviv_pixmap *etnaviv_get_pixmap_priv(PixmapPtr pixmap)
+{
+ extern etnaviv_Key etnaviv_pixmap_index;
+ return etnaviv_GetKeyPriv(&pixmap->devPrivates, &etnaviv_pixmap_index);
+}
+
+static inline struct etnaviv_pixmap *etnaviv_drawable_offset(
+ DrawablePtr pDrawable, xPoint *offset)
+{
+ PixmapPtr pix = drawable_pixmap_offset(pDrawable, offset);
+ return etnaviv_get_pixmap_priv(pix);
+}
+
+static inline struct etnaviv *etnaviv_get_screen_priv(ScreenPtr pScreen)
+{
+ extern etnaviv_Key etnaviv_screen_index;
+ return etnaviv_GetKeyPriv(&pScreen->devPrivates, &etnaviv_screen_index);
+}
+
+static inline void etnaviv_set_pixmap_priv(PixmapPtr pixmap, struct etnaviv_pixmap *g)
+{
+ extern etnaviv_Key etnaviv_pixmap_index;
+ dixSetPrivate(&pixmap->devPrivates, &etnaviv_pixmap_index, g);
+}
+
+static inline void etnaviv_set_screen_priv(ScreenPtr pScreen, struct etnaviv *g)
+{
+ extern etnaviv_Key etnaviv_screen_index;
+ dixSetPrivate(&pScreen->devPrivates, &etnaviv_screen_index, g);
+}
+
+Bool etnaviv_pixmap_flink(PixmapPtr pixmap, uint32_t *name);
+
+extern const struct armada_accel_ops etnaviv_ops;
+
+#endif
diff --git a/etnaviv/etnaviv_compat.h b/etnaviv/etnaviv_compat.h
new file mode 100644
index 0000000..94056c8
--- /dev/null
+++ b/etnaviv/etnaviv_compat.h
@@ -0,0 +1,22 @@
+/*
+ * Vivante GPU Acceleration Xorg driver
+ *
+ * Written by Russell King, 2012, derived in part from the
+ * Intel xorg X server driver.
+ */
+#ifndef VIVANTE_COMPAT_H
+#define VIVANTE_COMPAT_H
+
+#include "utils.h"
+
+#if HAS_DEVPRIVATEKEYREC
+#define etnaviv_CreateKey(key, type) dixRegisterPrivateKey(key, type, 0)
+#define etnaviv_GetKeyPriv(dp, key) dixGetPrivate(dp, key)
+#define etnaviv_Key DevPrivateKeyRec
+#else
+#define etnaviv_CreateKey(key, type) dixRequestPrivate(key, 0)
+#define etnaviv_GetKeyPriv(dp, key) dixLookupPrivate(dp, key)
+#define etnaviv_Key int
+#endif
+
+#endif
diff --git a/etnaviv/etnaviv_dri2.c b/etnaviv/etnaviv_dri2.c
new file mode 100644
index 0000000..32ff50f
--- /dev/null
+++ b/etnaviv/etnaviv_dri2.c
@@ -0,0 +1,375 @@
+/*
+ * Vivante GPU Acceleration Xorg driver
+ *
+ * Written by Russell King, 2012, derived in part from the
+ * Intel xorg X server driver.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+
+/* xorg includes */
+#include "dixstruct.h"
+#include "fb.h"
+#include "gcstruct.h"
+#include "xf86.h"
+#include "dri2.h"
+
+/* drm includes */
+#include <xf86drm.h>
+#include <armada_bufmgr.h>
+
+#include "compat-api.h"
+#include "common_drm.h"
+#include "common_drm_dri2.h"
+#include "common_drm_helper.h"
+#include "etnaviv_accel.h"
+#include "etnaviv_dri2.h"
+#include "etnaviv_utils.h"
+#include "pixmaputil.h"
+
+#if DRI2INFOREC_VERSION < 4
+#error DRI2 is too old!
+#endif
+
+struct etnaviv_dri2_info {
+ char *devname;
+};
+
+static DRI2Buffer2Ptr etnaviv_dri2_CreateBuffer(DrawablePtr drawable,
+ unsigned int attachment, unsigned int format)
+{
+ struct common_dri2_buffer *buf;
+ ScreenPtr pScreen = drawable->pScreen;
+ PixmapPtr pixmap = NULL;
+ uint32_t name;
+
+ buf = calloc(1, sizeof *buf);
+ if (!buf)
+ return NULL;
+
+ if (attachment == DRI2BufferFrontLeft) {
+ pixmap = drawable_pixmap(drawable);
+
+ if (!etnaviv_get_pixmap_priv(pixmap)) {
+ /* Force the backing buffer to be reallocated */
+ drawable = &pixmap->drawable;
+ pixmap = NULL;
+ } else {
+ pixmap->refcnt++;
+ }
+ }
+
+ if (pixmap == NULL) {
+ pixmap = common_dri2_create_pixmap(drawable, attachment, format,
+ 0);
+ if (!pixmap)
+ goto err;
+ }
+
+ if (!etnaviv_pixmap_flink(pixmap, &name))
+ goto err;
+
+ return common_dri2_setup_buffer(buf, attachment, format,
+ pixmap, name, 0);
+
+ err:
+ if (pixmap)
+ pScreen->DestroyPixmap(pixmap);
+ free(buf);
+
+ return NULL;
+}
+
+static void etnaviv_dri2_CopyRegion(DrawablePtr drawable, RegionPtr pRegion,
+ DRI2BufferPtr dstBuf, DRI2BufferPtr srcBuf)
+{
+ ScreenPtr screen = drawable->pScreen;
+ DrawablePtr src = common_dri2_get_drawable(srcBuf, drawable);
+ DrawablePtr dst = common_dri2_get_drawable(dstBuf, drawable);
+ RegionPtr clip;
+ GCPtr gc;
+
+ gc = GetScratchGC(dst->depth, screen);
+ if (!gc)
+ return;
+
+ clip = REGION_CREATE(screen, NULL, 0);
+ REGION_COPY(screen, clip, pRegion);
+ gc->funcs->ChangeClip(gc, CT_REGION, clip, 0);
+ ValidateGC(dst, gc);
+
+ /*
+ * FIXME: wait for scanline to be outside the region to be copied...
+ * that is an interesting problem for Dove/GAL stuff because they're
+ * independent, and there's no way for the GPU to know where the
+ * scan position is. For now, just do the copy anyway.
+ */
+ gc->ops->CopyArea(src, dst, gc, 0, 0,
+ drawable->width, drawable->height, 0, 0);
+
+ FreeScratchGC(gc);
+
+ /*
+ * We will commit the copy via the flush callback called from
+ * WriteToClient.
+ */
+}
+
+static void etnaviv_dri2_blit(ClientPtr client, DrawablePtr draw,
+ DRI2BufferPtr front, DRI2BufferPtr back, unsigned frame,
+ unsigned tv_sec, unsigned tv_usec, DRI2SwapEventPtr func, void *data)
+{
+ RegionRec region;
+ BoxRec box;
+
+ box.x1 = 0;
+ box.y1 = 0;
+ box.x2 = draw->width;
+ box.y2 = draw->height;
+ RegionInit(&region, &box, 0);
+
+ etnaviv_dri2_CopyRegion(draw, &region, front, back);
+
+ DRI2SwapComplete(client, draw, frame, tv_sec, tv_usec,
+ DRI2_BLIT_COMPLETE, func, data);
+}
+
+static void etnaviv_dri2_swap(struct common_dri2_wait *wait, DrawablePtr draw,
+ unsigned frame, unsigned tv_sec, unsigned tv_usec)
+{
+ etnaviv_dri2_blit(wait->client, draw, wait->front, wait->back,
+ frame, tv_sec, tv_usec,
+ wait->client ? wait->swap_func : NULL,
+ wait->swap_data);
+ common_dri2_wait_free(wait);
+}
+
+static void etnaviv_dri2_flip_complete(struct common_dri2_wait *wait,
+ DrawablePtr draw, unsigned frame, unsigned tv_sec, unsigned tv_usec)
+{
+ DRI2SwapComplete(wait->client, draw, frame, tv_sec, tv_usec,
+ DRI2_FLIP_COMPLETE,
+ wait->client ? wait->swap_func : NULL,
+ wait->swap_data);
+
+ common_dri2_wait_free(wait);
+}
+
+static Bool etnaviv_dri2_ScheduleFlip(DrawablePtr drawable,
+ struct common_dri2_wait *wait)
+{
+ ScreenPtr pScreen = drawable->pScreen;
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+ PixmapPtr front = pScreen->GetScreenPixmap(pScreen);
+ PixmapPtr back = to_common_dri2_buffer(wait->back)->pixmap;
+
+ assert(front == to_common_dri2_buffer(wait->front)->pixmap);
+
+ if (common_drm_flip(pScrn, back, wait, wait->crtc)) {
+ struct etnaviv_pixmap *f_pix = etnaviv_get_pixmap_priv(front);
+ struct etnaviv_pixmap *b_pix = etnaviv_get_pixmap_priv(back);
+
+ etnaviv_set_pixmap_priv(front, b_pix);
+ etnaviv_set_pixmap_priv(back, f_pix);
+
+ common_dri2_flip_buffers(pScreen, wait);
+
+ wait->event_func = etnaviv_dri2_flip_complete;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void etnaviv_dri2_flip(struct common_dri2_wait *wait, DrawablePtr draw,
+ unsigned frame, unsigned tv_sec, unsigned tv_usec)
+{
+ if (common_dri2_can_flip(draw, wait) &&
+ etnaviv_dri2_ScheduleFlip(draw, wait))
+ return;
+
+ etnaviv_dri2_swap(wait, draw, frame, tv_sec, tv_usec);
+}
+
+static int etnaviv_dri2_ScheduleSwap(ClientPtr client, DrawablePtr draw,
+ DRI2BufferPtr front, DRI2BufferPtr back, CARD64 *target_msc,
+ CARD64 divisor, CARD64 remainder, DRI2SwapEventPtr func, void *data)
+{
+ ScrnInfoPtr pScrn = xf86ScreenToScrn(draw->pScreen);
+ struct common_dri2_wait *wait;
+ drmVBlank vbl;
+ CARD64 cur_msc;
+ xf86CrtcPtr crtc;
+ int ret;
+
+ crtc = common_dri2_drawable_crtc(draw);
+
+ /* Drawable not displayed... just complete */
+ if (!crtc)
+ goto blit;
+
+ *target_msc &= 0xffffffff;
+ divisor &= 0xffffffff;
+ remainder &= 0xffffffff;
+
+ wait = common_dri2_wait_alloc(client, draw, DRI2_SWAP);
+ if (!wait)
+ goto blit;
+
+ wait->event_func = etnaviv_dri2_swap;
+ wait->crtc = crtc;
+ wait->swap_func = func;
+ wait->swap_data = data;
+ wait->front = front;
+ wait->back = back;
+
+ common_dri2_buffer_reference(front);
+ common_dri2_buffer_reference(back);
+
+ ret = common_drm_vblank_get(pScrn, crtc, &vbl, __FUNCTION__);
+ if (ret)
+ goto blit_free;
+
+ cur_msc = vbl.reply.sequence;
+
+ /* Flips need to be submitted one frame before */
+ if (common_dri2_can_flip(draw, wait)) {
+ wait->event_func = etnaviv_dri2_flip;
+ wait->type = DRI2_FLIP;
+ if (*target_msc > 0)
+ *target_msc -= 1;
+ }
+
+ if (divisor == 0 || cur_msc < *target_msc) {
+ /*
+ * If we can, schedule the flip directly from here rather
+ * than waiting for an event from the kernel for the current
+ * (or a past) MSC.
+ */
+ if (wait->type == DRI2_FLIP &&
+ divisor == 0 && cur_msc >= *target_msc &&
+ etnaviv_dri2_ScheduleFlip(draw, wait)) {
+ /*
+ * I think xf86-video-intel misses this: target_msc
+ * is in the past, we should update it with the new
+ * msc, otherwise it will remain at the original.
+ */
+ *target_msc = cur_msc;
+ return TRUE;
+ }
+
+ /*
+ * If target_msc has been passed, set it to cur_msc to
+ * ensure we return a reasonable value back to the caller.
+ * This makes the swap_interval logic more robust.
+ */
+ if (cur_msc > *target_msc)
+ *target_msc = cur_msc;
+
+ vbl.request.sequence = *target_msc;
+ } else {
+ vbl.request.sequence = cur_msc - (cur_msc % divisor) + remainder;
+
+ /*
+ * If the calculated deadline sequence is smaller than or equal
+ * to cur_msc, it means we've passed the point when effective
+ * onset frame seq could satisfy seq % divisor == remainder,
+ * so we need to wait for the next time this will happen.
+ *
+ * This comparison takes the 1 frame swap delay in pageflipping
+ * mode into account, as well as a potential
+ * DRM_VBLANK_NEXTONMISS delay if we are blitting/exchanging
+ * instead of flipping.
+ */
+ if (vbl.request.sequence <= cur_msc)
+ vbl.request.sequence += divisor;
+
+ /* Account for 1 frame extra pageflip delay if flip > 0 */
+ if (wait->type == DRI2_FLIP)
+ vbl.request.sequence -= 1;
+ }
+
+ ret = common_drm_vblank_queue_event(pScrn, crtc, &vbl, __FUNCTION__,
+ wait->type != DRI2_FLIP, wait);
+ if (ret)
+ goto blit_free;
+
+ *target_msc = vbl.reply.sequence + (wait->type == DRI2_FLIP);
+ wait->frame = *target_msc;
+
+ return TRUE;
+
+ blit_free:
+ common_dri2_wait_free(wait);
+ blit:
+ etnaviv_dri2_blit(client, draw, front, back, 0, 0, 0, func, data);
+ *target_msc = 0;
+ return TRUE;
+}
+
+static const DRI2InfoRec dri2_info = {
+ .version = 4,
+ .driverName = "etnaviv",
+
+ .CreateBuffer = etnaviv_dri2_CreateBuffer,
+ .DestroyBuffer = common_dri2_DestroyBuffer,
+ .CopyRegion = etnaviv_dri2_CopyRegion,
+
+ .ScheduleSwap = etnaviv_dri2_ScheduleSwap,
+ .GetMSC = common_dri2_GetMSC,
+ .ScheduleWaitMSC = common_dri2_ScheduleWaitMSC,
+};
+
+Bool etnaviv_dri2_ScreenInit(ScreenPtr pScreen, int drm_fd)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pScreen);
+ struct etnaviv_dri2_info *dri;
+ DRI2InfoRec info;
+ int dri2_major = 0;
+ int dri2_minor = 0;
+ const char *driverNames[1];
+
+ if (xf86LoaderCheckSymbol("DRI2Version"))
+ DRI2Version(&dri2_major, &dri2_minor);
+
+ if (dri2_major < 1 || (dri2_major == 1 && dri2_minor < 2)) {
+ xf86DrvMsg(etnaviv->scrnIndex, X_WARNING,
+ "DRI2 requires DRI2 module version 1.2.0 or later\n");
+ return FALSE;
+ }
+
+ if (!common_dri2_ScreenInit(pScreen))
+ return FALSE;
+
+ dri = xnfcalloc(1, sizeof *dri);
+ dri->devname = drmGetDeviceNameFromFd(drm_fd);
+
+ etnaviv->dri2 = dri;
+
+ info = dri2_info;
+ info.fd = drm_fd;
+ info.deviceName = dri->devname;
+ info.numDrivers = 1;
+ info.driverNames = driverNames;
+ driverNames[0] = info.driverName;
+
+ return DRI2ScreenInit(pScreen, &info);
+}
+
+void etnaviv_dri2_CloseScreen(CLOSE_SCREEN_ARGS_DECL)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pScreen);
+ struct etnaviv_dri2_info *dri = etnaviv->dri2;
+
+ if (dri) {
+ DRI2CloseScreen(pScreen);
+
+ etnaviv->dri2 = NULL;
+ drmFree(dri->devname);
+ free(dri);
+ }
+}
diff --git a/etnaviv/etnaviv_dri2.h b/etnaviv/etnaviv_dri2.h
new file mode 100644
index 0000000..9879d37
--- /dev/null
+++ b/etnaviv/etnaviv_dri2.h
@@ -0,0 +1,15 @@
+/*
+ * Vivante GPU Acceleration Xorg driver
+ *
+ * Written by Russell King, 2012, derived in part from the
+ * Intel xorg X server driver.
+ */
+#ifndef VIVANTE_DRI2_H
+#define VIVANTE_DRI2_H
+
+Bool etnaviv_dri2_ScreenInit(ScreenPtr pScreen, int drm_fd);
+void etnaviv_dri2_CloseScreen(CLOSE_SCREEN_ARGS_DECL);
+void etnaviv_dri2_vblank(int fd, unsigned frame, unsigned tv_sec,
+ unsigned tv_usec, void *event);
+
+#endif
diff --git a/etnaviv/etnaviv_emit.c b/etnaviv/etnaviv_emit.c
new file mode 100644
index 0000000..dbdbda8
--- /dev/null
+++ b/etnaviv/etnaviv_emit.c
@@ -0,0 +1,23 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "xf86.h"
+#include "fb.h"
+
+#include "etnaviv_accel.h"
+#include "etnaviv_op.h"
+
+void etnaviv_emit(struct etnaviv *etnaviv)
+{
+ struct etna_ctx *ctx = etnaviv->ctx;
+ struct etnaviv_reloc *r;
+ unsigned int i;
+
+ for (i = 0, r = etnaviv->reloc; i < etnaviv->reloc_size; i++, r++)
+ etnaviv->batch[r->batch_index] += etna_bo_gpu_address(r->bo);
+
+ etna_reserve(ctx, etnaviv->batch_size);
+ memcpy(&ctx->buf[ctx->offset], etnaviv->batch, etnaviv->batch_size * 4);
+ ctx->offset += etnaviv->batch_size;
+}
diff --git a/etnaviv/etnaviv_module.c b/etnaviv/etnaviv_module.c
new file mode 100644
index 0000000..ea5545f
--- /dev/null
+++ b/etnaviv/etnaviv_module.c
@@ -0,0 +1,74 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_DIX_CONFIG_H
+#include "dix-config.h"
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+#include "xf86.h"
+#include "utils.h"
+
+#include "armada_accel.h"
+#include "etnaviv_accel.h"
+
+_X_EXPORT Bool accel_module_init(const struct armada_accel_ops **ops)
+{
+ *ops = &etnaviv_ops;
+
+ return TRUE;
+}
+
+static const char *dev_names[] = {
+ "/dev/gal3d",
+ "/dev/galcore",
+ "/dev/graphics/galcore",
+};
+
+static pointer etnaviv_setup(pointer module, pointer opts, int *errmaj,
+ int *errmin)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dev_names); i++) {
+ /*
+ * Test for the presence of the special device,
+ * fail to load if it isn't present.
+ */
+ if (access(dev_names[i], R_OK|W_OK) == 0)
+ return (pointer) 1;
+
+ if (errno == ENOENT)
+ continue;
+
+ LogMessage(X_ERROR, "access(%s) failed: %s\n",
+ dev_names[i], strerror(errno));
+ }
+
+ *errmaj = LDR_NOHARDWARE;
+ *errmin = 0;
+
+ return NULL;
+}
+
+static XF86ModuleVersionInfo etnaviv_version = {
+ .modname = "Etnaviv GPU driver",
+ .vendor = MODULEVENDORSTRING,
+ ._modinfo1_ = MODINFOSTRING1,
+ ._modinfo2_ = MODINFOSTRING2,
+ .xf86version = XORG_VERSION_CURRENT,
+ .majorversion = PACKAGE_VERSION_MAJOR,
+ .minorversion = PACKAGE_VERSION_MINOR,
+ .patchlevel = PACKAGE_VERSION_PATCHLEVEL,
+ .abiclass = ABI_CLASS_ANSIC,
+ .abiversion = ABI_ANSIC_VERSION,
+ .moduleclass = MOD_CLASS_NONE,
+ .checksum = { 0, 0, 0, 0 },
+};
+
+_X_EXPORT XF86ModuleData etnaviv_gpuModuleData = {
+ .vers = &etnaviv_version,
+ .setup = etnaviv_setup,
+};
diff --git a/etnaviv/etnaviv_op.c b/etnaviv/etnaviv_op.c
new file mode 100644
index 0000000..f4755fe
--- /dev/null
+++ b/etnaviv/etnaviv_op.c
@@ -0,0 +1,192 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "xf86.h"
+#include "fb.h"
+
+#include "etnaviv_accel.h"
+#include "etnaviv_op.h"
+
+#include <etnaviv/etna.h>
+#include <etnaviv/etna_bo.h>
+#include <etnaviv/state.xml.h>
+#include <etnaviv/state_2d.xml.h>
+
+static inline uint32_t etnaviv_src_config(struct etnaviv_format fmt,
+ Bool relative)
+{
+ uint32_t src_cfg;
+
+ src_cfg = VIVS_DE_SRC_CONFIG_PE10_SOURCE_FORMAT(fmt.format) |
+ VIVS_DE_SRC_CONFIG_TRANSPARENCY(0) |
+ VIVS_DE_SRC_CONFIG_LOCATION_MEMORY |
+ VIVS_DE_SRC_CONFIG_PACK_PACKED8 |
+ VIVS_DE_SRC_CONFIG_SWIZZLE(fmt.swizzle) |
+ VIVS_DE_SRC_CONFIG_SOURCE_FORMAT(fmt.format);
+
+ if (relative)
+ src_cfg |= VIVS_DE_SRC_CONFIG_SRC_RELATIVE_RELATIVE;
+
+ return src_cfg;
+}
+
+static void etnaviv_set_source_bo(struct etnaviv *etnaviv, struct etna_bo *bo,
+ uint32_t pitch, struct etnaviv_format format, const xPoint *offset)
+{
+ size_t n_src = offset ? 5 : 4;
+ uint32_t src_cfg = etnaviv_src_config(format, offset != NULL);
+
+ EMIT_LOADSTATE(etnaviv, VIVS_DE_SRC_ADDRESS, n_src);
+ EMIT_RELOC(etnaviv, bo, 0, FALSE);
+ EMIT(etnaviv, VIVS_DE_SRC_STRIDE_STRIDE(pitch));
+ EMIT(etnaviv, VIVS_DE_SRC_ROTATION_CONFIG_ROTATION_DISABLE);
+ EMIT(etnaviv, src_cfg);
+ if (offset)
+ EMIT(etnaviv, VIVS_DE_SRC_ORIGIN_X(offset->x) |
+ VIVS_DE_SRC_ORIGIN_Y(offset->y));
+ EMIT_ALIGN(etnaviv);
+}
+
+static void etnaviv_set_dest_bo(struct etnaviv *etnaviv, struct etna_bo *bo,
+ uint32_t pitch, struct etnaviv_format fmt, uint32_t cmd)
+{
+ uint32_t dst_cfg;
+
+ dst_cfg = VIVS_DE_DEST_CONFIG_FORMAT(fmt.format) | cmd |
+ VIVS_DE_DEST_CONFIG_SWIZZLE(fmt.swizzle);
+
+ EMIT_LOADSTATE(etnaviv, VIVS_DE_DEST_ADDRESS, 4);
+ EMIT_RELOC(etnaviv, bo, 0, TRUE);
+ EMIT(etnaviv, VIVS_DE_DEST_STRIDE_STRIDE(pitch));
+ EMIT(etnaviv, VIVS_DE_DEST_ROTATION_CONFIG_ROTATION_DISABLE);
+ EMIT(etnaviv, dst_cfg);
+ EMIT_ALIGN(etnaviv);
+}
+
+static void etnaviv_emit_rop_clip(struct etnaviv *etnaviv, unsigned fg_rop,
+ unsigned bg_rop, const BoxRec *clip, xPoint offset)
+{
+ EMIT_LOADSTATE(etnaviv, VIVS_DE_ROP, clip ? 3 : 1);
+ EMIT(etnaviv, VIVS_DE_ROP_ROP_FG(fg_rop) |
+ VIVS_DE_ROP_ROP_BG(bg_rop) |
+ VIVS_DE_ROP_TYPE_ROP4);
+ if (clip) {
+ EMIT(etnaviv,
+ VIVS_DE_CLIP_TOP_LEFT_X(clip->x1 + offset.x) |
+ VIVS_DE_CLIP_TOP_LEFT_Y(clip->y1 + offset.y));
+ EMIT(etnaviv,
+ VIVS_DE_CLIP_BOTTOM_RIGHT_X(clip->x2 + offset.x) |
+ VIVS_DE_CLIP_BOTTOM_RIGHT_Y(clip->y2 + offset.y));
+ }
+}
+
+static void etnaviv_emit_brush(struct etnaviv *etnaviv, uint32_t fg)
+{
+ EMIT_LOADSTATE(etnaviv, VIVS_DE_PATTERN_MASK_LOW, 4);
+ EMIT(etnaviv, ~0);
+ EMIT(etnaviv, ~0);
+ EMIT(etnaviv, 0);
+ EMIT(etnaviv, fg);
+ EMIT_ALIGN(etnaviv);
+ EMIT_LOADSTATE(etnaviv, VIVS_DE_PATTERN_CONFIG, 1);
+ EMIT(etnaviv, VIVS_DE_PATTERN_CONFIG_INIT_TRIGGER(3));
+}
+
+static void etnaviv_set_blend(struct etnaviv *etnaviv,
+ const struct etnaviv_blend_op *op)
+{
+ if (!op) {
+ EMIT_LOADSTATE(etnaviv, VIVS_DE_ALPHA_CONTROL, 1);
+ EMIT(etnaviv, VIVS_DE_ALPHA_CONTROL_ENABLE_OFF);
+ } else {
+ Bool pe20 = VIV_FEATURE(etnaviv->conn, chipMinorFeatures0, 2DPE20);
+
+ EMIT_LOADSTATE(etnaviv, VIVS_DE_ALPHA_CONTROL, 2);
+ EMIT(etnaviv,
+ VIVS_DE_ALPHA_CONTROL_ENABLE_ON |
+ VIVS_DE_ALPHA_CONTROL_PE10_GLOBAL_SRC_ALPHA(op->src_alpha) |
+ VIVS_DE_ALPHA_CONTROL_PE10_GLOBAL_DST_ALPHA(op->dst_alpha));
+ EMIT(etnaviv, op->alpha_mode);
+ EMIT_ALIGN(etnaviv);
+
+ if (pe20) {
+ EMIT_LOADSTATE(etnaviv, VIVS_DE_GLOBAL_SRC_COLOR, 3);
+ EMIT(etnaviv, op->src_alpha << 24);
+ EMIT(etnaviv, op->dst_alpha << 24);
+ EMIT(etnaviv,
+ VIVS_DE_COLOR_MULTIPLY_MODES_SRC_PREMULTIPLY_DISABLE |
+ VIVS_DE_COLOR_MULTIPLY_MODES_DST_PREMULTIPLY_DISABLE |
+ VIVS_DE_COLOR_MULTIPLY_MODES_SRC_GLOBAL_PREMULTIPLY_DISABLE |
+ VIVS_DE_COLOR_MULTIPLY_MODES_DST_DEMULTIPLY_DISABLE);
+ }
+ }
+}
+
+static void etnaviv_emit_2d_draw(struct etnaviv *etnaviv, const BoxRec *pbox,
+ size_t n, xPoint offset)
+{
+ size_t i;
+
+ assert(n);
+
+ EMIT_DRAW_2D(etnaviv, n);
+
+ for (i = 0; i < n; i++, pbox++) {
+ EMIT(etnaviv,
+ VIV_FE_DRAW_2D_TOP_LEFT_X(offset.x + pbox->x1) |
+ VIV_FE_DRAW_2D_TOP_LEFT_Y(offset.y + pbox->y1));
+ EMIT(etnaviv,
+ VIV_FE_DRAW_2D_BOTTOM_RIGHT_X(offset.x + pbox->x2) |
+ VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y(offset.y + pbox->y2));
+ }
+}
+
+void etnaviv_de_start(struct etnaviv *etnaviv, const struct etnaviv_de_op *op)
+{
+ BATCH_SETUP_START(etnaviv);
+
+ if (op->src.bo)
+ etnaviv_set_source_bo(etnaviv, op->src.bo, op->src.pitch,
+ op->src.format, &op->src.offset);
+ etnaviv_set_dest_bo(etnaviv, op->dst.bo, op->dst.pitch, op->dst.format,
+ op->cmd);
+ etnaviv_set_blend(etnaviv, op->blend_op);
+ if (op->brush)
+ etnaviv_emit_brush(etnaviv, op->fg_colour);
+ etnaviv_emit_rop_clip(etnaviv, op->rop, op->rop, op->clip,
+ op->dst.offset);
+
+ BATCH_SETUP_END(etnaviv);
+}
+
+void etnaviv_de_end(struct etnaviv *etnaviv)
+{
+ /* Append a flush */
+ EMIT_LOADSTATE(etnaviv, VIVS_GL_FLUSH_CACHE, 1);
+ EMIT(etnaviv, VIVS_GL_FLUSH_CACHE_PE2D);
+
+ etnaviv_emit(etnaviv);
+}
+
+void etnaviv_de_op(struct etnaviv *etnaviv, const struct etnaviv_de_op *op,
+ const BoxRec *pBox, size_t nBox)
+{
+ unsigned int remaining = etnaviv->batch_de_high_watermark -
+ etnaviv->batch_size;
+
+ assert(nBox <= VIVANTE_MAX_2D_RECTS);
+
+ if (2 + 2 * nBox > remaining) {
+ etnaviv_emit(etnaviv);
+ BATCH_OP_START(etnaviv);
+ }
+
+ etnaviv_emit_2d_draw(etnaviv, pBox, nBox, op->dst.offset);
+}
+
+void etnaviv_flush(struct etnaviv *etnaviv)
+{
+ struct etna_ctx *ctx = etnaviv->ctx;
+ etna_set_state(ctx, VIVS_GL_FLUSH_CACHE, VIVS_GL_FLUSH_CACHE_PE2D);
+}
diff --git a/etnaviv/etnaviv_op.h b/etnaviv/etnaviv_op.h
new file mode 100644
index 0000000..45e121f
--- /dev/null
+++ b/etnaviv/etnaviv_op.h
@@ -0,0 +1,76 @@
+#ifndef ETNAVIV_OP_H
+#define ETNAVIV_OP_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdint.h>
+
+#include "xf86.h"
+
+#define VIVANTE_MAX_2D_RECTS 255
+
+struct etna_bo;
+struct etnaviv;
+
+#define UNKNOWN_FORMAT 0x1f
+
+struct etnaviv_format {
+ uint32_t
+ format:5,
+ swizzle:2;
+};
+
+struct etnaviv_blend_op {
+ uint32_t alpha_mode;
+ uint8_t src_alpha;
+ uint8_t dst_alpha;
+};
+
+struct etnaviv_blit_buf {
+ struct etnaviv_format format;
+ struct etnaviv_pixmap *pixmap;
+ struct etna_bo *bo;
+ unsigned pitch;
+ xPoint offset;
+};
+
+#define INIT_BLIT_BUF(_fmt,_pix,_bo,_pitch,_off) \
+ ((struct etnaviv_blit_buf){ \
+ .format = _fmt, \
+ .pixmap = _pix, \
+ .bo = _bo, \
+ .pitch = _pitch, \
+ .offset = _off, \
+ })
+
+#define INIT_BLIT_PIX(_pix, _fmt, _off) \
+ INIT_BLIT_BUF((_fmt), (_pix), (_pix)->etna_bo, (_pix)->pitch, (_off))
+
+#define INIT_BLIT_BO(_bo, _pitch, _fmt, _off) \
+ INIT_BLIT_BUF((_fmt), NULL, (_bo), (_pitch), (_off))
+
+#define INIT_BLIT_NULL \
+ INIT_BLIT_BUF({ }, NULL, NULL, 0, ZERO_OFFSET)
+
+#define ZERO_OFFSET ((xPoint){ 0, 0 })
+
+struct etnaviv_de_op {
+ struct etnaviv_blit_buf dst;
+ struct etnaviv_blit_buf src;
+ const struct etnaviv_blend_op *blend_op;
+ const BoxRec *clip;
+ unsigned rop, cmd;
+ Bool brush;
+ uint32_t fg_colour;
+};
+
+void etnaviv_de_start(struct etnaviv *etnaviv, const struct etnaviv_de_op *op);
+void etnaviv_de_end(struct etnaviv *etnaviv);
+void etnaviv_de_op(struct etnaviv *etnaviv, const struct etnaviv_de_op *op,
+ const BoxRec *pBox, size_t nBox);
+void etnaviv_emit(struct etnaviv *etnaviv);
+void etnaviv_flush(struct etnaviv *etnaviv);
+
+#endif
diff --git a/etnaviv/etnaviv_utils.c b/etnaviv/etnaviv_utils.c
new file mode 100644
index 0000000..cf67e6b
--- /dev/null
+++ b/etnaviv/etnaviv_utils.c
@@ -0,0 +1,395 @@
+/*
+ * Vivante GPU Acceleration Xorg driver
+ *
+ * Written by Russell King, 2012, derived in part from the
+ * Intel xorg X server driver.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef HAVE_DIX_CONFIG_H
+#include "dix-config.h"
+#endif
+#include "fb.h"
+#include "gcstruct.h"
+#include "xf86.h"
+
+#include <armada_bufmgr.h>
+#include <etnaviv/state_2d.xml.h>
+#include "cpu_access.h"
+#include "gal_extension.h"
+#include "pamdump.h"
+#include "pixmaputil.h"
+
+#include "etnaviv_accel.h"
+#include "etnaviv_utils.h"
+#include "etnaviv_compat.h"
+
+static const char *etnaviv_errors[] = {
+ "invalid argument",
+ "invalid object",
+ "out of memory",
+ "memory locked",
+ "memory unlocked",
+ "heap corrupted",
+ "generic IO",
+ "invalid address",
+ "context loss",
+ "too complex",
+ "buffer too small",
+ "interface error",
+ "not supported",
+ "more data",
+ "timeout",
+ "out of resources",
+ "invalid data",
+ "invalid mipmap",
+ "not found",
+ "not aligned",
+ "invalid request",
+ "GPU unresponsive",
+};
+
+const char *etnaviv_strerror(int err)
+{
+ const char *str = NULL;
+
+ if (err < 0) {
+ if (err >= VIV_STATUS_GPU_NOT_RESPONDING)
+ str = etnaviv_errors[-1 - err];
+ }
+ return str;
+}
+
+void __etnaviv_error(struct etnaviv *etnaviv, const char *fn, const char *w, int err)
+{
+ xf86DrvMsg(etnaviv->scrnIndex, X_ERROR,
+ "[etnaviv] %s: %s failed: %s\n", fn, w,
+ etnaviv_strerror(err));
+}
+
+
+/*
+ * Unmap a pixmap from the GPU. Note that we must wait for any outstanding
+ * GPU operations to complete before unmapping the pixmap from the GPU.
+ */
+static void etnaviv_unmap_gpu(struct etnaviv *etnaviv, struct etnaviv_pixmap *vPix)
+{
+#ifdef DEBUG_MAP
+ dbg("Unmapping vPix %p bo %p\n", vPix, vPix->bo);
+#endif
+ etna_bo_del(etnaviv->conn, vPix->etna_bo, NULL);
+ vPix->etna_bo = NULL;
+ vPix->info = 0;
+}
+
+/*
+ * Map a pixmap to the GPU, and mark the GPU as owning this BO.
+ */
+Bool etnaviv_map_gpu(struct etnaviv *etnaviv, struct etnaviv_pixmap *vPix)
+{
+ uint32_t handle;
+
+#ifdef DEBUG_CHECK_DRAWABLE_USE
+ assert(vPix->in_use == 0);
+#endif
+
+ if (vPix->owner == GPU)
+ return TRUE;
+
+ if (vPix->state & ST_DMABUF) {
+ vPix->owner = CPU;
+ return TRUE;
+ }
+
+ /*
+ * If we have a shmem bo from KMS, map it to an etna_bo. This
+ * gives us etna_bo's for everything except the dumb KMS buffers.
+ */
+ if (vPix->bo) {
+ struct drm_armada_bo *bo = vPix->bo;
+ struct etna_bo *etna_bo;
+
+ etna_bo = etna_bo_from_usermem(etnaviv->conn, bo->ptr,
+ bo->size);
+ if (!etna_bo) {
+ xf86DrvMsg(etnaviv->scrnIndex, X_ERROR,
+ "etnaviv: etna_bo_from_usermem(ptr=%p, size=%zu) failed\n", bo->ptr, bo->size);
+ return FALSE;
+ }
+
+ vPix->etna_bo = etna_bo;
+ }
+
+ if (vPix->etna_bo) {
+ struct etna_bo *etna_bo = vPix->etna_bo;
+
+ if (vPix->owner == CPU)
+ etna_bo_cpu_fini(etna_bo);
+ }
+
+ vPix->owner = GPU;
+
+#ifdef DEBUG_MAP
+ dbg("Mapped vPix %p etna bo %p to 0x%08x\n",
+ vPix, vPix->etna_bo, etna_bo_gpu_address(vPix->etna_bo));
+#endif
+
+ /*
+ * This should never happen - if it does, and we proceeed, we will
+ * take the machine out, so assert and kill ourselves instead.
+ */
+ handle = etna_bo_gpu_address(vPix->etna_bo);
+ assert(handle != 0 && handle != -1);
+
+ return TRUE;
+}
+
+/*
+ * Finish a bo for CPU access. NULL out the fb layer's pixmap data
+ * pointer to ensure any further unprotected accesses get caught.
+ */
+void finish_cpu_drawable(DrawablePtr pDrawable, int access)
+{
+ PixmapPtr pixmap = drawable_pixmap(pDrawable);
+ struct etnaviv_pixmap *vPix = etnaviv_get_pixmap_priv(pixmap);
+
+ if (vPix) {
+#ifdef DEBUG_CHECK_DRAWABLE_USE
+ vPix->in_use--;
+#endif
+ if (!(vPix->state & ST_DMABUF))
+ pixmap->devPrivate.ptr = NULL;
+ }
+}
+
+/*
+ * Prepare a bo for CPU access. If the GPU has been accessing the
+ * pixmap data, we need to unmap the buffer from the GPU to ensure
+ * that our view is up to date.
+ */
+void prepare_cpu_drawable(DrawablePtr pDrawable, int access)
+{
+ PixmapPtr pixmap = drawable_pixmap(pDrawable);
+ struct etnaviv_pixmap *vPix = etnaviv_get_pixmap_priv(pixmap);
+
+ if (vPix) {
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDrawable->pScreen);
+
+ /* Ensure that the drawable is up to date with all GPU operations */
+ etnaviv_batch_wait_commit(etnaviv, vPix);
+
+ if (!(vPix->state & ST_DMABUF)) {
+ if (vPix->bo) {
+ /*
+ * If we have an etna_bo, it means we're mapped on
+ * the GPU (via etnaviv_map_gpu above).
+ */
+ if (vPix->etna_bo)
+ etnaviv_unmap_gpu(etnaviv, vPix);
+
+ pixmap->devPrivate.ptr = vPix->bo->ptr;
+#ifdef DEBUG_MAP
+ dbg("Pixmap %p bo %p to %p\n", pixmap, vPix->bo,
+ pixmap->devPrivate.ptr);
+#endif
+ } else if (vPix->etna_bo) {
+ struct etna_bo *etna_bo = vPix->etna_bo;
+
+ if (vPix->owner == GPU)
+ etna_bo_cpu_prep(etna_bo, NULL, DRM_ETNA_PREP_WRITE);
+
+ pixmap->devPrivate.ptr = etna_bo_map(etna_bo);
+#ifdef DEBUG_MAP
+ dbg("Pixmap %p etnabo %p to %p\n", pixmap,
+ etna_bo, pixmap->devPrivate.ptr);
+#endif
+ }
+ }
+#ifdef DEBUG_CHECK_DRAWABLE_USE
+ vPix->in_use++;
+#endif
+ vPix->owner = CPU;
+ }
+}
+
+#ifdef RENDER
+struct etnaviv_format etnaviv_pict_format(PictFormatShort format, Bool force)
+{
+ switch (format) {
+#define DE_FORMAT_UNKNOWN UNKNOWN_FORMAT
+#define C(pf,vf,af,sw) case PICT_##pf: \
+ return (struct etnaviv_format){ \
+ .format = force ? DE_FORMAT_##af : DE_FORMAT_##vf, \
+ .swizzle = DE_SWIZZLE_##sw, \
+ }
+
+ C(a8r8g8b8, A8R8G8B8, A8R8G8B8, ARGB);
+ C(x8r8g8b8, X8R8G8B8, A8R8G8B8, ARGB);
+ C(a8b8g8r8, A8R8G8B8, A8R8G8B8, ABGR);
+ C(x8b8g8r8, X8R8G8B8, A8R8G8B8, ABGR);
+ C(b8g8r8a8, A8R8G8B8, A8R8G8B8, BGRA);
+ C(b8g8r8x8, X8R8G8B8, A8R8G8B8, BGRA);
+ C(r5g6b5, R5G6B5, UNKNOWN, ARGB);
+ C(b5g6r5, R5G6B5, UNKNOWN, ABGR);
+ C(a1r5g5b5, A1R5G5B5, A1R5G5B5, ARGB);
+ C(x1r5g5b5, X1R5G5B5, A1R5G5B5, ARGB);
+ C(a1b5g5r5, A1R5G5B5, A1R5G5B5, ABGR);
+ C(x1b5g5r5, X1R5G5B5, A1R5G5B5, ABGR);
+ C(a4r4g4b4, A4R4G4B4, A4R4G4B4, ARGB);
+ C(x4r4g4b4, X4R4G4B4, A4R4G4B4, ARGB);
+ C(a4b4g4r4, A4R4G4B4, A4R4G4B4, ABGR);
+ C(x4b4g4r4, X4R4G4B4, A4R4G4B4, ABGR);
+ C(a8, A8, A8, ARGB);
+ C(c8, INDEX8, INDEX8, ARGB);
+
+/* The remainder we don't support */
+// C(r8g8b8, R8G8B8, UNKNOWN, ARGB);
+// C(b8g8r8, R8G8B8, UNKNOWN, ABGR);
+// C(r3g3b2, R3G3B2, UNKNOWN);
+// C(b2g3r3, UNKNOWN, UNKNOWN);
+// C(a2r2g2b2, A2R2G2B2, A2R2G2B2);
+// C(a2b2g2r2, UNKNOWN, A2R2G2B2);
+// C(g8, L8, UNKNOWN);
+// C(x4a4, UNKNOWN, UNKNOWN);
+// C(x4c4, UNKNOWN, UNKNOWN); /* same value as c8 */
+// C(x4g4, UNKNOWN, UNKNOWN); /* same value as g8 */
+// C(a4, A4, A4);
+// C(r1g2b1, UNKNOWN, UNKNOWN);
+// C(b1g2r1, UNKNOWN, UNKNOWN);
+// C(a1r1g1b1, UNKNOWN, UNKNOWN);
+// C(a1b1g1r1, UNKNOWN, UNKNOWN);
+// C(c4, INDEX4, UNKNOWN);
+// C(g4, L4, UNKNOWN);
+// C(a1, A1, A1);
+// C(g1, L1, UNKNOWN);
+ default:
+ break;
+ }
+ return (struct etnaviv_format){ .format = UNKNOWN_FORMAT, .swizzle = 0 };
+#undef C
+}
+#endif
+
+Bool etnaviv_src_format_valid(struct etnaviv *etnaviv,
+ struct etnaviv_format fmt)
+{
+ if (fmt.format == DE_FORMAT_YV12 &&
+ !VIV_FEATURE(etnaviv->conn, chipFeatures, YUV420_SCALER))
+ return FALSE;
+ if ((fmt.format >= 16 || fmt.swizzle) &&
+ !VIV_FEATURE(etnaviv->conn, chipMinorFeatures0, 2DPE20))
+ return FALSE;
+ return fmt.format != UNKNOWN_FORMAT;
+}
+
+Bool etnaviv_dst_format_valid(struct etnaviv *etnaviv,
+ struct etnaviv_format fmt)
+{
+ /* Don't permit BGRA or RGBA formats on PE1.0 */
+ if (fmt.swizzle &&
+ !VIV_FEATURE(etnaviv->conn, chipMinorFeatures0, 2DPE20))
+ return FALSE;
+ return fmt.format != UNKNOWN_FORMAT;
+}
+
+#if 1 //def DEBUG
+static void dump_pix(struct etnaviv *etnaviv, struct etnaviv_pixmap *vPix,
+ bool alpha, int x1, int y1, int x2, int y2,
+ const char *fmt, va_list ap)
+ __attribute__((__format__(__printf__, 8, 0)));
+static void dump_pix(struct etnaviv *etnaviv, struct etnaviv_pixmap *vPix,
+ bool alpha, int x1, int y1, int x2, int y2,
+ const char *fmt, va_list ap)
+{
+ static int idx;
+ unsigned owner = vPix->owner;
+ unsigned state = vPix->state;
+ const uint32_t *ptr;
+ char n[80];
+
+ if (state & ST_DMABUF) {
+ /* Can't dump ST_DMABUF pixmaps */
+ return;
+ } else if (vPix->bo) {
+ ptr = vPix->bo->ptr;
+ } else {
+ ptr = etna_bo_map(vPix->etna_bo);
+ owner = CPU;
+ }
+
+ if (owner == GPU) {
+ etnaviv_unmap_gpu(etnaviv, vPix);
+ vPix->owner = CPU;
+ }
+
+ vsprintf(n, fmt, ap);
+
+ dump_pam(ptr, vPix->pitch, alpha, x1, y1, x2, y2,
+ "/tmp/X.%04u.%s-%u.%u.%u.%u.pam",
+ idx++, n, x1, y1, x2, y2);
+
+ if (owner == GPU)
+ etnaviv_map_gpu(etnaviv, vPix);
+}
+
+void dump_Drawable(DrawablePtr pDraw, const char *fmt, ...)
+{
+ struct etnaviv *etnaviv = etnaviv_get_screen_priv(pDraw->pScreen);
+ xPoint offset;
+ struct etnaviv_pixmap *vPix = etnaviv_drawable_offset(pDraw, &offset);
+ va_list ap;
+
+ if (!vPix)
+ return;
+
+ va_start(ap, fmt);
+ dump_pix(etnaviv, vPix, 0,
+ pDraw->x + offset.x, pDraw->y + offset.y,
+ pDraw->width, pDraw->height, fmt, ap);
+ va_end(ap);
+}
+
+void dump_Picture(PicturePtr pDst, const char *fmt, ...)
+{
+ DrawablePtr pDraw = pDst->pDrawable;
+ struct etnaviv *etnaviv;
+ struct etnaviv_pixmap *vPix;
+ xPoint offset;
+ bool alpha;
+ va_list ap;
+
+ if (!pDraw)
+ return;
+
+ etnaviv = etnaviv_get_screen_priv(pDraw->pScreen);
+ vPix = etnaviv_drawable_offset(pDraw, &offset);
+ if (!vPix)
+ return;
+
+ alpha = PICT_FORMAT_A(pDst->format) != 0;
+
+ va_start(ap, fmt);
+ dump_pix(etnaviv, vPix, alpha,
+ pDraw->x + offset.x, pDraw->y + offset.y,
+ pDraw->width, pDraw->height, fmt, ap);
+ va_end(ap);
+}
+
+void dump_vPix(struct etnaviv *etnaviv, struct etnaviv_pixmap *vPix,
+ int alpha, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ dump_pix(etnaviv, vPix, !!alpha,
+ 0, 0, vPix->width, vPix->height,
+ fmt, ap);
+ va_end(ap);
+}
+#endif
diff --git a/etnaviv/etnaviv_utils.h b/etnaviv/etnaviv_utils.h
new file mode 100644
index 0000000..940f127
--- /dev/null
+++ b/etnaviv/etnaviv_utils.h
@@ -0,0 +1,43 @@
+/*
+ * Vivante GPU Acceleration Xorg driver
+ *
+ * Written by Russell King, 2012, derived in part from the
+ * Intel xorg X server driver.
+ */
+#ifndef VIVANTE_UTILS_H
+#define VIVANTE_UTILS_H
+
+#include "utils.h"
+
+#include <etnaviv/viv.h>
+
+struct etnaviv;
+struct etnaviv_pixmap;
+
+const char *etnaviv_strerror(int err);
+#define etnaviv_error(v,w,e) __etnaviv_error(v,__func__,w,e)
+void __etnaviv_error(struct etnaviv *, const char *, const char *, int);
+
+Bool etnaviv_map_gpu(struct etnaviv *etnaviv, struct etnaviv_pixmap *vPix);
+
+struct etnaviv_format etnaviv_pict_format(PictFormatShort format, Bool force);
+Bool etnaviv_src_format_valid(struct etnaviv *, struct etnaviv_format fmt);
+Bool etnaviv_dst_format_valid(struct etnaviv *, struct etnaviv_format fmt);
+
+void dump_Drawable(DrawablePtr pDraw, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+void dump_Picture(PicturePtr pDst, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+void dump_vPix(struct etnaviv *etnaviv, struct etnaviv_pixmap *vPix,
+ int alpha, const char *fmt, ...)
+ __attribute__((__format__(__printf__, 4, 5)));
+
+static inline unsigned int etnaviv_pitch(unsigned width, unsigned bpp)
+{
+ unsigned pitch = bpp != 4 ? width * ((bpp + 7) / 8) : width / 2;
+
+ /* GC320 and GC600 needs pitch aligned to 16 */
+ return ALIGN(pitch, 16);
+}
+
+#endif
diff --git a/src/armada_drm.c b/src/armada_drm.c
index 40ae87a..915b25a 100644
--- a/src/armada_drm.c
+++ b/src/armada_drm.c
@@ -509,6 +509,9 @@ static Bool armada_drm_load_accel(ScrnInfoPtr pScrn,
}
static const char *armada_drm_accelerators[] = {
+#ifdef HAVE_ACCEL_ETNAVIV
+ "etnaviv_gpu",
+#endif
#ifdef HAVE_ACCEL_GALCORE
"vivante_gpu",
#endif