summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk_cubox@arm.linux.org.uk>2013-05-23 00:40:18 +0100
committerRussell King <rmk@arm.linux.org.uk>2013-06-14 13:07:17 +0100
commit204ea08a1598909ae05c217e1e7ca709776efbde (patch)
tree326c2fd3d998707e83365ac5f156c98f0fff3956
downloadxf86-video-armada-204ea08a1598909ae05c217e1e7ca709776efbde.tar.gz
xf86-video-armada-204ea08a1598909ae05c217e1e7ca709776efbde.tar.xz
Initial checkin of Marvell Armada xorg driver
Signed-off-by: Russell King <rmk_cubox@arm.linux.org.uk>
-rw-r--r--.gitignore29
-rw-r--r--Makefile.am22
-rwxr-xr-xautogen.sh16
-rw-r--r--configure.ac146
-rw-r--r--debian/changelog5
-rw-r--r--debian/compat1
-rw-r--r--debian/control54
-rwxr-xr-xdebian/rules21
-rw-r--r--debian/xserver-xorg-video-armada.install2
-rw-r--r--man/Makefile.am59
-rw-r--r--man/armada.man84
-rw-r--r--src/Makefile.am34
-rw-r--r--src/armada_drm.c1529
-rw-r--r--src/armada_drm.h73
-rw-r--r--src/armada_drm_xv.c907
-rw-r--r--src/armada_ioctl.h138
-rw-r--r--src/armada_module.c138
-rw-r--r--src/vivante.c661
-rw-r--r--src/vivante.h18
-rw-r--r--src/vivante_accel.c1619
-rw-r--r--src/vivante_accel.h167
-rw-r--r--src/vivante_compat.h26
-rw-r--r--src/vivante_dri2.c751
-rw-r--r--src/vivante_dri2.h15
-rw-r--r--src/vivante_unaccel.c237
-rw-r--r--src/vivante_unaccel.h66
-rw-r--r--src/vivante_unaccel_render.c231
-rw-r--r--src/vivante_utils.c352
-rw-r--r--src/vivante_utils.h80
29 files changed, 7481 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2d3b1b9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+.deps
+.libs
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+build-aux
+config.h
+config.h.in
+config.log
+config.status
+configure
+debian/autoreconf.*
+debian/files
+debian/tmp
+debian/*.log
+debian/*.substvars
+debian/xserver-xorg-video-armada-dbg
+debian/xserver-xorg-video-armada
+libtool
+m4
+*.4
+*.la
+*.lo
+*.orig
+*.swp
+*~
+\#*#
+stamp-*
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..933c55a
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,22 @@
+# Copyright 2005 Adam Jackson.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# on the rights to use, copy, modify, merge, publish, distribute, sub
+# license, and/or sell copies of the Software, and to permit persons to whom
+# the Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+# ADAM JACKSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = src man
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..c017353
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,16 @@
+#! /bin/sh
+#
+# Run the various GNU autotools to bootstrap the build
+# system. Should only need to be done once.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+autoreconf --install || exit 1
+
+cd $ORIGDIR || exit $?
+
+$srcdir/configure --enable-maintainer-mode "$@"
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..545ed84
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,146 @@
+# Copyright 2005 Adam Jackson.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# on the rights to use, copy, modify, merge, publish, distribute, sub
+# license, and/or sell copies of the Software, and to permit persons to whom
+# the Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+# ADAM JACKSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Process this file with autoconf to produce a configure script
+
+AC_PREREQ(2.57)
+AC_INIT([xf86-video-armada],
+ [0.0.0],
+ [],
+ [xf86-video-armada])
+
+DRIVER_NAME=armada
+
+AC_CONFIG_SRCDIR([Makefile.am])
+AC_CONFIG_HEADER([config.h])
+AC_CONFIG_AUX_DIR(build-aux)
+AC_CONFIG_MACRO_DIR([m4])
+
+AM_INIT_AUTOMAKE([foreign dist-bzip2])
+AM_MAINTAINER_MODE
+
+# Require X.Org macros 1.8 or later for MAN_SUBSTS set by XORG_MANPAGE_SECTIONS
+m4_ifndef([XORG_MACROS_VERSION],
+ [m4_fatal([must install xorg-macros 1.8 or later before running autoconf/autogen.
+ Hint: either install from source, git://anongit.freedesktop.org/xorg/util/macros or,
+ depending on you distribution, try package 'xutils-dev' or 'xorg-x11-util-macros'])])
+
+XORG_MACROS_VERSION(1.8)
+XORG_DEFAULT_OPTIONS
+
+# Require X.Org server macros (i.e. XORG_DRIVER_CHECK_EXT) to check fori
+# required modules
+m4_ifndef([XORG_DRIVER_CHECK_EXT],
+ [m4_fatal([must install xorg-server macros before running autoconf/autogen.
+ Hint: either install from source, git://anongit.freedesktop.org/xorg/xserver or,
+ depending on your distribution, try package 'xserver-xorg-dev' or 'xorg-x11-server-devel'])])
+
+# Checks for programs.
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+AC_PROG_CC
+
+PKG_CHECK_MODULES(UDEV, [libudev], [udev=yes], [udev=no])
+if test x"$udev" = xyes; then
+ AC_DEFINE(HAVE_UDEV,1,[Enable udev-based monitor hotplug detection])
+fi
+
+
+AH_TOP([#include "xorg-server.h"])
+
+AC_ARG_WITH(xorg-module-dir,
+ AC_HELP_STRING([--with-xorg-module-dir=DIR],
+ [Default xorg module directory [[default=$libdir/xorg/modules]]]),
+ [moduledir="$withval"],
+ [moduledir="$libdir/xorg/modules"])
+
+AC_ARG_WITH(libgal-include,
+ AC_HELP_STRING([--with-libgal-include=PATH],
+ [specify directory for installed libGAL include files [[default=/usr/local/include/HAL]]]),
+ [libgal_include="$withval"],
+ [libgal_include="/usr/local/include/HAL"])
+
+AC_ARG_WITH(libgal-lib,
+ AC_HELP_STRING([--with-libgal-lib=PATH],
+ [specify directory for installed libGAL library [[default=/usr/local/lib]]]),
+ [libgal_lib="$withval"],
+ [libgal_lib="/usr/local/lib"])
+
+LIBGAL_CFLAGS="-I$libgal_include"
+LIBGAL_LIBS="-L$libgal_lib -lGAL"
+
+saved_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS $LIBGAL_CFLAGS"
+AC_CHECK_HEADERS([gc_hal.h gc_hal_base.h gc_hal_enum.h],
+ [],
+ [AC_MSG_ERROR([required libGAL header file missing])])
+AC_CHECK_HEADERS([gc_hal_raster.h],
+ [],
+ [AC_MSG_ERROR([required libGAL header file missing])],
+ [#include <gc_hal.h>])
+CPPFLAGS="$saved_CPPFLAGS"
+
+AC_ARG_ENABLE(dri2, AC_HELP_STRING([--disable-dri2],
+ [Disable DRI support [[default=auto]]]),
+ [DRI2="$enableval"],
+ [DRI2=auto])
+
+# Checks for pkg-config packages
+PKG_CHECK_MODULES(XORG, [xorg-server >= 1.9.99.1 xproto fontsproto])
+sdkdir=$(pkg-config --variable=sdkdir xorg-server)
+
+# Checks for required extensions
+XORG_DRIVER_CHECK_EXT(RANDR, randrproto)
+XORG_DRIVER_CHECK_EXT(RENDER, renderproto)
+XORG_DRIVER_CHECK_EXT(XV, videoproto)
+
+# Obtain compilter/linker options for the driver dependencies
+PKG_CHECK_MODULES(DRM, [libdrm >= 2.4.38])
+PKG_CHECK_MODULES(DRMARMADA, [libdrm_armada >= 2.0.0])
+PKG_CHECK_MODULES(DRI2, [dri2proto >= 2.6], , DRI2=no)
+
+# Check those options requiring DRM support
+if test x$DRI2 != xno; then
+ save_CFLAGS=$CFLAGS
+ CFLAGS="$XORG_CFLAGS $DRM_CFLAGS $DRI_CFLAGS $DRI2_CFLAGS"
+ AC_CHECK_HEADERS([dri2.h], DRI2=yes, DRI2=no, [#include <dixstruct.h>])
+ CFLAGS=$save_CFLAGS
+fi
+AC_MSG_CHECKING([whether to include DRI2 support])
+AM_CONDITIONAL(HAVE_DRI2, test x$DRI2 = xyes)
+AC_MSG_RESULT([$DRI2])
+if test x$DRI2 = xyes; then
+ AC_DEFINE(HAVE_DRI2,1,[Enable DRI2 driver support])
+fi
+
+# Checks for header files.
+AC_HEADER_STDC
+
+AC_SUBST([XORG_CFLAGS])
+AC_SUBST([LIBGAL_CFLAGS])
+AC_SUBST([LIBGAL_LIBS])
+AC_SUBST([DRIVER_NAME])
+AC_SUBST([moduledir])
+
+AC_OUTPUT([
+ Makefile
+ src/Makefile
+ man/Makefile
+])
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..7b5b09c
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+xf86-video-armada (0.0.0-1) precise; urgency=low
+
+ * Initial release
+
+ -- Russell King <rmk_cubox@arm.linux.org.uk> Wed, 22 May 2013 22:59:30 +0100
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..9804337
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,54 @@
+Source: xf86-video-armada
+Section: x11
+Priority: optional
+Maintainer: Russell King <rmk+cubox@arm.linux.org.uk>
+Build-Depends:
+ debhelper (>= 8.1.2ubuntu2),
+ dh-autoreconf,
+ pkg-config,
+ xserver-xorg-dev (>= 2:1.11.4),
+ x11proto-core-dev,
+ x11proto-fonts-dev,
+ x11proto-randr-dev,
+ x11proto-render-dev,
+ x11proto-video-dev,
+ libdrm-dev (>= 2.4.23),
+ libudev-dev [linux-any],
+ dpkg-dev (>= 1.14.17),
+ automake,
+ libtool,
+ xutils-dev
+Standards-Version: 3.9.2
+
+Package: xserver-xorg-video-armada
+Architecture: armel armhf
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ ${xviddriver:Depends}
+Provides:
+ ${xviddriver:Provides}
+Description: Xorg X server -- Armada display driver
+ This package provides the driver for the Marvell Armada LCD controllers
+ with Vivante GCxxx acceleration.
+ .
+ More information about X.Org can be found at:
+ <URL:http://www.X.org>
+ <URL:http://xorg.freedesktop.org>
+ <URL:http://lists.freedesktop.org/mailman/listinfo/xorg>
+ .
+ This package is built from the X.org xf86-video-armada driver module.
+
+Package: xserver-xorg-video-armada-dbg
+Section: debug
+Priority: extra
+Architecture: linux-any
+Depends:
+ ${misc:Depends},
+Multi-Arch: same
+Description: Xorg X server -- Armada display driver
+ This package provides the driver for the Marvell Armada LCD controllers
+ with Vivante GCxxx acceleration.
+ .
+ This package provides debugging symbols for the xserver-xorg-video-armada
+ package.
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..9566f7e
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,21 @@
+#!/usr/bin/make -f
+# debian/rules for the Debian xserver-xorg-driver-armada package.
+
+DEB_HOST_MULTIARCH=
+
+override_dh_auto_install:
+ dh_auto_install --destdir=debian/tmp
+
+override_dh_install:
+ find debian/tmp -name '*.la' -delete
+ dh_install --fail-missing
+
+override_dh_strip:
+ dh_strip --dbg-package=xserver-xorg-video-armada-dbg
+ dh_strip -s --remaining-packages
+
+override_dh_shlibdeps:
+ dh_shlibdeps -- --warnings=6
+
+%:
+ dh $@ --with autoreconf,xsf --builddirectory=build/
diff --git a/debian/xserver-xorg-video-armada.install b/debian/xserver-xorg-video-armada.install
new file mode 100644
index 0000000..0596acd
--- /dev/null
+++ b/debian/xserver-xorg-video-armada.install
@@ -0,0 +1,2 @@
+usr/lib/xorg/modules/drivers/*.so
+usr/share/man
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644
index 0000000..203490e
--- /dev/null
+++ b/man/Makefile.am
@@ -0,0 +1,59 @@
+# $Id: Makefile.am,v 1.1 2009/01/08 02:25:52 tarang Exp $
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation.
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the copyright holders shall
+# not be used in advertising or otherwise to promote the sale, use or
+# other dealings in this Software without prior written authorization
+# from the copyright holders.
+#
+
+drivermandir = $(DRIVER_MAN_DIR)
+
+driverman_PRE = @DRIVER_NAME@.man
+
+driverman_DATA = $(driverman_PRE:man=@DRIVER_MAN_SUFFIX@)
+
+EXTRA_DIST = @DRIVER_NAME@.man
+
+CLEANFILES = $(driverman_DATA)
+
+SED = sed
+
+# Strings to replace in man pages
+XORGRELSTRING = @PACKAGE_STRING@
+ XORGMANNAME = X Version 11
+
+MAN_SUBSTS = \
+ -e 's|__vendorversion__|"$(XORGRELSTRING)" "$(XORGMANNAME)"|' \
+ -e 's|__xorgversion__|"$(XORGRELSTRING)" "$(XORGMANNAME)"|' \
+ -e 's|__xservername__|Xorg|g' \
+ -e 's|__xconfigfile__|xorg.conf|g' \
+ -e 's|__projectroot__|$(prefix)|g' \
+ -e 's|__appmansuffix__|$(APP_MAN_SUFFIX)|g' \
+ -e 's|__drivermansuffix__|$(DRIVER_MAN_SUFFIX)|g' \
+ -e 's|__adminmansuffix__|$(ADMIN_MAN_SUFFIX)|g' \
+ -e 's|__miscmansuffix__|$(MISC_MAN_SUFFIX)|g' \
+ -e 's|__filemansuffix__|$(FILE_MAN_SUFFIX)|g'
+
+SUFFIXES = .$(DRIVER_MAN_SUFFIX) .man
+
+.man.$(DRIVER_MAN_SUFFIX):
+ sed $(MAN_SUBSTS) < $< > $@
diff --git a/man/armada.man b/man/armada.man
new file mode 100644
index 0000000..8764ee8
--- /dev/null
+++ b/man/armada.man
@@ -0,0 +1,84 @@
+.\" $XFree86: xc/programs/Xserver/hw/xfree86/drivers/fbdev/fbdev.man,v 1.2 2001/01/27 18:20:47 dawes Exp $
+.\" shorthand for double quote that works everywhere.
+.ds q \N'34'
+.TH ARMADA __drivermansuffix__ __vendorversion__
+.SH NAME
+armada \- Marvell Armada 510 Xorg driver
+.SH SYNOPSIS
+.nf
+.B "Section \*qDevice\*q"
+.BI " Identifier \*q" devname \*q
+.B " Driver \*qarmada\*q"
+.BI " BusID \*qplatform:armada-drm:" id \*q
+\ \ ...
+.B EndSection
+.fi
+.SH DESCRIPTION
+.B armada
+is an __xservername__ driver for Marvell Armada SoCs. This is an accelerated
+driver, the following framebuffer depths are supported: 8, 15, 16, 24.
+All visual types are supported for depth 8, and TrueColor visual is
+supported for the other depths. The driver has preliminary support for
+the Direct Rendering Infrastructure (DRI). Multi-head configurations are
+supported.
+.PP
+Support for video overlay is provided, supporting I420, YV12, I422, YUY2,
+UYVY, VYUY and YV16 formats. The common I420 and YV12 formats are
+converted on the fly by this driver to I422 and YV16 for the hardware.
+Support is also provided for video overlay using an XVBO format, which
+can be used to pass a drm buffer handle to the video overlay backend, as
+well as Marvell's special
+.B libbmm
+based method.
+.SH SUPPORTED HARDWARE
+The
+.B armada
+driver currently supports the Marvell Armada 510 LCD controller(s).
+.PP
+.B armada
+communicates with the Linux Kernel Direct Rendering Manager (drm) layer
+to control and arbitrate access to the hardware. Kernel drm support is
+required.
+.SH CONFIGURATION DETAILS
+Please refer to __xconfigfile__(__filemansuffix__) for general configuration
+details. This section only covers configuration details specific to
+this driver.
+.PP
+For this driver it is not required to specify modes in the screen
+section of the config file. The
+.B armada
+driver will request video mode information from the Linux Kernel drm layer.
+.PP
+The VideoRAM option is ignored by this driver; video memory is managed
+by the Linux Kernel drm layer using system memory.
+.PP
+The following driver
+.B Options
+are supported:
+.TP
+.BI "Option \*qHotplug\*q \*q" boolean \*q
+This option controls whether the driver automatically notifies when
+monitors are connected or disconnected.
+.IP
+Default: enabled.
+.TP
+.BI "Option \*qHWcursor\*q \*q" boolean \*q
+Enable or disable use of the hardware cursor.
+.IP
+Default: disabled.
+.TP
+.BI "Option \*qUseGPU\*q \*q" boolean \*q
+Enable or disable use of the Vivante GPU for acceleration.
+.IP
+Default: enabled.
+.TP
+.BI "Option \*qXvAccel\*q \*q" boolean \*q
+Enable or disable the X Video backend.
+.IP
+Default: enabled.
+.TP
+.SH "SEE ALSO"
+__xservername__(__appmansuffix__), __xconfigfile__(__filemansuffix__), xorgconfig(__appmansuffix__), Xserver(__appmansuffix__),
+X(__miscmansuffix__)
+.SH AUTHORS
+Authors include: Gerd Knorr, Michel Dänzer, Geert Uytterhoeven, Russell King.
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..b51f3cd
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,34 @@
+#
+# 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) $(LIBGAL_CFLAGS) $(DRMARMADA_CFLAGS) $(UDEV_CFLAGS)
+armada_drv_la_LTLIBRARIES = armada_drv.la
+armada_drv_la_LDFLAGS = -module -avoid-version
+armada_drv_la_LIBADD = $(LIBGAL_LIBS) $(DRMARMADA_LIBS) $(UDEV_LIBS)
+armada_drv_ladir = @moduledir@/drivers
+armada_drv_la_SOURCES = armada_module.c \
+ armada_drm.c \
+ armada_drm_xv.c \
+ vivante.c \
+ vivante_accel.c \
+ vivante_unaccel.c \
+ vivante_unaccel_render.c \
+ vivante_utils.c
+if HAVE_DRI2
+armada_drv_la_SOURCES += vivante_dri2.c
+armada_drv_la_LIBADD += $(DRI_LIBS)
+endif
diff --git a/src/armada_drm.c b/src/armada_drm.c
new file mode 100644
index 0000000..2ba77ac
--- /dev/null
+++ b/src/armada_drm.c
@@ -0,0 +1,1529 @@
+/*
+ * Marvell Armada DRM-based 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 <stdint.h>
+#include <stdlib.h>
+
+#include <armada_bufmgr.h>
+
+#include "armada_drm.h"
+#include "xf86_OSproc.h"
+#include "xf86Crtc.h"
+#include "xf86cmap.h"
+#include "fb.h"
+#include "mibstore.h"
+#include "micmap.h"
+#include <xf86DDC.h>
+#include <X11/extensions/dpmsconst.h>
+#include <X11/Xatom.h>
+
+#ifdef HAVE_UDEV
+#include <libudev.h>
+#endif
+
+#include "vivante.h"
+#include "vivante_dri2.h"
+
+#define CURSOR_MAX_WIDTH 64
+#define CURSOR_MAX_HEIGHT 32
+
+#define DRM_MODULE_NAME "armada-drm"
+#define DRM_DEFAULT_BUS_ID "platform:armada-drm:00"
+
+const OptionInfoRec armada_drm_options[] = {
+ { OPTION_HW_CURSOR, "HWcursor", OPTV_BOOLEAN, {0}, FALSE },
+ { OPTION_XV_ACCEL, "XvAccel", OPTV_BOOLEAN, {0}, FALSE },
+ { OPTION_USE_GPU, "UseGPU", OPTV_BOOLEAN, {0}, FALSE },
+ { OPTION_HOTPLUG, "HotPlug", OPTV_BOOLEAN, {0}, TRUE },
+ { -1, NULL, OPTV_NONE, {0}, FALSE }
+};
+
+
+struct armada_property {
+ drmModePropertyPtr mode_prop;
+ uint64_t value;
+ int natoms;
+ Atom *atoms;
+};
+
+struct armada_conn_info {
+ struct armada_drm_info *drm;
+ drmModeConnectorPtr mode_output;
+ drmModeEncoderPtr mode_encoder;
+ int id;
+ int dpms_mode;
+ int nprops;
+ struct armada_property *props;
+};
+
+static void armada_drm_ModifyScreenPixmap(ScreenPtr pScreen,
+ struct armada_drm_info *drm, int width, int height, int depth, int bpp,
+ struct drm_armada_bo *bo)
+{
+ PixmapPtr pixmap = pScreen->GetScreenPixmap(pScreen);
+
+ pScreen->ModifyPixmapHeader(pixmap, width, height, depth, bpp,
+ bo->pitch, bo->ptr);
+ if (drm->accel)
+ vivante_set_pixmap_bo(pixmap, bo);
+}
+
+static void drmmode_ConvertToKMode(drmModeModeInfoPtr kmode, DisplayModePtr mode)
+{
+ memset(kmode, 0, sizeof(*kmode));
+
+ kmode->clock = mode->Clock;
+ kmode->hdisplay = mode->HDisplay;
+ kmode->hsync_start = mode->HSyncStart;
+ kmode->hsync_end = mode->HSyncEnd;
+ kmode->htotal = mode->HTotal;
+ kmode->hskew = mode->HSkew;
+ kmode->vdisplay = mode->VDisplay;
+ kmode->vsync_start = mode->VSyncStart;
+ kmode->vsync_end = mode->VSyncEnd;
+ kmode->vtotal = mode->VTotal;
+ kmode->vscan = mode->VScan;
+ kmode->flags = mode->Flags;
+ if (mode->name)
+ strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
+ kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+}
+
+static void drmmode_ConvertFromKMode(ScrnInfoPtr pScrn,
+ drmModeModeInfoPtr kmode, DisplayModePtr mode)
+{
+ memset(mode, 0, sizeof(*mode));
+
+ mode->status = MODE_OK;
+ mode->Clock = kmode->clock;
+ mode->HDisplay = kmode->hdisplay;
+ mode->HSyncStart = kmode->hsync_start;
+ mode->HSyncEnd = kmode->hsync_end;
+ mode->HTotal = kmode->htotal;
+ mode->HSkew = kmode->hskew;
+ mode->VDisplay = kmode->vdisplay;
+ mode->VSyncStart = kmode->vsync_start;
+ mode->VSyncEnd = kmode->vsync_end;
+ mode->VTotal = kmode->vtotal;
+ mode->VScan = kmode->vscan;
+ mode->Flags = kmode->flags;
+ mode->name = strdup(kmode->name);
+ if (kmode->type & DRM_MODE_TYPE_DRIVER)
+ mode->type = M_T_DRIVER;
+ if (kmode->type & DRM_MODE_TYPE_PREFERRED)
+ mode->type |= M_T_PREFERRED;
+ xf86SetModeCrtc (mode, pScrn->adjustFlags);
+}
+
+static struct drm_armada_bo *armada_bo_alloc_framebuffer(ScrnInfoPtr pScrn,
+ struct armada_drm_info *drm, int width, int height, int bpp)
+{
+ struct drm_armada_bo *bo;
+ int ret;
+
+ bo = drm_armada_bo_dumb_create(drm->bufmgr, width, height, bpp);
+ if (!bo) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to allocate new bo: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ ret = drm_armada_bo_map(bo);
+ if (ret) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to map fb bo: %s\n", strerror(errno));
+ drm_armada_bo_put(bo);
+ return NULL;
+ }
+
+// xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+// "Allocated new frame buffer %dx%d stride %d\n",
+// width, height, bo->pitch);
+
+ return bo;
+}
+
+static drmModePropertyPtr armada_drm_conn_find_property(
+ struct armada_conn_info *conn, const char *name, uint32_t *blob)
+{
+ drmModeConnectorPtr koutput = conn->mode_output;
+ struct armada_drm_info *drm = conn->drm;
+ int i;
+
+ for (i = 0; i < koutput->count_props; i++) {
+ drmModePropertyPtr p;
+
+ p = drmModeGetProperty(drm->fd, koutput->props[i]);
+ if (!p || (blob && !(p->flags & DRM_MODE_PROP_BLOB)))
+ continue;
+
+ if (!strcmp(p->name, name)) {
+ if (blob)
+ *blob = koutput->prop_values[i];
+ return p;
+ }
+
+ drmModeFreeProperty(p);
+ }
+ return NULL;
+}
+
+static void armada_drm_conn_create_resources(xf86OutputPtr output)
+{
+ struct armada_conn_info *conn = output->driver_private;
+ struct armada_drm_info *drm = conn->drm;
+ drmModeConnectorPtr mop = conn->mode_output;
+ int i, j, n, err;
+
+ conn->props = calloc(mop->count_props, sizeof *conn->props);
+ if (!conn->props)
+ return;
+
+ for (i = n = 0; i < mop->count_props; i++) {
+ struct armada_property *prop = &conn->props[n];
+ drmModePropertyPtr dprop;
+ Bool immutable;
+
+ dprop = drmModeGetProperty(drm->fd, mop->props[i]);
+ if (!dprop || dprop->flags & DRM_MODE_PROP_BLOB ||
+ !strcmp(dprop->name, "DPMS") ||
+ !strcmp(dprop->name, "EDID")) {
+ drmModeFreeProperty(dprop);
+ continue;
+ }
+
+ n++;
+ prop->mode_prop = dprop;
+ prop->value = mop->prop_values[i];
+
+ immutable = dprop->flags & DRM_MODE_PROP_IMMUTABLE ?
+ TRUE : FALSE;
+
+ if (dprop->flags & DRM_MODE_PROP_RANGE) {
+ INT32 range[2];
+ uint32_t value = prop->value;
+
+ prop->natoms = 1;
+ prop->atoms = calloc(prop->natoms, sizeof *prop->atoms);
+ if (!prop->atoms)
+ continue;
+
+ range[0] = dprop->values[0];
+ range[1] = dprop->values[1];
+
+ prop->atoms[0] = MakeAtom(dprop->name,
+ strlen(dprop->name), TRUE);
+ err = RRConfigureOutputProperty(output->randr_output,
+ prop->atoms[0], FALSE,
+ TRUE, immutable, 2,
+ range);
+ if (err)
+ xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+ "RRConfigureOutputProperty error %d\n",
+ err);
+
+ err = RRChangeOutputProperty(output->randr_output,
+ prop->atoms[0],
+ XA_INTEGER, 32,
+ PropModeReplace, 1,
+ &value, FALSE, TRUE);
+ if (err)
+ xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+ "RRChangeOutputProperty error %d\n",
+ err);
+ } else if (dprop->flags & DRM_MODE_PROP_ENUM) {
+ int current;
+
+ prop->natoms = dprop->count_enums + 1;
+ prop->atoms = calloc(prop->natoms, sizeof *prop->atoms);
+ if (!prop->atoms)
+ continue;
+
+ current = prop->natoms;
+ prop->atoms[0] = MakeAtom(dprop->name,
+ strlen(dprop->name), TRUE);
+ for (j = 1; j < prop->natoms; j++) {
+ struct drm_mode_property_enum *e;
+
+ e = &dprop->enums[j - 1];
+ prop->atoms[j] = MakeAtom(e->name,
+ strlen(e->name),
+ TRUE);
+ if (prop->value == e->value)
+ current = j;
+ }
+
+ err = RRConfigureOutputProperty(output->randr_output,
+ prop->atoms[0], FALSE, FALSE,
+ immutable, prop->natoms - 1,
+ (INT32 *)&prop->atoms[1]);
+ if (err)
+ xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+ "RRConfigureOutputProperty error, %d\n",
+ err);
+
+ err = RRChangeOutputProperty(output->randr_output,
+ prop->atoms[0], XA_ATOM,
+ 32, PropModeReplace, 1,
+ &prop->atoms[current],
+ FALSE, TRUE);
+ if (err)
+ xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
+ "RRChangeOutputProperty error, %d\n",
+ err);
+ }
+ }
+ conn->nprops = n;
+}
+
+static void armada_drm_conn_dpms(xf86OutputPtr output, int mode)
+{
+ struct armada_conn_info *conn = output->driver_private;
+ struct armada_drm_info *drm = conn->drm;
+ drmModePropertyPtr p = armada_drm_conn_find_property(conn, "DPMS", NULL);
+
+ if (p) {
+ drmModeConnectorSetProperty(drm->fd, conn->id, p->prop_id,
+ mode);
+ conn->dpms_mode = mode;
+ drmModeFreeProperty(p);
+ }
+}
+
+static xf86OutputStatus armada_drm_conn_detect(xf86OutputPtr output)
+{
+ struct armada_conn_info *conn = output->driver_private;
+ struct armada_drm_info *drm = conn->drm;
+ xf86OutputStatus status = XF86OutputStatusUnknown;
+ drmModeConnectorPtr koutput;
+
+ koutput = drmModeGetConnector(drm->fd, conn->id);
+ if (!koutput)
+ return XF86OutputStatusUnknown;
+
+ drmModeFreeConnector(conn->mode_output);
+ conn->mode_output = koutput;
+
+ switch (koutput->connection) {
+ case DRM_MODE_CONNECTED:
+ status = XF86OutputStatusConnected;
+ break;
+ case DRM_MODE_DISCONNECTED:
+ status = XF86OutputStatusDisconnected;
+ break;
+ case DRM_MODE_UNKNOWNCONNECTION:
+ break;
+ }
+ return status;
+}
+
+static Bool
+armada_drm_conn_mode_valid(xf86OutputPtr output, DisplayModePtr pModes)
+{
+ return MODE_OK;
+}
+
+static DisplayModePtr armada_drm_conn_get_modes(xf86OutputPtr output)
+{
+ ScrnInfoPtr pScrn = output->scrn;
+ DisplayModePtr modes = NULL;
+ struct armada_conn_info *conn = output->driver_private;
+ struct armada_drm_info *drm = conn->drm;
+ uint32_t blob;
+ drmModePropertyPtr p;
+ drmModePropertyBlobPtr edid = NULL;
+ xf86MonPtr mon;
+ int i;
+
+ p = armada_drm_conn_find_property(conn, "EDID", &blob);
+ if (p) {
+ edid = drmModeGetPropertyBlob(drm->fd, blob);
+ drmModeFreeProperty(p);
+ }
+
+ mon = xf86InterpretEDID(pScrn->scrnIndex, edid ? edid->data : NULL);
+ if (mon && edid->length > 128)
+ mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
+ xf86OutputSetEDID(output, mon);
+
+ /* modes should already be available */
+ for (i = 0; i < conn->mode_output->count_modes; i++) {
+ DisplayModePtr mode = xnfalloc(sizeof *mode);
+
+ drmmode_ConvertFromKMode(pScrn, &conn->mode_output->modes[i], mode);
+ modes = xf86ModesAdd(modes, mode);
+ }
+
+ return modes;
+}
+
+#ifdef RANDR_12_INTERFACE
+static Bool armada_drm_conn_set_property(xf86OutputPtr output, Atom property,
+ RRPropertyValuePtr value)
+{
+ struct armada_conn_info *conn = output->driver_private;
+ struct armada_drm_info *drm = conn->drm;
+ int i;
+
+ for (i = 0; i < conn->nprops; i++) {
+ struct armada_property *prop = &conn->props[i];
+ drmModePropertyPtr dprop;
+
+ if (prop->atoms[0] != property)
+ continue;
+
+ dprop = prop->mode_prop;
+ if (dprop->flags & DRM_MODE_PROP_RANGE) {
+ if (value->type != XA_INTEGER || value->format != 32 || value->size != 1)
+ return FALSE;
+
+ drmModeConnectorSetProperty(drm->fd, conn->id,
+ dprop->prop_id, (uint64_t)*(uint32_t *)value->data);
+
+ return TRUE;
+ } else if (dprop->flags & DRM_MODE_PROP_ENUM) {
+ Atom atom;
+ const char *name;
+ int j;
+
+ if (value->type != XA_ATOM || value->format != 32 || value->size != 1)
+ return FALSE;
+
+ memcpy(&atom, value->data, sizeof(atom));
+ name = NameForAtom(atom);
+ if (name == NULL)
+ return FALSE;
+
+ for (j = 0; j < dprop->count_enums; j++) {
+ if (!strcmp(dprop->enums[j].name, name)) {
+ drmModeConnectorSetProperty(drm->fd, conn->id,
+ dprop->prop_id, dprop->enums[j].value);
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+#endif
+#ifdef RANDR_13_INTERFACE
+static Bool armada_drm_conn_get_property(xf86OutputPtr output, Atom property)
+{
+ return FALSE;
+}
+#endif
+
+static void armada_drm_conn_destroy(xf86OutputPtr output)
+{
+ struct armada_conn_info *conn = output->driver_private;
+
+ drmModeFreeConnector(conn->mode_output);
+ drmModeFreeEncoder(conn->mode_encoder);
+ free(conn);
+
+ output->driver_private = NULL;
+}
+
+static const xf86OutputFuncsRec drm_output_funcs = {
+ .create_resources = armada_drm_conn_create_resources,
+ .dpms = armada_drm_conn_dpms,
+ .detect = armada_drm_conn_detect,
+ .mode_valid = armada_drm_conn_mode_valid,
+ .get_modes = armada_drm_conn_get_modes,
+#ifdef RANDR_12_INTERFACE
+ .set_property = armada_drm_conn_set_property,
+#endif
+#ifdef RANDR_13_INTERFACE
+ .get_property = armada_drm_conn_get_property,
+#endif
+ .destroy = armada_drm_conn_destroy,
+};
+
+static const char *const output_names[] = {
+ "None", "VGA", "DVI", "DVI", "DVI", "Composite", "TV",
+ "LVDS", "CTV", "DIN", "DP", "HDMI", "HDMI",
+};
+
+static const int subpixel_conv_table[] = {
+ 0, SubPixelUnknown, SubPixelHorizontalRGB, SubPixelHorizontalBGR,
+ SubPixelVerticalRGB, SubPixelVerticalBGR, SubPixelNone
+};
+
+static void
+armada_drm_conn_init(ScrnInfoPtr pScrn, struct armada_drm_info *drm,
+ uint32_t id)
+{
+ drmModeConnectorPtr koutput;
+ drmModeEncoderPtr kencoder;
+ xf86OutputPtr output;
+ struct armada_conn_info *conn;
+ char name[32];
+
+ koutput = drmModeGetConnector(drm->fd, id);
+ if (!koutput)
+ return;
+
+ kencoder = drmModeGetEncoder(drm->fd, koutput->encoders[0]);
+ if (!kencoder) {
+ drmModeFreeConnector(koutput);
+ return;
+ }
+
+ snprintf(name, sizeof(name), "%s%d",
+ output_names[koutput->connector_type],
+ koutput->connector_type_id);
+
+ output = xf86OutputCreate(pScrn, &drm_output_funcs, name);
+ if (!output) {
+ drmModeFreeEncoder(kencoder);
+ drmModeFreeConnector(koutput);
+ return;
+ }
+
+ conn = calloc(1, sizeof *conn);
+ if (!conn) {
+ xf86OutputDestroy(output);
+ drmModeFreeEncoder(kencoder);
+ drmModeFreeConnector(koutput);
+ return;
+ }
+
+ conn->drm = drm;
+ conn->id = id;
+ conn->mode_output = koutput;
+ conn->mode_encoder = kencoder;
+
+ output->driver_private = conn;
+ output->mm_width = koutput->mmWidth;
+ output->mm_height = koutput->mmHeight;
+ output->subpixel_order = subpixel_conv_table[koutput->subpixel];
+ output->possible_crtcs = kencoder->possible_crtcs;
+ output->possible_clones = kencoder->possible_clones;
+ output->interlaceAllowed = 1; /* wish there was a way to read that */
+ output->doubleScanAllowed = 0;
+}
+
+/*
+ * CRTC support
+ */
+static Bool armada_drm_crtc_apply(xf86CrtcPtr crtc, drmModeModeInfoPtr kmode)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+ uint32_t fb_id, *output_ids;
+ int x, y, i, ret, output_num;
+
+ if (!kmode)
+ kmode = &drmc->kmode;
+
+ output_ids = calloc(xf86_config->num_output, sizeof *output_ids);
+ if (!output_ids)
+ return FALSE;
+
+ for (output_num = i = 0; i < xf86_config->num_output; i++) {
+ xf86OutputPtr output = xf86_config->output[i];
+ struct armada_conn_info *conn;
+
+ if (output->crtc == crtc) {
+ conn = output->driver_private;
+ output_ids[output_num++] = conn->mode_output->connector_id;
+ }
+ }
+
+ if (!xf86CrtcRotate(crtc)) {
+ ret = FALSE;
+ goto done;
+ }
+
+ crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
+ crtc->gamma_blue, crtc->gamma_size);
+
+ if (drmc->rotate_fb_id) {
+ fb_id = drmc->rotate_fb_id;
+ x = y = 0;
+ } else {
+ fb_id = drm->fb_id;
+ x = crtc->x;
+ y = crtc->y;
+ }
+
+ ret = drmModeSetCrtc(drm->fd, drmc->mode_crtc->crtc_id, fb_id, x, y,
+ output_ids, output_num, kmode);
+ if (ret) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to set mode on crtc %u: %s\n",
+ drmc->mode_crtc->crtc_id, strerror(errno));
+ ret = FALSE;
+ } else {
+ ret = TRUE;
+
+ for (i = 0; i < xf86_config->num_output; i++) {
+ xf86OutputPtr output = xf86_config->output[i];
+
+ if (output->funcs != &drm_output_funcs ||
+ output->crtc != crtc)
+ continue;
+
+ armada_drm_conn_dpms(output, DPMSModeOn);
+ }
+ }
+
+ /* Work around stricter checks in X */
+ if (pScrn->pScreen && drm->hw_cursor)
+ xf86_reload_cursors(pScrn->pScreen);
+
+done:
+ free(output_ids);
+ return ret;
+}
+
+static void armada_drm_crtc_dpms(xf86CrtcPtr crtc, int mode)
+{
+}
+
+static Bool
+armada_drm_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
+ Rotation rotation, int x, int y)
+{
+ ScrnInfoPtr pScrn = crtc->scrn;
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+ drmModeModeInfo kmode;
+ DisplayModeRec saved_mode;
+ Rotation saved_rotation;
+ int ret, saved_x, saved_y;
+
+ if (drm->fb_id == 0) {
+ ret = drmModeAddFB(drm->fd, pScrn->virtualX, pScrn->virtualY,
+ pScrn->depth, pScrn->bitsPerPixel,
+ drm->front_bo->pitch, drm->front_bo->handle,
+ &drm->fb_id);
+ if (ret < 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to add fb: %s\n",
+ strerror(errno));
+ return FALSE;
+ }
+ }
+
+ saved_mode = crtc->mode;
+ saved_x = crtc->x;
+ saved_y = crtc->y;
+ saved_rotation = crtc->rotation;
+ crtc->mode = *mode;
+ crtc->x = x;
+ crtc->y = y;
+ crtc->rotation = rotation;
+
+ drmmode_ConvertToKMode(&kmode, mode);
+
+ ret = armada_drm_crtc_apply(crtc, &kmode);
+ if (!ret) {
+ crtc->mode = saved_mode;
+ crtc->x = saved_x;
+ crtc->y = saved_y;
+ crtc->rotation = saved_rotation;
+ } else
+ drmc->kmode = kmode;
+ return ret;
+}
+
+static void armada_drm_crtc_gamma_set(xf86CrtcPtr crtc, CARD16 *red,
+ CARD16 *green, CARD16 *blue, int size)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+
+ drmModeCrtcSetGamma(drm->fd, drmc->mode_crtc->crtc_id,
+ size, red, green, blue);
+}
+
+static void armada_drm_crtc_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+
+ drmModeMoveCursor(drm->fd, drmc->mode_crtc->crtc_id, x, y);
+}
+
+static void armada_drm_crtc_show_cursor(xf86CrtcPtr crtc)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+
+ drmModeSetCursor(drm->fd, drmc->mode_crtc->crtc_id,
+ drmc->cursor_bo->handle,
+ CURSOR_MAX_WIDTH, CURSOR_MAX_HEIGHT);
+}
+
+static void armada_drm_crtc_hide_cursor(xf86CrtcPtr crtc)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+
+ drmModeSetCursor(drm->fd, drmc->mode_crtc->crtc_id, 0, 0, 0);
+}
+
+static void armada_drm_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+
+ drm_armada_bo_subdata(drmc->cursor_bo, 0,
+ CURSOR_MAX_WIDTH * CURSOR_MAX_HEIGHT * 4, image);
+}
+
+static void *
+armada_drm_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ ScrnInfoPtr pScrn = crtc->scrn;
+ struct drm_armada_bo *bo;
+ int ret;
+
+ bo = armada_bo_alloc_framebuffer(pScrn, drmc->drm, width, height,
+ pScrn->bitsPerPixel);
+ if (!bo) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to allocate shadow memory for rotated CRTC\n");
+ return NULL;
+ }
+
+ ret = drmModeAddFB(drmc->drm->fd, width, height, pScrn->depth,
+ pScrn->bitsPerPixel, bo->pitch, bo->handle,
+ &drmc->rotate_fb_id);
+ if (ret) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to add rotate fb: %s\n",
+ strerror(errno));
+ drm_armada_bo_put(bo);
+ return NULL;
+ }
+
+ return bo;
+}
+
+static PixmapPtr
+armada_drm_crtc_shadow_create(xf86CrtcPtr crtc, void *data,
+ int width, int height)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ ScrnInfoPtr scrn = crtc->scrn;
+ PixmapPtr rotate_pixmap;
+ struct drm_armada_bo *bo;
+
+ if (!data)
+ data = armada_drm_crtc_shadow_allocate(crtc, width, height);
+ if (!data) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Failed to allocate shadow pixmap data for rotated CRTC\n");
+ return NULL;
+ }
+
+ bo = data;
+
+ rotate_pixmap = GetScratchPixmapHeader(scrn->pScreen, width, height,
+ scrn->depth, scrn->bitsPerPixel,
+ bo->pitch, bo->ptr);
+ if (!rotate_pixmap) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "Failed to allocate shadow pixmap for rotated CRTC\n");
+ return NULL;
+ }
+
+ if (drmc->drm->accel)
+ vivante_set_pixmap_bo(rotate_pixmap, bo);
+
+ return rotate_pixmap;
+}
+
+static void
+armada_drm_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rot_pixmap,
+ void *data)
+{
+ if (rot_pixmap) {
+ vivante_free_pixmap(rot_pixmap);
+ FreeScratchPixmapHeader(rot_pixmap);
+ }
+ if (data) {
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+
+ drmModeRmFB(drm->fd, drmc->rotate_fb_id);
+ drmc->rotate_fb_id = 0;
+
+ drm_armada_bo_put(data);
+ }
+}
+
+static void armada_drm_crtc_destroy(xf86CrtcPtr crtc)
+{
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ struct armada_drm_info *drm = drmc->drm;
+
+ if (drmc->cursor_bo) {
+ drmModeSetCursor(drm->fd, drmc->mode_crtc->crtc_id, 0, 0, 0);
+ drm_armada_bo_put(drmc->cursor_bo);
+ }
+ drmModeFreeCrtc(drmc->mode_crtc);
+
+ free(drmc);
+}
+
+static const xf86CrtcFuncsRec drm_crtc_funcs = {
+ .dpms = armada_drm_crtc_dpms,
+ .gamma_set = armada_drm_crtc_gamma_set,
+ .set_mode_major = armada_drm_crtc_set_mode_major,
+ .set_cursor_position = armada_drm_crtc_set_cursor_position,
+ .show_cursor = armada_drm_crtc_show_cursor,
+ .hide_cursor = armada_drm_crtc_hide_cursor,
+ .load_cursor_argb = armada_drm_crtc_load_cursor_argb,
+ .shadow_create = armada_drm_crtc_shadow_create,
+ .shadow_allocate = armada_drm_crtc_shadow_allocate,
+ .shadow_destroy = armada_drm_crtc_shadow_destroy,
+ .destroy = armada_drm_crtc_destroy,
+};
+
+static Bool
+armada_drm_crtc_init(ScrnInfoPtr pScrn, struct armada_drm_info *drm,
+ uint32_t id)
+{
+ xf86CrtcPtr crtc;
+ struct armada_crtc_info *drmc;
+
+ crtc = xf86CrtcCreate(pScrn, &drm_crtc_funcs);
+ if (!crtc)
+ return FALSE;
+
+ drmc = xnfcalloc(1, sizeof *drmc);
+ if (!drmc)
+ return FALSE;
+
+ drmc->drm = drm;
+ drmc->mode_crtc = drmModeGetCrtc(drm->fd, id);
+ crtc->driver_private = drmc;
+
+ drmc->cursor_bo = drm_armada_bo_dumb_create(drm->bufmgr,
+ CURSOR_MAX_WIDTH,
+ CURSOR_MAX_HEIGHT,
+ 32);
+
+ return TRUE;
+}
+
+static Bool armada_drm_xf86crtc_resize(ScrnInfoPtr pScrn, int width, int height)
+{
+ ScreenPtr screen = screenInfo.screens[pScrn->scrnIndex];
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ struct drm_armada_bo *bo, *old_bo;
+ uint32_t old_fb_id;
+ int ret, i;
+
+ if (pScrn->virtualX == width && pScrn->virtualY == height)
+ return TRUE;
+
+ bo = armada_bo_alloc_framebuffer(pScrn, drm, width, height,
+ pScrn->bitsPerPixel);
+ if (!bo)
+ return FALSE;
+
+ old_fb_id = drm->fb_id;
+ ret = drmModeAddFB(drm->fd, width, height,
+ pScrn->depth, pScrn->bitsPerPixel,
+ bo->pitch, bo->handle, &drm->fb_id);
+ if (ret) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to add fb: %s\n", strerror(errno));
+ drm_armada_bo_put(bo);
+ return FALSE;
+ }
+
+ /* Okay, now switch everything */
+ pScrn->virtualX = width;
+ pScrn->virtualY = height;
+ pScrn->displayWidth = bo->pitch / drm->cpp;
+ old_bo = drm->front_bo;
+ drm->front_bo = bo;
+
+ armada_drm_ModifyScreenPixmap(screen, drm, width, height, -1, -1, bo);
+
+ for (i = 0; i < xf86_config->num_crtc; i++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[i];
+
+ if (!crtc->enabled)
+ continue;
+
+ armada_drm_crtc_apply(crtc, NULL);
+ }
+
+ drmModeRmFB(drm->fd, old_fb_id);
+ drm_armada_bo_put(old_bo);
+ return TRUE;
+}
+
+static const xf86CrtcConfigFuncsRec armada_drm_config_funcs = {
+ armada_drm_xf86crtc_resize,
+};
+
+#ifdef HAVE_UDEV
+static void armada_drm_handle_uevent(int fd, pointer data)
+{
+ ScrnInfoPtr pScrn = data;
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ struct udev_device *ud;
+
+ ud = udev_monitor_receive_device(drm->udev_monitor);
+ if (ud) {
+ dev_t dev = udev_device_get_devnum(ud);
+ const char *hp = udev_device_get_property_value(ud, "HOTPLUG");
+
+ if (dev == drm->drm_dev && hp && strtol(hp, NULL, 10) == 1)
+ RRGetInfo(screenInfo.screens[pScrn->scrnIndex], TRUE);
+
+ udev_device_unref(ud);
+ }
+}
+
+static Bool armada_drm_udev_init(ScrnInfoPtr pScrn)
+{
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ struct udev_monitor *udev_mon;
+ struct udev *udev;
+ struct stat st;
+ MessageType from = X_CONFIG;
+ Bool hotplug;
+
+ if (!xf86GetOptValBool(drm->Options, OPTION_HOTPLUG, &hotplug)) {
+ from = X_DEFAULT;
+ hotplug = TRUE;
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, from, "hotplug detection %sabled\n",
+ hotplug ? "en" : "dis");
+ if (!hotplug)
+ return TRUE;
+
+ if (fstat(drm->fd, &st) || !S_ISCHR(st.st_mode))
+ return FALSE;
+
+ drm->drm_dev = st.st_rdev;
+
+ udev = udev_new();
+ if (!udev)
+ return FALSE;
+
+ udev_mon = udev_monitor_new_from_netlink(udev, "udev");
+ if (!udev_mon) {
+ udev_unref(udev);
+ return FALSE;
+ }
+
+ if (udev_monitor_filter_add_match_subsystem_devtype(udev_mon,
+ "drm", "drm_minor") ||
+ udev_monitor_enable_receiving(udev_mon)) {
+ udev_monitor_unref(udev_mon);
+ udev_unref(udev);
+ return FALSE;
+ }
+
+ drm->udev_monitor = udev_mon;
+ drm->udev_handler = xf86AddGeneralHandler(udev_monitor_get_fd(udev_mon),
+ armada_drm_handle_uevent,
+ pScrn);
+
+ return TRUE;
+}
+
+static void armada_drm_udev_fini(ScrnInfoPtr pScrn, struct armada_drm_info *drm)
+{
+ if (drm->udev_monitor) {
+ struct udev *udev = udev_monitor_get_udev(drm->udev_monitor);
+
+ xf86RemoveGeneralHandler(drm->udev_handler);
+ udev_monitor_unref(drm->udev_monitor);
+ udev_unref(udev);
+ }
+}
+#endif
+
+static void armada_drm_LoadPalette(ScrnInfoPtr pScrn, int num, int *indices,
+ LOCO *colors, VisualPtr pVisual)
+{
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ uint16_t lut_r[256], lut_g[256], lut_b[256];
+ int i, p;
+
+ for (i = 0; i < num; i++) {
+ int index = indices[i];
+ lut_r[index] = colors[index].red << 8;
+ lut_g[index] = colors[index].green << 8;
+ lut_b[index] = colors[index].blue << 8;
+ }
+
+ for (p = 0; p < xf86_config->num_crtc; p++) {
+ xf86CrtcPtr crtc = xf86_config->crtc[p];
+
+#ifdef RANDR_12_INTERFACE
+ RRCrtcGammaSet(crtc->randr_crtc, lut_r, lut_g, lut_b);
+#else
+ crtc->funcs->gamma_set(crtc, lut_r, lut_g, lut_b, 256);
+#endif
+ }
+}
+
+static void armada_drm_AdjustFrame(int scrnIndex, int x, int y, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ xf86OutputPtr output = config->output[config->compat_output];
+ xf86CrtcPtr crtc = output->crtc;
+
+ if (crtc && crtc->enabled) {
+ int saved_x = crtc->x;
+ int saved_y = crtc->y;
+ int ret;
+
+ crtc->x = x;
+ crtc->y = y;
+
+ ret = crtc->funcs->set_mode_major(crtc, &crtc->mode,
+ crtc->rotation, x, y);
+ if (!ret) {
+ crtc->x = saved_x;
+ crtc->y = saved_y;
+ }
+ }
+}
+
+static Bool armada_drm_EnterVT(int scrnIndex, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ int i;
+
+ if (drmSetMaster(drm->fd))
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "[drm] set master failed: %s\n", strerror(errno));
+
+ if (!xf86SetDesiredModes(pScrn))
+ return FALSE;
+
+ /* Disable unused CRTCs */
+ for (i = 0; i < config->num_crtc; i++) {
+ xf86CrtcPtr crtc = config->crtc[i];
+ struct armada_crtc_info *drmc = crtc->driver_private;
+ if (!crtc->enabled)
+ drmModeSetCrtc(drm->fd, drmc->mode_crtc->crtc_id,
+ 0, 0, 0, NULL, 0, NULL);
+ }
+
+ return TRUE;
+}
+
+static void armada_drm_LeaveVT(int scrnIndex, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+
+ xf86RotateFreeShadow(pScrn);
+
+ xf86_hide_cursors(pScrn);
+
+ drmDropMaster(drm->fd);
+}
+
+static ModeStatus
+armada_drm_ValidMode(int scrnIndex, DisplayModePtr mode, Bool verbose,
+ int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+
+ if (mode->Flags & V_DBLSCAN) {
+ if (verbose)
+ xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
+ "Removing double-scanned mode \"%s\"\n",
+ mode->name);
+ return MODE_BAD;
+ }
+
+ return MODE_OK;
+}
+
+static Bool armada_drm_SwitchMode(int scrnIndex, DisplayModePtr mode, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+
+ return xf86SetSingleMode(pScrn, mode, RR_Rotate_0);
+}
+
+static Bool armada_drm_CloseScreen(int scrnIndex, ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ PixmapPtr pixmap = pScreen->GetScreenPixmap(pScreen);
+ Bool ret;
+
+#ifdef HAVE_UDEV
+ armada_drm_udev_fini(pScrn, drm);
+#endif
+
+ if (drm->fb_id) {
+ drmModeRmFB(drm->fd, drm->fb_id);
+ drm->fb_id = 0;
+ }
+ if (drm->front_bo) {
+ drm_armada_bo_put(drm->front_bo);
+ drm->front_bo = NULL;
+ }
+ if (drm->accel)
+ vivante_free_pixmap(pixmap);
+
+ if (drm->hw_cursor)
+ xf86_cursors_fini(pScreen);
+
+ pScreen->CloseScreen = drm->CloseScreen;
+ ret = (*pScreen->CloseScreen)(scrnIndex, pScreen);
+
+ if (pScrn->vtSema)
+ armada_drm_LeaveVT(pScreen->myNum, 0);
+
+ pScrn->vtSema = FALSE;
+
+ return ret;
+}
+
+static Bool armada_drm_CreateScreenResources(ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ Bool ret;
+
+ pScreen->CreateScreenResources = drm->CreateScreenResources;
+ ret = pScreen->CreateScreenResources(pScreen);
+ if (ret) {
+ struct drm_armada_bo *bo = drm->front_bo;
+
+ armada_drm_ModifyScreenPixmap(pScreen, drm, -1, -1, -1, -1, bo);
+ }
+ return ret;
+}
+
+static void armada_drm_wakeup_handler(pointer data, int err, pointer p)
+{
+ struct armada_drm_info *drm = data;
+ fd_set *read_mask = p;
+
+ if (data == NULL || err < 0)
+ return;
+
+ if (FD_ISSET(drm->fd, read_mask))
+ drmHandleEvent(drm->fd, &drm->event_context);
+}
+
+static Bool
+armada_drm_ScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
+{
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ struct drm_armada_bo *bo;
+ int visuals, preferredCVC;
+
+ if (drmSetMaster(drm->fd)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] set master failed: %s\n", strerror(errno));
+ return FALSE;
+ }
+
+ drm->accel = xf86ReturnOptValBool(drm->Options, OPTION_USE_GPU, TRUE);
+
+ bo = armada_bo_alloc_framebuffer(pScrn, drm, pScrn->virtualX,
+ pScrn->virtualY, pScrn->bitsPerPixel);
+ if (!bo)
+ return FALSE;
+
+ drm->front_bo = bo;
+ pScrn->displayWidth = bo->pitch / drm->cpp;
+
+ miClearVisualTypes();
+ if (pScrn->bitsPerPixel > 8) {
+ visuals = TrueColorMask;
+ preferredCVC = TrueColor;
+ } else {
+ visuals = miGetDefaultVisualMask(pScrn->depth);
+ preferredCVC = pScrn->defaultVisual;
+ }
+
+ if (!miSetVisualTypes(pScrn->depth, visuals, pScrn->rgbBits, preferredCVC)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to set visual types for %d bpp depth %d\n",
+ pScrn->bitsPerPixel, pScrn->depth);
+ return FALSE;
+ }
+
+ if (!miSetPixmapDepths()) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to set pixmap depths\n");
+ return FALSE;
+ }
+
+ if (!fbScreenInit(pScreen, NULL, pScrn->virtualX, pScrn->virtualY,
+ pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth,
+ pScrn->bitsPerPixel)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] fbScreenInit failed\n");
+ return FALSE;
+ }
+
+ if (pScrn->bitsPerPixel > 8) {
+ /* Fixup RGB ordering */
+ VisualPtr visual = pScreen->visuals + pScreen->numVisuals;
+ while (--visual >= pScreen->visuals) {
+ if ((visual->class | DynamicClass) == DirectColor) {
+ visual->offsetRed = pScrn->offset.red;
+ visual->offsetGreen = pScrn->offset.green;
+ visual->offsetBlue = pScrn->offset.blue;
+ visual->redMask = pScrn->mask.red;
+ visual->greenMask = pScrn->mask.green;
+ visual->blueMask = pScrn->mask.blue;
+ }
+ }
+ }
+
+ if (!fbPictureInit(pScreen, NULL, 0)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] fbPictureInit failed\n");
+ return FALSE;
+ }
+
+ xf86SetBlackWhitePixels(pScreen);
+
+ if (drm->accel && !vivante_ScreenInit(pScreen, drm->bufmgr)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "[drm] Vivante initialization failed, running unaccelerated\n");
+ drm->accel = FALSE;
+ }
+
+ miInitializeBackingStore(pScreen);
+ xf86SetBackingStore(pScreen);
+ xf86SetSilkenMouse(pScreen);
+
+ /* software cursor */
+ miDCInitialize(pScreen, xf86GetPointerScreenFuncs());
+
+ drm->hw_cursor = xf86ReturnOptValBool(drm->Options, OPTION_HW_CURSOR, FALSE);
+ if (drm->hw_cursor &&
+ xf86_cursors_init(pScreen,
+ CURSOR_MAX_WIDTH, CURSOR_MAX_HEIGHT,
+ HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
+ HARDWARE_CURSOR_BIT_ORDER_MSBFIRST |
+ HARDWARE_CURSOR_INVERT_MASK |
+ HARDWARE_CURSOR_SWAP_SOURCE_AND_MASK |
+ HARDWARE_CURSOR_AND_SOURCE_WITH_MASK |
+ HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_64 |
+ HARDWARE_CURSOR_UPDATE_UNHIDDEN |
+ HARDWARE_CURSOR_ARGB)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Using hardware cursors\n");
+ } else {
+ drm->hw_cursor = FALSE;
+ }
+
+ pScreen->SaveScreen = xf86SaveScreen;
+ drm->CloseScreen = pScreen->CloseScreen;
+ pScreen->CloseScreen = armada_drm_CloseScreen;
+ drm->CreateScreenResources = pScreen->CreateScreenResources;
+ pScreen->CreateScreenResources = armada_drm_CreateScreenResources;
+
+ if (!xf86CrtcScreenInit(pScreen)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to initialize screen\n");
+ return FALSE;
+ }
+
+ if (!miCreateDefColormap(pScreen)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to initialize default colormap\n");
+ return FALSE;
+ }
+
+ if (!xf86HandleColormaps(pScreen, 256, 8, armada_drm_LoadPalette, NULL,
+ CMAP_RELOAD_ON_MODE_SWITCH |
+ CMAP_PALETTED_TRUECOLOR)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to initialize colormap handler\n");
+ return FALSE;
+ }
+
+ xf86DPMSInit(pScreen, xf86DPMSSet, 0);
+
+ if (xf86ReturnOptValBool(drm->Options, OPTION_XV_ACCEL, TRUE))
+ armada_drm_XvInit(pScrn);
+
+ /* Setup the synchronisation feedback */
+ AddGeneralSocket(drm->fd);
+ RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
+ armada_drm_wakeup_handler, drm);
+
+#ifdef HAVE_UDEV
+ if (!armada_drm_udev_init(pScrn)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to connect with udev: %s\n",
+ strerror(errno));
+ return FALSE;
+ }
+#endif
+
+ pScrn->vtSema = TRUE;
+
+ return armada_drm_EnterVT(pScreen->myNum, 0);
+}
+
+static Bool armada_drm_pre_init(ScrnInfoPtr pScrn)
+{
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ drmVersionPtr version;
+ Gamma zeros = { 0.0, 0.0, 0.0 };
+ int i;
+
+ xf86CollectOptions(pScrn, NULL);
+ drm->Options = malloc(sizeof(armada_drm_options));
+ if (!drm->Options)
+ return FALSE;
+ memcpy(drm->Options, armada_drm_options, sizeof(armada_drm_options));
+ xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, drm->Options);
+
+ version = drmGetVersion(drm->fd);
+ if (version) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "hardware: %s\n",
+ version->name);
+ drmFreeVersion(version);
+ }
+
+ drm->cpp = (pScrn->bitsPerPixel + 7) / 8;
+
+ xf86CrtcConfigInit(pScrn, &armada_drm_config_funcs);
+
+ drm->mode_res = drmModeGetResources(drm->fd);
+ if (!drm->mode_res) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "failed to get resources: %s\n", strerror(errno));
+ return FALSE;
+ }
+
+ pScrn->virtualX = drm->mode_res->max_width;
+ pScrn->virtualY = drm->mode_res->max_height;
+
+ xf86CrtcSetSizeRange(pScrn, 320, 200, pScrn->virtualX, pScrn->virtualY);
+
+ for (i = 0; i < drm->mode_res->count_crtcs; i++)
+ if (!armada_drm_crtc_init(pScrn, drm, drm->mode_res->crtcs[i]))
+ return FALSE;
+
+ for (i = 0; i < drm->mode_res->count_connectors; i++)
+ armada_drm_conn_init(pScrn, drm, drm->mode_res->connectors[i]);
+
+ xf86InitialConfiguration(pScrn, TRUE);
+
+ /* Limit the maximum framebuffer size to 16MB */
+ pScrn->videoRam = 16 * 1048576;
+
+ drm->event_context.version = DRM_EVENT_CONTEXT_VERSION;
+#ifdef HAVE_DRI2
+ drm->event_context.vblank_handler = vivante_dri2_vblank;
+#else
+ drm->event_context.vblank_handler = NULL;
+#endif
+ drm->event_context.page_flip_handler = NULL;
+
+ if (!xf86SetGamma(pScrn, zeros))
+ return FALSE;
+
+ if (pScrn->modes == NULL) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No modes.\n");
+ return FALSE;
+ }
+
+ pScrn->currentMode = pScrn->modes;
+
+ /* Set display resolution */
+ xf86SetDpi(pScrn, 0, 0);
+
+ if (!xf86LoadSubModule(pScrn, "fb"))
+ return FALSE;
+
+ return TRUE;
+}
+
+static Bool armada_drm_open_master(ScrnInfoPtr pScrn)
+{
+ struct armada_drm_info *drm;
+ EntityInfoPtr pEnt;
+ drmSetVersion sv;
+ const char *busid = DRM_DEFAULT_BUS_ID;
+ int err;
+
+ pEnt = xf86GetEntityInfo(pScrn->entityList[0]);
+ if (pEnt) {
+ if (pEnt->device->busID)
+ busid = pEnt->device->busID;
+ free(pEnt);
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Using BusID \"%s\"\n", busid);
+
+ drm = calloc(1, sizeof *drm);
+ if (!drm)
+ return FALSE;
+
+ drm->fd = drmOpen(DRM_MODULE_NAME, busid);
+ if (drm->fd < 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] Failed to open DRM device for %s: %s\n",
+ busid, strerror(errno));
+ goto err_free;
+ }
+
+ /*
+ * Check that what we opened was a master or a master-capable FD
+ * by setting the version of the interface we'll use to talk to it.
+ */
+ sv.drm_di_major = 1;
+ sv.drm_di_minor = 1;
+ sv.drm_dd_major = -1;
+ sv.drm_dd_minor = -1;
+ err = drmSetInterfaceVersion(drm->fd, &sv);
+ if (err) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to set DRM interface version: %s\n",
+ strerror(errno));
+ goto err_drm_close;
+ }
+
+ err = drm_armada_init(drm->fd, &drm->bufmgr);
+ if (err) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] failed to initialize Armada DRM manager.\n");
+ goto err_drm_close;
+ }
+
+ SET_DRM_INFO(pScrn, drm);
+
+ return TRUE;
+
+ err_drm_close:
+ drmClose(drm->fd);
+ err_free:
+ free(drm);
+ return FALSE;
+}
+
+static void armada_drm_close_master(ScrnInfoPtr pScrn)
+{
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+
+ if (drm) {
+ drm_armada_fini(drm->bufmgr);
+ drmClose(drm->fd);
+ SET_DRM_INFO(pScrn, NULL);
+ free(drm);
+ }
+}
+
+static void armada_drm_FreeScreen(int scrnIndex, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+
+ armada_drm_close_master(pScrn);
+}
+
+static Bool armada_drm_PreInit(ScrnInfoPtr pScrn, int flags)
+{
+ rgb defaultWeight = { 0, 0, 0 };
+ int flags24;
+
+ if (pScrn->numEntities != 1)
+ return FALSE;
+
+ if (flags & PROBE_DETECT)
+ return FALSE;
+
+ if (!armada_drm_open_master(pScrn)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Failed to become DRM master.\n");
+ return FALSE;
+ }
+
+ pScrn->monitor = pScrn->confScreen->monitor;
+ pScrn->progClock = TRUE;
+ pScrn->rgbBits = 8;
+ pScrn->chipset = "fbdev";
+ pScrn->displayWidth = 640;
+
+ flags24 = Support24bppFb | Support32bppFb | SupportConvert24to32;
+ if (!xf86SetDepthBpp(pScrn, 0, 0, 0, flags24))
+ goto fail;
+
+ switch (pScrn->depth) {
+ case 8:
+ case 15:
+ case 16:
+ case 24:
+ break;
+ default:
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Given depth (%d) is not supported.\n",
+ pScrn->depth);
+ goto fail;
+ }
+
+ xf86PrintDepthBpp(pScrn);
+
+ if (!xf86SetWeight(pScrn, defaultWeight, defaultWeight))
+ goto fail;
+ if (!xf86SetDefaultVisual(pScrn, -1))
+ goto fail;
+
+ if (pScrn->depth > 8 && pScrn->defaultVisual != TrueColor) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Requested default visual (%s) is not supported at depth %d\n",
+ xf86GetVisualName(pScrn->defaultVisual),
+ pScrn->depth);
+ goto fail;
+ }
+
+ if (!armada_drm_pre_init(pScrn))
+ goto fail;
+
+ return TRUE;
+
+ fail:
+ armada_drm_FreeScreen(pScrn->scrnIndex, 0);
+ return FALSE;
+}
+
+Bool armada_drm_init_screen(ScrnInfoPtr pScrn)
+{
+ pScrn->PreInit = armada_drm_PreInit;
+ pScrn->ScreenInit = armada_drm_ScreenInit;
+ pScrn->SwitchMode = armada_drm_SwitchMode;
+ pScrn->AdjustFrame = armada_drm_AdjustFrame;
+ pScrn->EnterVT = armada_drm_EnterVT;
+ pScrn->LeaveVT = armada_drm_LeaveVT;
+ pScrn->FreeScreen = armada_drm_FreeScreen;
+ pScrn->ValidMode = armada_drm_ValidMode;
+
+ return TRUE;
+}
diff --git a/src/armada_drm.h b/src/armada_drm.h
new file mode 100644
index 0000000..c207564
--- /dev/null
+++ b/src/armada_drm.h
@@ -0,0 +1,73 @@
+/*
+ * Marvell Armada DRM-based driver
+ *
+ * Written by Russell King, 2012, derived in part from the
+ * Intel xorg X server driver.
+ */
+#ifndef DOVEFB_DRM_H
+#define DOVEFB_DRM_H
+
+#include "xf86.h"
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+struct drm_armada_bo;
+
+struct armada_crtc_info {
+ struct armada_drm_info *drm;
+
+ drmModeModeInfo kmode;
+ drmModeCrtcPtr mode_crtc;
+
+ struct drm_armada_bo *cursor_bo;
+
+ uint32_t rotate_fb_id;
+};
+
+struct armada_drm_info {
+ int fd;
+ struct drm_armada_bufmgr *bufmgr;
+ drmEventContext event_context;
+
+ unsigned cpp;
+
+ struct drm_armada_bo *front_bo;
+ uint32_t fb_id;
+ drmModeResPtr mode_res;
+
+ Bool hw_cursor;
+ Bool accel;
+
+#ifdef HAVE_UDEV
+ struct udev_monitor *udev_monitor;
+ pointer *udev_handler;
+ dev_t drm_dev;
+#endif
+
+ CloseScreenProcPtr CloseScreen;
+ CreateScreenResourcesProcPtr CreateScreenResources;
+ OptionInfoPtr Options;
+};
+
+enum {
+ OPTION_HW_CURSOR,
+ OPTION_XV_ACCEL,
+ OPTION_USE_GPU,
+ OPTION_HOTPLUG,
+};
+
+#define GET_DRM_INFO(pScrn) ((struct armada_drm_info *)(pScrn)->driverPrivate)
+#define SET_DRM_INFO(pScrn, ptr) ((pScrn)->driverPrivate = (ptr))
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define mint(x,y) ({(void)(&x == &y); x < y ? x : y; })
+#define maxt(x,y) ({(void)(&x == &y); x < y ? y : x; })
+
+/* DRM core support */
+Bool armada_drm_init_screen(ScrnInfoPtr pScrn);
+
+/* DRM Xv support */
+Bool armada_drm_XvInit(ScrnInfoPtr pScrn);
+
+#endif
diff --git a/src/armada_drm_xv.c b/src/armada_drm_xv.c
new file mode 100644
index 0000000..b8188e0
--- /dev/null
+++ b/src/armada_drm_xv.c
@@ -0,0 +1,907 @@
+/*
+ * Marvell Armada DRM-based Xvideo 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 <stdint.h>
+#include <stdlib.h>
+
+#include <armada_bufmgr.h>
+
+#include "armada_drm.h"
+#include "xf86Crtc.h"
+#include "xf86xv.h"
+#include "fourcc.h"
+#include <X11/extensions/Xv.h>
+#include <X11/Xatom.h>
+
+#include "armada_ioctl.h"
+
+#define FOURCC_VYUY 0x59555956
+#define XVIMAGE_VYUY \
+ { \
+ FOURCC_VYUY, XvYUV, LSBFirst, \
+ { 'V', 'Y', 'U', 'Y', 0x00,0x00,0x00,0x10,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 }, \
+ 16, XvPacked, 1, \
+ 0, 0, 0, 0, \
+ 8, 8, 8, \
+ 1, 2, 2, \
+ 1, 1, 1, \
+ { 'V', 'Y', 'U', 'Y' }, \
+ XvTopToBottom, \
+ }
+
+#define FOURCC_I422 0x32323449
+#define XVIMAGE_I422 \
+ { \
+ FOURCC_I422, XvYUV, LSBFirst, \
+ { 'I', '4', '2', '2', 0x00,0x00,0x00,0x10,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 }, \
+ 16, XvPlanar, 3, \
+ 0, 0, 0, 0, \
+ 8, 8, 8, \
+ 1, 2, 2, \
+ 1, 1, 1, \
+ { 'Y', 'U', 'V' }, \
+ XvTopToBottom, \
+ }
+
+#define FOURCC_YV16 0x36315659
+#define XVIMAGE_YV16 \
+ { \
+ FOURCC_YV16, XvYUV, LSBFirst, \
+ { 'Y', 'V', '1', '6', 0x00,0x00,0x00,0x10,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 }, \
+ 16, XvPlanar, 3, \
+ 0, 0, 0, 0, \
+ 8, 8, 8, \
+ 1, 2, 2, \
+ 1, 1, 1, \
+ { 'Y', 'V', 'U' }, \
+ XvTopToBottom, \
+ }
+
+#define FOURCC_XVBO 0x4f425658
+#define XVIMAGE_XVBO \
+ { \
+ FOURCC_XVBO, XvYUV, LSBFirst, \
+ { 'X', 'V', 'B', 'O', 0x00,0x00,0x00,0x10,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 }, \
+ 16, XvPacked, 1, \
+ 0, 0, 0, 0, \
+ 8, 8, 8, \
+ 1, 2, 2, \
+ 1, 1, 1, \
+ { 'U', 'Y', 'V', 'Y' }, \
+ XvTopToBottom, \
+ }
+
+#define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE)
+
+static Atom xvAutoPaintColorKey;
+static Atom xvColorKey;
+static Atom xvBrightness;
+static Atom xvContrast;
+static Atom xvSaturation;
+static Atom xvDeinterlace;
+static Atom xvPipe;
+
+/* Size of physical addresses via BMM */
+typedef uint32_t phys_t;
+
+struct drm_xv {
+ struct armada_drm_info *drm;
+ xf86CrtcPtr desired_crtc;
+ Bool is_bmm;
+ Bool autopaint_colorkey;
+
+ /* Cache the information */
+ int image_fourcc;
+ short image_width;
+ short image_height;
+ const XF86ImageRec *image;
+ uint32_t image_flags;
+ uint32_t image_size;
+ int pitches[3];
+ int offsets[3];
+ int (*cvt)(struct drm_xv *, unsigned char *, struct drm_armada_bo **);
+
+ unsigned bo_idx;
+ struct drm_armada_bo *bo[3];
+ phys_t bo_phys[3];
+
+ RegionRec clipBoxes;
+
+ struct drm_armada_overlay_attrs attrs;
+ struct drm_armada_overlay_put_image arg;
+};
+
+static XF86VideoEncodingRec OverlayEncodings[] = {
+ { 0, "XV_IMAGE", 2048, 2048, { 1, 1 }, },
+};
+
+static XF86VideoFormatRec OverlayFormats[] = {
+ { 8, PseudoColor },
+ { 16, TrueColor },
+ { 24, TrueColor },
+ { 32, TrueColor },
+};
+
+static XF86AttributeRec OverlayAttributes[] = {
+ { XvSettable | XvGettable, 0, 1, "XV_AUTOPAINT_COLORKEY" },
+ { XvSettable | XvGettable, 0, 0x00ffffff, "XV_COLORKEY" },
+ { XvSettable | XvGettable, -128, 127, "XV_BRIGHTNESS" },
+ { XvSettable | XvGettable, 0, 255, "XV_CONTRAST" },
+ { XvSettable | XvGettable, 0, 1023, "XV_SATURATION" },
+ { XvSettable | XvGettable, -1, 0, "XV_PIPE" },
+};
+
+/*
+ * These are in order of preference. The I420/YV12 formats require
+ * conversion within the X server rather than the application, that's
+ * relatively easy to do, and moreover involves reading less data than
+ * I422/YV16. YV16 and VYUY are not common formats (vlc at least does
+ * not have any support for it but does have I422) so these comes at
+ * the very end, to try to avoid vlc complaining about them.
+ */
+static XF86ImageRec OverlayImages[] = {
+ XVIMAGE_I420,
+ XVIMAGE_YV12,
+ XVIMAGE_I422,
+ XVIMAGE_YUY2,
+ XVIMAGE_UYVY,
+ XVIMAGE_VYUY,
+ XVIMAGE_YV16,
+ XVIMAGE_XVBO,
+};
+
+/* It would be nice to be given the image pointer... */
+static const XF86ImageRec *armada_drm_ovl_get_img(int image)
+{
+ const XF86ImageRec *img = NULL;
+ int i;
+
+ for (i = 0; i < sizeof(OverlayImages) / sizeof(XF86ImageRec); i++)
+ if (OverlayImages[i].id == image) {
+ img = &OverlayImages[i];
+ break;
+ }
+
+ return img;
+}
+
+static int armada_drm_ovl_set_img_info(const XF86ImageRec *img,
+ int *pitch, int *offset, short width, short height)
+{
+ int ret = 0;
+
+ if (img->id == FOURCC_XVBO) {
+ /* Our special XVBO format is only two uint32_t */
+ pitch[0] = 2 * sizeof(uint32_t);
+ offset[0] = 0;
+ ret = pitch[0];
+ } else if (img->format == XvPlanar) {
+ int size[3];
+
+ pitch[0] = width / img->horz_y_period;
+ pitch[1] = width / img->horz_u_period;
+ pitch[2] = width / img->horz_v_period;
+ size[0] = (pitch[0] * (height / img->vert_y_period) + 7) & ~7;
+ size[1] = (pitch[1] * (height / img->vert_u_period) + 7) & ~7;
+ size[2] = (pitch[2] * (height / img->vert_v_period) + 7) & ~7;
+ offset[0] = 0;
+ offset[1] = offset[0] + size[0];
+ offset[2] = offset[1] + size[1];
+
+ ret = size[0] + size[1] + size[2];
+ } else if (img->format == XvPacked) {
+ offset[0] = 0;
+ pitch[0] = width * ((img->bits_per_pixel + 7) / 8);
+ ret = offset[0] + pitch[0] * height;
+ }
+
+ return ret;
+}
+
+static void armada_drm_ovl_bo_free(struct drm_xv *drmxv)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(drmxv->bo); i++)
+ if (drmxv->bo[i]) {
+ drm_armada_bo_put(drmxv->bo[i]);
+ drmxv->bo[i] = NULL;
+ }
+}
+
+static struct drm_armada_bo *armada_drm_ovl_get_bo(struct drm_xv *drmxv,
+ size_t size, size_t width)
+{
+ struct armada_drm_info *drm = drmxv->drm;
+ struct drm_armada_bo *bo;
+
+ bo = drm_armada_bo_dumb_create(drm->bufmgr, width, size / width / 4, 32);
+ if (bo && drm_armada_bo_map(bo)) {
+ drm_armada_bo_put(bo);
+ bo = NULL;
+ }
+ return bo;
+}
+
+static void armada_drm_ovl_StopVideo(ScrnInfoPtr pScrn, pointer data, Bool cleanup)
+{
+ struct drm_xv *drmxv = data;
+ struct drm_armada_overlay_put_image arg;
+ int ret;
+
+ RegionEmpty(&drmxv->clipBoxes);
+
+ memset(&arg, 0, sizeof(arg));
+ ret = drmIoctl(drmxv->drm->fd, DRM_IOCTL_ARMADA_OVERLAY_PUT_IMAGE, &arg);
+ if (ret)
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "[drm] unable to stop overlay: %s\n", strerror(errno));
+
+ if (cleanup) {
+ drmxv->image = NULL;
+ armada_drm_ovl_bo_free(drmxv);
+ }
+}
+
+static int armada_drm_ovl_SetPortAttribute(ScrnInfoPtr pScrn, Atom attribute, INT32 value, pointer data)
+{
+ struct drm_xv *drmxv = data;
+ Bool update_attrs = TRUE;
+ int ret = Success;
+
+ if (attribute == xvAutoPaintColorKey) {
+ drmxv->autopaint_colorkey = !!value;
+ if (value == 0)
+ drmxv->attrs.color_key = 0;
+ } else if (attribute == xvColorKey)
+ drmxv->attrs.color_key = value;
+ else if (attribute == xvBrightness)
+ drmxv->attrs.brightness = value;
+ else if (attribute == xvContrast)
+ drmxv->attrs.contrast = value;
+ else if (attribute == xvSaturation)
+ drmxv->attrs.saturation = value;
+ else if (attribute == xvDeinterlace)
+ update_attrs = FALSE;
+ else if (attribute == xvPipe) {
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+
+ if (value < -1 || value >= config->num_crtc)
+ return BadValue;
+ if (value == -1)
+ drmxv->desired_crtc = NULL;
+ else
+ drmxv->desired_crtc = config->crtc[value];
+
+ update_attrs = FALSE;
+ } else
+ ret = BadMatch;
+
+ if (ret == Success && update_attrs) {
+ struct drm_armada_overlay_attrs arg;
+
+ arg = drmxv->attrs;
+ arg.flags = ARMADA_OVERLAY_UPDATE_ATTRS;
+ drmIoctl(drmxv->drm->fd, DRM_IOCTL_ARMADA_OVERLAY_ATTRS, &arg);
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "SetPortAttribute: attrib %#lx value %#lx ret %u\n",
+ attribute, value, ret);
+
+ return ret;
+}
+
+static int armada_drm_ovl_GetPortAttribute(ScrnInfoPtr pScrn, Atom attribute,
+ INT32 *value, pointer data)
+{
+ struct drm_xv *drmxv = data;
+ int ret = Success;
+
+ if (attribute == xvAutoPaintColorKey)
+ *value = drmxv->autopaint_colorkey;
+ else if (attribute == xvColorKey)
+ *value = drmxv->attrs.color_key;
+ else if (attribute == xvBrightness)
+ *value = drmxv->attrs.brightness;
+ else if (attribute == xvContrast)
+ *value = drmxv->attrs.contrast;
+ else if (attribute == xvSaturation)
+ *value = drmxv->attrs.saturation;
+ else if (attribute == xvPipe) {
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int i;
+
+ for (i = 0; i < config->num_crtc; i++)
+ if (config->crtc[i] == drmxv->desired_crtc)
+ break;
+
+ if (i == config->num_crtc)
+ i = -1;
+
+ *value = i;
+ } else
+ ret = BadMatch;
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "GetPortAttribute: attrib %#lx value %#lx ret %u\n",
+ attribute, *value, ret);
+
+ return ret;
+}
+
+static void armada_drm_ovl_QueryBestSize(ScrnInfoPtr pScrn, Bool motion,
+ short vid_w, short vid_h, short drw_w, short drw_h,
+ unsigned int *p_w, unsigned int *p_h, pointer data)
+{
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "QueryBestSize: vid %dx%d drw %dx%d\n",
+ vid_w, vid_h, drw_w, drw_h);
+ *p_w = maxt(vid_w, drw_w);
+ *p_h = maxt(vid_h, drw_h);
+}
+
+#define BMM_SHM_MAGIC1 0x13572468
+#define BMM_SHM_MAGIC2 0x24681357
+
+static uint32_t armada_drm_bmm_chk(unsigned char *buf, uint32_t len)
+{
+ uint32_t i, chk, *ptr = (uint32_t *)buf;
+
+ for (chk = i = 0; i < len; i++)
+ chk ^= ptr[i];
+
+ return chk;
+}
+
+static Bool armada_drm_is_bmm(unsigned char *buf)
+{
+ uint32_t *ptr, len;
+
+ if ((uintptr_t)buf & (sizeof(*ptr) - 1))
+ return FALSE;
+
+ ptr = (uint32_t *)buf;
+ if (*ptr != BMM_SHM_MAGIC1)
+ return FALSE;
+
+ len = 2 + ptr[1];
+ return armada_drm_bmm_chk(buf, len) == ptr[len];
+}
+
+static phys_t armada_drm_bmm_getbuf(unsigned char *buf)
+{
+ uint32_t *ptr, len;
+
+ if ((uintptr_t)buf & (sizeof(*ptr) - 1))
+ return (phys_t)-1;
+
+ ptr = (uint32_t *)buf;
+ if (*ptr != BMM_SHM_MAGIC1)
+ return (phys_t)-1;
+
+ len = 2 + ptr[1];
+ if (armada_drm_bmm_chk(buf, len) != ptr[len])
+ return (phys_t)-1;
+
+ return ptr[2];
+}
+
+static void armada_drm_bmm_putbuf(unsigned char *buf, phys_t phys)
+{
+ uint32_t *ptr = (uint32_t *)buf;
+
+ *ptr++ = BMM_SHM_MAGIC2;
+ *ptr++ = 1;
+ *ptr++ = phys;
+ *ptr = armada_drm_bmm_chk(buf, 3);
+}
+
+static int armada_drm_ovl_bmm(struct drm_xv *drmxv, unsigned char *buf, struct drm_armada_bo **pbo)
+{
+ struct drm_armada_bo *old, *bo;
+ phys_t phys;
+
+ /* Marvell Dove special protocol */
+ phys = armada_drm_bmm_getbuf(buf);
+ if (phys == (phys_t)-1)
+ return BadAlloc;
+
+ /* Map the passed buffer into a bo */
+ bo = drm_armada_bo_create_phys(drmxv->drm->bufmgr, phys, drmxv->image_size);
+ if (!bo)
+ return BadAlloc;
+
+ /* Free the old bo, and pass it back */
+ old = drmxv->bo[drmxv->bo_idx];
+ if (old) {
+ drm_armada_bo_put(old);
+ armada_drm_bmm_putbuf(buf, drmxv->bo_phys[drmxv->bo_idx]);
+ }
+ drmxv->bo[drmxv->bo_idx] = bo;
+ drmxv->bo_phys[drmxv->bo_idx] = phys;
+
+ *pbo = bo;
+
+ drm_armada_bo_get(bo);
+
+ return Success;
+}
+
+static int armada_drm_ovl_cvt(struct drm_xv *drmxv, unsigned char *src, struct drm_armada_bo **pbo)
+{
+ struct drm_armada_bo *bo;
+ unsigned char *dst;
+ size_t size;
+ int i;
+
+ /* Standard XV protocol */
+ bo = drmxv->bo[drmxv->bo_idx];
+ if (!bo)
+ return BadAlloc;
+
+ if (++drmxv->bo_idx >= ARRAY_SIZE(drmxv->bo))
+ drmxv->bo_idx = 0;
+
+ *pbo = bo;
+
+ /* Convert YV12 to YV16 or I420 to I422 */
+ dst = bo->ptr;
+ size = drmxv->offsets[1];
+
+ /* Copy Y */
+ memcpy(dst, src, size);
+
+ dst += size;
+ src += size;
+
+ /* Copy U and V, doubling the number of pixels vertically */
+ size = drmxv->pitches[1];
+ for (i = drmxv->image_height; i > 0; i--) {
+ memcpy(dst, src, size);
+ dst += size;
+ memcpy(dst, src, size);
+ dst += size;
+ src += size;
+ }
+
+ drm_armada_bo_get(bo);
+
+ return Success;
+}
+
+static int armada_drm_ovl_std(struct drm_xv *drmxv, unsigned char *src, struct drm_armada_bo **pbo)
+{
+ struct drm_armada_bo *bo;
+
+ /* Standard XV protocol */
+ bo = drmxv->bo[drmxv->bo_idx];
+ if (!bo)
+ return BadAlloc;
+
+ if (++drmxv->bo_idx >= ARRAY_SIZE(drmxv->bo))
+ drmxv->bo_idx = 0;
+
+ *pbo = bo;
+
+ memcpy(bo->ptr, src, drmxv->image_size);
+
+ drm_armada_bo_get(bo);
+
+ return Success;
+}
+
+static int armada_drm_ovl_SetupStd(struct drm_xv *drmxv, int image,
+ short width, short height)
+{
+ int i, size, image2 = image;
+ const XF86ImageRec *img;
+
+ drmxv->image = NULL;
+
+ /*
+ * We can't actually do YV12 or I420 - we convert to YV16
+ * or YV16 with U and V swapped.
+ */
+ if (image2 == FOURCC_YV12)
+ image2 = FOURCC_YV16;
+ if (image2 == FOURCC_I420)
+ image2 = FOURCC_I422;
+
+ /*
+ * Lookup the image type. Note that the server already did this in
+ * Xext/xvdisp.c, but xf86XVPutImage() in hw/xfree86/common/xf86xv.c
+ * threw that information away.
+ */
+ img = armada_drm_ovl_get_img(image2);
+ if (!img)
+ return BadMatch;
+
+ size = armada_drm_ovl_set_img_info(img, drmxv->pitches, drmxv->offsets,
+ width, height);
+ if (!size)
+ return BadAlloc;
+
+ drmxv->arg.flags = ARMADA_OVERLAY_ENABLE;
+ if (img->type == XvYUV) {
+ if (img->vert_u_period == 1)
+ drmxv->arg.flags |= ARMADA_OVERLAY_YUV422;
+ else
+ drmxv->arg.flags |= ARMADA_OVERLAY_YUV420;
+ }
+
+ if (img->format == XvPlanar) {
+ drmxv->arg.flags |= ARMADA_OVERLAY_YUV_PLANAR;
+ for (i = 0; i < img->num_planes; i++) {
+ switch (img->component_order[i]) {
+ case 'Y':
+ drmxv->arg.offset_Y = drmxv->offsets[i];
+ drmxv->arg.stride_Y = drmxv->pitches[i];
+ break;
+ case 'U':
+ drmxv->arg.offset_U = drmxv->offsets[i];
+ drmxv->arg.stride_UV = drmxv->pitches[i];
+ break;
+ case 'V':
+ drmxv->arg.offset_V = drmxv->offsets[i];
+ drmxv->arg.stride_UV = drmxv->pitches[i];
+ break;
+ }
+ }
+ } else {
+ drmxv->arg.flags |= ARMADA_OVERLAY_YUV_PACKED;
+ drmxv->arg.offset_Y = drmxv->offsets[0];
+ drmxv->arg.offset_U = drmxv->offsets[0];
+ drmxv->arg.offset_V = drmxv->offsets[0];
+ drmxv->arg.stride_Y = drmxv->pitches[0];
+ drmxv->arg.stride_UV = drmxv->pitches[0];
+
+ switch (image) {
+ case FOURCC_VYUY:
+ drmxv->arg.flags |= ARMADA_OVERLAY_UV_SWAP;
+ case FOURCC_UYVY:
+ drmxv->arg.flags |= ARMADA_OVERLAY_Y_SWAP;
+ case FOURCC_YUY2:
+ break;
+ default:
+ return BadAlloc;
+ }
+ }
+
+ armada_drm_ovl_bo_free(drmxv);
+
+ if (drmxv->is_bmm) {
+ drmxv->cvt = armada_drm_ovl_bmm;
+ } else {
+ if (image != image2)
+ drmxv->cvt = armada_drm_ovl_cvt;
+ else
+ drmxv->cvt = armada_drm_ovl_std;
+
+ for (i = 0; i < ARRAY_SIZE(drmxv->bo); i++) {
+ drmxv->bo[i] = armada_drm_ovl_get_bo(drmxv, size, width);
+ if (!drmxv->bo[i]) {
+ armada_drm_ovl_bo_free(drmxv);
+ return BadAlloc;
+ }
+ }
+ }
+
+ drmxv->image = img;
+ drmxv->image_size = size;
+ drmxv->image_width = width;
+ drmxv->image_height = height;
+ drmxv->image_fourcc = image;
+
+ return Success;
+}
+
+static int armada_drm_ovl_clip(ScrnInfoPtr pScrn, struct drm_xv *drmxv,
+ short src_x, short src_y, short drw_x, short drw_y,
+ short src_w, short src_h, short drw_w, short drw_h,
+ RegionPtr clipBoxes, short width, short height, xf86CrtcPtr *crtcp)
+{
+ struct armada_crtc_info *drmc;
+ xf86CrtcPtr crtc = NULL;
+ INT32 xa, xb, ya, yb;
+ short src_sw, src_sh;
+ BoxRec dst;
+
+ xa = src_x;
+ xb = src_x + src_w;
+ ya = src_y;
+ yb = src_y + src_h;
+
+ dst.x1 = drw_x;
+ dst.x2 = drw_x + drw_w;
+ dst.y1 = drw_y;
+ dst.y2 = drw_y + drw_h;
+
+ if (!xf86_crtc_clip_video_helper(pScrn, &crtc, drmxv->desired_crtc,
+ &dst, &xa, &xb, &ya, &yb, clipBoxes,
+ width, height))
+ return BadAlloc;
+
+ *crtcp = crtc;
+
+ if (!crtc)
+ return Success;
+
+ src_sw = width * ((float)(dst.x2 - dst.x1) / drw_w);
+ src_sh = height * ((float)(dst.y2 - dst.y1) / drw_h);
+
+ /* Convert to coordinates on this CRTC. */
+ drw_w = dst.x2 - dst.x1;
+ drw_h = dst.y2 - dst.y1;
+ drw_x = dst.x1 - crtc->x;
+ drw_y = dst.y1 - crtc->y;
+
+{
+ static int dx, dy, dw, dh, sow, soh, ssw, ssh;
+ if (dx != drw_x || dy != drw_y || dw != drw_w || dh != drw_h || sow != width || soh != height || ssw != src_sw || ssh != src_sh) {
+ dx = drw_x; dy = drw_y; dw = drw_w; dh = drw_h; sow = width; soh = height; ssw = src_sw; ssh = src_sh;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "os[%dx%d] ss[%dx%d] dst[%d,%d %dx%d]\n",
+ sow, soh, ssw, ssh, dx, dy, dw, dh);
+ }
+}
+
+ drmc = crtc->driver_private;
+
+ drmxv->arg.src_width = width;
+ drmxv->arg.src_height = height;
+ drmxv->arg.src_scan_width = src_sw;
+ drmxv->arg.src_scan_height = src_sh;
+ drmxv->arg.crtc_id = drmc->mode_crtc->crtc_id;
+ drmxv->arg.dst_x = drw_x;
+ drmxv->arg.dst_y = drw_y;
+ drmxv->arg.dst_width = drw_w;
+ drmxv->arg.dst_height = drw_h;
+
+ return Success;
+}
+
+static int armada_drm_ovl_PutImage(ScrnInfoPtr pScrn,
+ short src_x, short src_y, short drw_x, short drw_y,
+ short src_w, short src_h, short drw_w, short drw_h,
+ int image, unsigned char *buf, short width, short height,
+ Bool sync, RegionPtr clipBoxes, pointer data, DrawablePtr pDraw)
+{
+ xf86CrtcPtr crtc = NULL;
+ struct drm_xv *drmxv = data;
+ struct drm_armada_bo *bo;
+ int ret;
+
+// xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+// "PutImage: s%dx%x+%d+%d d%dx%d+%d+%d image %x %dx%d sync %u\n",
+// src_w, src_h, src_x, src_y, drw_w, drw_h, drw_x, drw_y,
+// image, width, height, sync);
+
+ ret = armada_drm_ovl_clip(pScrn, drmxv, src_x, src_y, drw_x, drw_y,
+ src_w, src_h, drw_w, drw_h, clipBoxes,
+ width, height, &crtc);
+ if (ret != Success)
+ return ret;
+
+ if (!crtc) {
+ armada_drm_ovl_StopVideo(pScrn, data, TRUE);
+ return Success;
+ }
+
+ if (image == FOURCC_XVBO) {
+ /* XVBO support allows applications to prepare the DRM buffer
+ * object themselves, and pass a global name to the X server to
+ * update the hardware with. This is similar to Intel XvMC
+ * support, except we also allow the image format to be
+ * specified via a fourcc as the first word.
+ */
+ uint32_t *p = (uint32_t *)buf;
+ uint32_t fmt = p[0];
+ uint32_t name = p[1];
+
+ if (!drmxv->image || drmxv->image_fourcc != fmt ||
+ drmxv->image_width != width ||
+ drmxv->image_height != height) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "[drm] XVBO: fourcc %08x name %08x\n",
+ fmt, name);
+ drmxv->is_bmm = 1;
+ ret = armada_drm_ovl_SetupStd(drmxv, fmt, width, height);
+ if (ret != Success) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] XVBO: SetupStd failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /*
+ * Convert the global name back to a buffer object, so we have
+ * a handle to pass to the kernel.
+ */
+ bo = drm_armada_bo_create_from_name(drmxv->drm->bufmgr, name);
+ if (!bo) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "[drm] XVBO: bo_create_from_name failed: %d\n", ret);
+ return BadAlloc;
+ }
+ } else {
+ if (!drmxv->image || drmxv->image_fourcc != image)
+ drmxv->is_bmm = armada_drm_is_bmm(buf);
+
+ if (!drmxv->image || drmxv->image_fourcc != image ||
+ drmxv->image_width != width || drmxv->image_height != height) {
+ ret = armada_drm_ovl_SetupStd(drmxv, image, width, height);
+ if (ret != Success)
+ return ret;
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "[drm] bmm %u image fourcc %08x flags %08x\n",
+ drmxv->is_bmm, image, drmxv->arg.flags);
+ }
+
+ ret = drmxv->cvt(drmxv, buf, &bo);
+ if (ret != Success)
+ return ret;
+ }
+
+ drmxv->arg.bo_handle = bo->handle;
+
+ ret = drmIoctl(drmxv->drm->fd, DRM_IOCTL_ARMADA_OVERLAY_PUT_IMAGE,
+ &drmxv->arg);
+
+ /*
+ * The kernel will keep a reference internally to the buffer object
+ * while it is being displayed.
+ */
+ drm_armada_bo_put(bo);
+
+ /*
+ * Finally, fill the clip boxes; do this after we've done the ioctl
+ * so we don't impact on latency.
+ */
+ if (drmxv->autopaint_colorkey &&
+ !RegionEqual(&drmxv->clipBoxes, clipBoxes)) {
+ RegionCopy(&drmxv->clipBoxes, clipBoxes);
+
+ xf86XVFillKeyHelper(pScrn->pScreen, drmxv->attrs.color_key, clipBoxes);
+ }
+
+ return Success;
+}
+
+static int armada_drm_ovl_ReputImage(ScrnInfoPtr pScrn,
+ short src_x, short src_y, short drw_x, short drw_y,
+ short src_w, short src_h, short drw_w, short drw_h,
+ RegionPtr clipBoxes, pointer data, DrawablePtr pDraw)
+{
+ xf86CrtcPtr crtc = NULL;
+ struct drm_xv *drmxv = data;
+ int ret;
+
+ if (!drmxv->image)
+ return Success;
+
+ ret = armada_drm_ovl_clip(pScrn, drmxv, src_x, src_y, drw_x, drw_y,
+ src_w, src_h, drw_w, drw_h, clipBoxes,
+ drmxv->image_width, drmxv->image_height, &crtc);
+ if (ret)
+ return ret;
+
+ if (!crtc) {
+ armada_drm_ovl_StopVideo(pScrn, data, TRUE);
+ return Success;
+ }
+
+ ret = drmIoctl(drmxv->drm->fd, DRM_IOCTL_ARMADA_OVERLAY_PUT_IMAGE,
+ &drmxv->arg);
+
+ if (drmxv->autopaint_colorkey &&
+ !RegionEqual(&drmxv->clipBoxes, clipBoxes)) {
+ RegionCopy(&drmxv->clipBoxes, clipBoxes);
+
+ xf86XVFillKeyHelper(pScrn->pScreen, drmxv->attrs.color_key, clipBoxes);
+ }
+
+ return Success;
+}
+
+static int armada_drm_ovl_QueryImageAttributes(ScrnInfoPtr pScrn,
+ int image, unsigned short *width, unsigned short *height,
+ int *pitches, int *offsets)
+{
+ const XF86ImageRec *img;
+ unsigned ret = 0;
+
+ *width = (*width + 1) & ~1;
+ *height = (*height + 1) & ~1;
+
+ img = armada_drm_ovl_get_img(image);
+ if (img) {
+ int pitch[3], offset[3];
+
+ ret = armada_drm_ovl_set_img_info(img, pitch, offset,
+ *width, *height);
+
+ if (ret) {
+ if (pitches)
+ memcpy(pitches, pitch, sizeof(*pitches) * img->num_planes);
+ if (offsets)
+ memcpy(offsets, offset, sizeof(*offsets) * img->num_planes);
+ }
+ }
+ return ret;
+}
+
+Bool armada_drm_XvInit(ScrnInfoPtr pScrn)
+{
+ ScreenPtr scrn = screenInfo.screens[pScrn->scrnIndex];
+ XF86VideoAdaptorPtr xv[1], p;
+ struct armada_drm_info *drm = GET_DRM_INFO(pScrn);
+ struct drm_xv *drmxv;
+ DevUnion priv[1];
+ Bool ret;
+
+ if (!xvColorKey) {
+ xvAutoPaintColorKey = MAKE_ATOM("XV_AUTOPAINT_COLORKEY");
+ xvColorKey = MAKE_ATOM("XV_COLORKEY");
+ xvBrightness = MAKE_ATOM("XV_BRIGHTNESS");
+ xvContrast = MAKE_ATOM("XV_CONTRAST");
+ xvSaturation = MAKE_ATOM("XV_SATURATION");
+ xvDeinterlace = MAKE_ATOM("XV_DEINTERLACE");
+ xvPipe = MAKE_ATOM("XV_PIPE");
+ }
+
+ drmxv = calloc(1, sizeof *drmxv);
+ if (!drmxv)
+ return FALSE;
+
+ drmxv->drm = drm;
+ drmxv->autopaint_colorkey = TRUE;
+
+ drmIoctl(drm->fd, DRM_IOCTL_ARMADA_OVERLAY_ATTRS, &drmxv->attrs);
+
+ p = xf86XVAllocateVideoAdaptorRec(pScrn);
+ if (!p) {
+ free(drmxv);
+ return FALSE;
+ }
+
+ priv[0].ptr = drmxv;
+
+ p->type = XvWindowMask | XvInputMask | XvImageMask;
+ p->flags = VIDEO_OVERLAID_IMAGES;
+ p->name = "Marvell Armada Overlay Video";
+ p->nEncodings = sizeof(OverlayEncodings) / sizeof(XF86VideoEncodingRec);
+ p->pEncodings = OverlayEncodings;
+ p->nFormats = sizeof(OverlayFormats) / sizeof(XF86VideoFormatRec);
+ p->pFormats = OverlayFormats;
+ p->nPorts = 1;
+ p->pPortPrivates = priv;
+ p->nAttributes = sizeof(OverlayAttributes) / sizeof(XF86AttributeRec);
+ p->pAttributes = OverlayAttributes;
+ p->nImages = sizeof(OverlayImages) / sizeof(XF86ImageRec);
+ p->pImages = OverlayImages;
+ p->StopVideo = armada_drm_ovl_StopVideo;
+ p->SetPortAttribute = armada_drm_ovl_SetPortAttribute;
+ p->GetPortAttribute = armada_drm_ovl_GetPortAttribute;
+ p->QueryBestSize = armada_drm_ovl_QueryBestSize;
+ p->PutImage = armada_drm_ovl_PutImage;
+ p->ReputImage = armada_drm_ovl_ReputImage;
+ p->QueryImageAttributes = armada_drm_ovl_QueryImageAttributes;
+
+ xv[0] = p;
+ ret = xf86XVScreenInit(scrn, xv, 1);
+ free(p);
+
+ if (!ret)
+ free(drmxv);
+
+ return ret;
+}
diff --git a/src/armada_ioctl.h b/src/armada_ioctl.h
new file mode 100644
index 0000000..780312c
--- /dev/null
+++ b/src/armada_ioctl.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012 Russell King
+ * With inspiration from the i915 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef DRM_ARMADA_IOCTL_H
+#define DRM_ARMADA_IOCTL_H
+
+#define DRM_ARMADA_GEM_CREATE 0x00
+#define DRM_ARMADA_GEM_CREATE_PHYS 0x01
+#define DRM_ARMADA_GEM_MMAP 0x02
+#define DRM_ARMADA_GEM_PWRITE 0x03
+#define DRM_ARMADA_GEM_PROP 0x04
+#define DRM_ARMADA_GEM_CACHE 0x05
+#define DRM_ARMADA_OVERLAY_PUT_IMAGE 0x06
+#define DRM_ARMADA_OVERLAY_ATTRS 0x07
+
+#define ARMADA_IOCTL(dir,name,str) \
+ DRM_##dir(DRM_COMMAND_BASE + DRM_ARMADA_##name, struct drm_armada_##str)
+
+struct drm_armada_gem_create {
+ uint32_t height;
+ uint32_t width;
+ uint32_t bpp;
+ uint32_t handle;
+ uint32_t pitch;
+ uint32_t size;
+};
+#define DRM_IOCTL_ARMADA_GEM_CREATE \
+ ARMADA_IOCTL(IOWR, GEM_CREATE, gem_create)
+
+struct drm_armada_gem_create_phys {
+ uint32_t size;
+ uint32_t handle;
+ uint64_t phys;
+};
+#define DRM_IOCTL_ARMADA_GEM_CREATE_PHYS \
+ ARMADA_IOCTL(IOWR, GEM_CREATE_PHYS, gem_create_phys)
+
+struct drm_armada_gem_mmap {
+ uint32_t handle;
+ uint32_t pad;
+ uint64_t offset;
+ uint64_t size;
+ uint64_t addr;
+};
+#define DRM_IOCTL_ARMADA_GEM_MMAP \
+ ARMADA_IOCTL(IOWR, GEM_MMAP, gem_mmap)
+
+struct drm_armada_gem_pwrite {
+ uint32_t handle;
+ uint32_t offset;
+ uint32_t size;
+ uint64_t ptr;
+};
+#define DRM_IOCTL_ARMADA_GEM_PWRITE \
+ ARMADA_IOCTL(IOW, GEM_PWRITE, gem_pwrite)
+
+struct drm_armada_gem_prop {
+ uint64_t phys;
+ uint32_t handle;
+};
+#define DRM_IOCTL_ARMADA_GEM_PROP \
+ ARMADA_IOCTL(IOWR, GEM_PROP, gem_prop)
+
+struct drm_armada_gem_cache {
+ uint64_t ptr;
+ uint32_t handle;
+ uint32_t size;
+ uint32_t op;
+};
+#define DRM_IOCTL_ARMADA_GEM_CACHE \
+ ARMADA_IOCTL(IOW, GEM_CACHE, gem_cache)
+
+/* Same as Intel I915 */
+struct drm_armada_overlay_put_image {
+ uint32_t flags;
+#define ARMADA_OVERLAY_TYPE_MASK 0x000000ff
+#define ARMADA_OVERLAY_YUV_PLANAR 0x00000001
+#define ARMADA_OVERLAY_YUV_PACKED 0x00000002
+#define ARMADA_OVERLAY_RGB 0x00000003
+#define ARMADA_OVERLAY_DEPTH_MASK 0x0000ff00
+#define ARMADA_OVERLAY_RGB24 0x00001000
+#define ARMADA_OVERLAY_RGB16 0x00002000
+#define ARMADA_OVERLAY_RGB15 0x00003000
+#define ARMADA_OVERLAY_YUV422 0x00000100
+#define ARMADA_OVERLAY_YUV411 0x00000200
+#define ARMADA_OVERLAY_YUV420 0x00000300
+#define ARMADA_OVERLAY_YUV410 0x00000400
+#define ARMADA_OVERLAY_SWAP_MASK 0x00ff0000
+#define ARMADA_OVERLAY_NO_SWAP 0x00000000
+#define ARMADA_OVERLAY_UV_SWAP 0x00010000
+#define ARMADA_OVERLAY_Y_SWAP 0x00020000
+#define ARMADA_OVERLAY_Y_AND_UV_SWAP 0x00030000
+#define ARMADA_OVERLAY_FLAGS_MASK 0xff000000
+#define ARMADA_OVERLAY_ENABLE 0x01000000
+ uint32_t bo_handle;
+ uint16_t stride_Y;
+ uint16_t stride_UV;
+ uint32_t offset_Y;
+ uint32_t offset_U;
+ uint32_t offset_V;
+ uint16_t src_width;
+ uint16_t src_height;
+ uint16_t src_scan_width;
+ uint16_t src_scan_height;
+ uint32_t crtc_id;
+ uint16_t dst_x;
+ uint16_t dst_y;
+ uint16_t dst_width;
+ uint16_t dst_height;
+};
+#define DRM_IOCTL_ARMADA_OVERLAY_PUT_IMAGE \
+ ARMADA_IOCTL(IOW, OVERLAY_PUT_IMAGE, overlay_put_image)
+
+/* Same as Intel I915 */
+struct drm_armada_overlay_attrs {
+ uint32_t flags;
+#define ARMADA_OVERLAY_UPDATE_ATTRS (1<<0)
+#define ARMADA_OVERLAY_UPDATE_GAMMA (1<<1)
+ uint32_t color_key;
+ int32_t brightness;
+ uint32_t contrast;
+ uint32_t saturation;
+ uint32_t gamma0;
+ uint32_t gamma1;
+ uint32_t gamma2;
+ uint32_t gamma3;
+ uint32_t gamma4;
+ uint32_t gamma5;
+};
+#define DRM_IOCTL_ARMADA_OVERLAY_ATTRS \
+ ARMADA_IOCTL(IOWR, OVERLAY_ATTRS, overlay_attrs)
+
+#endif
diff --git a/src/armada_module.c b/src/armada_module.c
new file mode 100644
index 0000000..13483ea
--- /dev/null
+++ b/src/armada_module.c
@@ -0,0 +1,138 @@
+/*
+ * Marvell Armada DRM-based 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 "armada_drm.h"
+
+#define ARMADA_VERSION 4000
+#define ARMADA_NAME "armada"
+#define ARMADA_DRIVER_NAME "armada"
+
+/* Supported "chipsets" */
+static SymTabRec armada_chipsets[] = {
+// { 0, "88AP16x" },
+ { 0, "88AP510" },
+ { -1, NULL }
+};
+
+static void armada_identify(int flags)
+{
+ xf86PrintChipsets(ARMADA_NAME, "Support for Marvell LCD Controller",
+ armada_chipsets);
+}
+
+static Bool armada_probe(DriverPtr drv, int flags)
+{
+ GDevPtr *devSections;
+ int i, numDevSections;
+ Bool foundScreen = FALSE;
+
+ if (flags & PROBE_DETECT)
+ return FALSE;
+
+ numDevSections = xf86MatchDevice(ARMADA_DRIVER_NAME, &devSections);
+ if (numDevSections <= 0)
+ return FALSE;
+
+ for (i = 0; i < numDevSections; i++) {
+ ScrnInfoPtr pScrn;
+ int entity;
+
+ entity = xf86ClaimFbSlot(drv, 0, devSections[i], TRUE);
+ pScrn = xf86ConfigFbEntity(NULL, 0, entity,
+ NULL, NULL, NULL, NULL);
+
+ if (pScrn) {
+ foundScreen = TRUE;
+
+ pScrn->driverVersion = ARMADA_VERSION;
+ pScrn->driverName = ARMADA_DRIVER_NAME;
+ pScrn->name = ARMADA_NAME;
+ pScrn->Probe = NULL;
+
+ armada_drm_init_screen(pScrn);
+ }
+ }
+
+ free(devSections);
+
+ return foundScreen;
+}
+
+static const OptionInfoRec *armada_available_options(int chipid, int busid)
+{
+ extern const OptionInfoRec armada_drm_options[];
+ return armada_drm_options;
+}
+
+static Bool
+armada_driver_func(ScrnInfoPtr pScrn, xorgDriverFuncOp op, pointer ptr)
+{
+ xorgHWFlags *flag;
+
+ switch (op) {
+ case GET_REQUIRED_HW_INTERFACES:
+ flag = (CARD32*)ptr;
+ (*flag) = 0;
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+_X_EXPORT DriverRec armada_driver = {
+ .driverVersion = ARMADA_VERSION,
+ .driverName = ARMADA_DRIVER_NAME,
+ .Identify = armada_identify,
+ .Probe = armada_probe,
+ .AvailableOptions = armada_available_options,
+ .driverFunc = armada_driver_func,
+};
+
+#ifdef XFree86LOADER
+
+static pointer armada_setup(pointer module, pointer opts, int *errmaj,
+ int *errmin)
+{
+ static Bool setupDone = FALSE;
+
+ if (setupDone) {
+ if (errmaj)
+ *errmaj = LDR_ONCEONLY;
+ return NULL;
+ }
+
+ setupDone = TRUE;
+
+ xf86AddDriver(&armada_driver, module, HaveDriverFuncs);
+
+ return (pointer) 1;
+}
+
+static XF86ModuleVersionInfo armada_version = {
+ .modname = "armada",
+ .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_VIDEODRV,
+ .abiversion = ABI_VIDEODRV_VERSION,
+ .moduleclass = MOD_CLASS_VIDEODRV,
+ .checksum = { 0, 0, 0, 0 },
+};
+
+_X_EXPORT XF86ModuleData armadaModuleData = {
+ .vers = &armada_version,
+ .setup = armada_setup,
+};
+
+#endif /* XFree86LOADER */
diff --git a/src/vivante.c b/src/vivante.c
new file mode 100644
index 0000000..d9bdb5c
--- /dev/null
+++ b/src/vivante.c
@@ -0,0 +1,661 @@
+/*
+ * 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 <unistd.h>
+
+#include <armada_bufmgr.h>
+
+#include "armada_drm.h"
+
+#include "fb.h"
+#include "gcstruct.h"
+#include "xf86.h"
+
+#include <gc_hal.h>
+
+#include "vivante.h"
+#include "vivante_accel.h"
+#include "vivante_dri2.h"
+#include "vivante_unaccel.h"
+#include "vivante_utils.h"
+
+vivante_Key vivante_pixmap_index;
+vivante_Key vivante_screen_index;
+
+static Bool vivante_map_bo_to_gpu(struct vivante *vivante,
+ struct drm_armada_bo *bo, void **info, uint32_t *handle)
+{
+ *handle = drm_armada_bo_phys(bo);
+ return TRUE;
+}
+
+void vivante_free_pixmap(PixmapPtr pixmap)
+{
+ struct vivante_pixmap *vPix = vivante_get_pixmap_priv(pixmap);
+ struct vivante *vivante;
+
+ if (vPix) {
+ vivante = vivante_get_screen_priv(pixmap->drawable.pScreen);
+ vivante_batch_wait_commit(vivante, vPix);
+ if (vPix->owner == GPU)
+ vivante_unmap_gpu(vivante, vPix);
+ drm_armada_bo_put(vPix->bo);
+ free(vPix);
+ }
+}
+
+void vivante_set_pixmap_bo(PixmapPtr pixmap, struct drm_armada_bo *bo)
+{
+ struct vivante_pixmap *vPix = vivante_get_pixmap_priv(pixmap);
+ struct vivante *vivante = vivante_get_screen_priv(pixmap->drawable.pScreen);
+
+ if (!vPix && !bo)
+ return;
+
+ if (vPix) {
+ if (vPix->bo == bo)
+ return;
+
+ vivante_free_pixmap(pixmap);
+ vPix = NULL;
+ }
+
+ if (bo) {
+ gceSURF_FORMAT format;
+
+ if (bo->pitch != pixmap->devKind) {
+ xf86DrvMsg(vivante->scrnIndex, X_ERROR,
+ "%s: bo pitch %u and pixmap pitch %u mismatch\n",
+ __FUNCTION__, bo->pitch, pixmap->devKind);
+ goto fail;
+ }
+
+ /*
+ * This is an imprecise conversion to the Vivante GAL format.
+ * One important thing here is that Pixmaps don't have an
+ * alpha channel.
+ */
+ switch (pixmap->drawable.bitsPerPixel) {
+ case 16:
+ if (pixmap->drawable.depth == 15)
+ format = gcvSURF_X1R5G5B5;
+ else
+ format = gcvSURF_R5G6B5;
+ break;
+ case 32:
+ format = gcvSURF_X8R8G8B8;
+ break;
+ default:
+ goto fail;
+ }
+
+ vPix = calloc(1, sizeof *vPix);
+ if (!vPix)
+ goto fail;
+
+ vPix->bo = bo;
+ vPix->width = pixmap->drawable.width;
+ vPix->height = pixmap->drawable.height;
+ vPix->pitch = bo->pitch;
+ vPix->handle = -1;
+ vPix->format = format;
+ vPix->owner = NONE;
+
+ /*
+ * If this is not a SHMEM bo, then we need to map it
+ * for the GPU. As it will not be a fully cached mapping,
+ * we can keep this mapped.
+ */
+ if (bo->type != DRM_ARMADA_BO_SHMEM) {
+ if (!vivante_map_bo_to_gpu(vivante, bo, &vPix->info,
+ &vPix->handle)) {
+ free(vPix);
+ goto fail;
+ }
+ }
+
+#ifdef DEBUG_PIXMAP
+ dbg("Pixmap %p: vPix=%p bo=%p\n", pixmap, vPix, bo);
+#endif
+ drm_armada_bo_get(bo);
+ }
+
+ fail:
+ vivante_set_pixmap_priv(pixmap, vPix);
+}
+
+
+/* Determine whether this GC can be accelerated */
+static Bool vivante_GC_can_accel(GCPtr pGC, DrawablePtr pDrawable)
+{
+ unsigned long fullmask = FbFullMask(pDrawable->depth);
+
+ /* Must be full-planes */
+ return !pGC || (pGC->planemask & fullmask) == fullmask;
+}
+
+static Bool vivante_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
+vivante_FillSpans(DrawablePtr pDrawable, GCPtr pGC, int n, DDXPointPtr ppt,
+ int *pwidth, int fSorted)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen);
+
+ assert(vivante_GC_can_accel(pGC, pDrawable));
+
+ if (vivante->force_fallback ||
+ !vivante_GCfill_can_accel(pGC, pDrawable) ||
+ !vivante_accel_FillSpans(pDrawable, pGC, n, ppt, pwidth, fSorted))
+ vivante_unaccel_FillSpans(pDrawable, pGC, n, ppt, pwidth, fSorted);
+}
+
+static void
+vivante_PutImage(DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y,
+ int w, int h, int leftPad, int format, char *bits)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen);
+
+ assert(vivante_GC_can_accel(pGC, pDrawable));
+
+ if (vivante->force_fallback ||
+ !vivante_accel_PutImage(pDrawable, pGC, depth, x, y, w, h, leftPad,
+ format, bits))
+ vivante_unaccel_PutImage(pDrawable, pGC, depth, x, y, w, h, leftPad,
+ format, bits);
+}
+
+static RegionPtr
+vivante_CopyArea(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC,
+ int srcx, int srcy, int w, int h, int dstx, int dsty)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDst->pScreen);
+
+ assert(vivante_GC_can_accel(pGC, pDst));
+
+ if (vivante->force_fallback)
+ return vivante_unaccel_CopyArea(pSrc, pDst, pGC, srcx, srcy, w, h,
+ dstx, dsty);
+
+ return miDoCopy(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty,
+ vivante_accel_CopyNtoN, 0, NULL);
+}
+
+static void
+vivante_PolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt,
+ DDXPointPtr ppt)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen);
+
+ assert(vivante_GC_can_accel(pGC, pDrawable));
+
+ if (vivante->force_fallback ||
+ !vivante_GCfill_can_accel(pGC, pDrawable) ||
+ !vivante_accel_PolyPoint(pDrawable, pGC, mode, npt, ppt))
+ vivante_unaccel_PolyPoint(pDrawable, pGC, mode, npt, ppt);
+}
+
+static void
+vivante_PolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrect,
+ xRectangle * prect)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen);
+
+ if (vivante->force_fallback)
+ goto fallback;
+
+ assert(vivante_GC_can_accel(pGC, pDrawable));
+
+ if (vivante_GCfill_can_accel(pGC, pDrawable)) {
+ if (vivante_accel_PolyFillRectSolid(pDrawable, pGC, nrect, prect))
+ return;
+ } else if (pGC->fillStyle == FillTiled) {
+ if (vivante_accel_PolyFillRectTiled(pDrawable, pGC, nrect, prect))
+ return;
+ }
+
+ fallback:
+ vivante_unaccel_PolyFillRect(pDrawable, pGC, nrect, prect);
+}
+
+static GCOps vivante_GCOps = {
+ vivante_FillSpans,
+ vivante_unaccel_SetSpans,
+ vivante_PutImage,
+ vivante_CopyArea,
+ vivante_unaccel_CopyPlane,
+ vivante_PolyPoint,
+ vivante_unaccel_PolyLines,
+ vivante_unaccel_PolySegment,
+ miPolyRectangle,
+ miPolyArc,
+ miFillPolygon,
+ vivante_PolyFillRect,
+ miPolyFillArc,
+ miPolyText8,
+ miPolyText16,
+ miImageText8,
+ miImageText16,
+ vivante_unaccel_ImageGlyphBlt,
+ vivante_unaccel_PolyGlyphBlt,
+ vivante_unaccel_PushPixels
+};
+
+static GCOps vivante_unaccel_GCOps = {
+ vivante_unaccel_FillSpans,
+ vivante_unaccel_SetSpans,
+ vivante_unaccel_PutImage,
+ vivante_unaccel_CopyArea,
+ vivante_unaccel_CopyPlane,
+ vivante_unaccel_PolyPoint,
+ vivante_unaccel_PolyLines,
+ vivante_unaccel_PolySegment,
+ miPolyRectangle,
+ miPolyArc,
+ miFillPolygon,
+ vivante_unaccel_PolyFillRect,
+ miPolyFillArc,
+ miPolyText8,
+ miPolyText16,
+ miImageText8,
+ miImageText16,
+ vivante_unaccel_ImageGlyphBlt,
+ vivante_unaccel_PolyGlyphBlt,
+ vivante_unaccel_PushPixels
+};
+
+static void
+vivante_ValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable)
+{
+#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);
+ vivante_prepare_drawable(&pOldTile->drawable, ACCESS_RO);
+ pNewTile = fb24_32ReformatTile(pOldTile, pDrawable->bitsPerPixel);
+ vivante_finish_drawable(&pOldTile->drawable, 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)) {
+ vivante_prepare_drawable(&pGC->tile.pixmap->drawable, ACCESS_RW);
+ fbPadPixmap(pGC->tile.pixmap);
+ vivante_finish_drawable(&pGC->tile.pixmap->drawable, ACCESS_RW);
+ }
+ /* mask out gctile changes now that we've done the work */
+ changes &= ~GCTile;
+ }
+ if (changes & GCStipple && pGC->stipple) {
+ vivante_prepare_drawable(&pGC->stipple->drawable, ACCESS_RW);
+ fbValidateGC(pGC, changes, pDrawable);
+ vivante_finish_drawable(&pGC->stipple->drawable, ACCESS_RW);
+ } else {
+ fbValidateGC(pGC, changes, pDrawable);
+ }
+ /*
+ * Select the GC ops depending on whether we have any
+ * chance to accelerate with this GC.
+ */
+ pGC->ops = vivante_GC_can_accel(pGC, pDrawable)
+ ? &vivante_GCOps : &vivante_unaccel_GCOps;
+}
+
+static GCFuncs vivante_GCFuncs = {
+ vivante_ValidateGC,
+ miChangeGC,
+ miCopyGC,
+ miDestroyGC,
+ miChangeClip,
+ miDestroyClip,
+ miCopyClip
+};
+
+
+static Bool vivante_CloseScreen(int scrnIndex, ScreenPtr pScreen)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pScreen);
+#ifdef RENDER
+ PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);
+#endif
+
+#ifdef HAVE_DRI2
+ vivante_dri2_CloseScreen(scrnIndex, pScreen);
+#endif
+
+#ifdef RENDER
+ /* Restore the Pointers */
+ ps->Composite = vivante->Composite;
+ ps->Glyphs = vivante->Glyphs;
+ ps->UnrealizeGlyph = vivante->UnrealizeGlyph;
+ ps->Triangles = vivante->Triangles;
+ ps->Trapezoids = vivante->Trapezoids;
+ ps->AddTriangles = vivante->AddTriangles;
+ ps->AddTraps = vivante->AddTraps;
+#endif
+
+ pScreen->CloseScreen = vivante->CloseScreen;
+ pScreen->GetImage = vivante->GetImage;
+ pScreen->GetSpans = vivante->GetSpans;
+ pScreen->ChangeWindowAttributes = vivante->ChangeWindowAttributes;
+ pScreen->CopyWindow = vivante->CopyWindow;
+ pScreen->CreatePixmap = vivante->CreatePixmap;
+ pScreen->DestroyPixmap = vivante->DestroyPixmap;
+ pScreen->CreateGC = vivante->CreateGC;
+ pScreen->BitmapToRegion = vivante->BitmapToRegion;
+ pScreen->BlockHandler = vivante->BlockHandler;
+
+ vivante_accel_shutdown(vivante);
+
+ drm_armada_bo_put(vivante->batch_bo);
+
+ free(vivante);
+
+ return pScreen->CloseScreen(scrnIndex, pScreen);
+}
+
+static void
+vivante_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);
+
+ miCopyRegion(&pPixmap->drawable, &pPixmap->drawable, NULL,
+ &rgnDst, dx, dy, vivante_accel_CopyNtoN, 0, NULL);
+
+ RegionUninit(&rgnDst);
+}
+
+static PixmapPtr
+vivante_CreatePixmap(ScreenPtr pScreen, int w, int h, int depth, unsigned usage)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pScreen);
+ struct drm_armada_bo *bo;
+ PixmapPtr pixmap;
+ int ret, bpp;
+
+ if (w > 32768 || h > 32768)
+ return NullPixmap;
+
+ if (depth == 1 || vivante->force_fallback)
+ goto fallback;
+
+ if (usage == CREATE_PIXMAP_USAGE_GLYPH_PICTURE && w <= 32 && h <= 32)
+ goto fallback;
+
+ pixmap = vivante->CreatePixmap(pScreen, 0, 0, depth, usage);
+ if (pixmap == NullPixmap || w == 0 || h == 0)
+ return pixmap;
+
+ bpp = pixmap->drawable.bitsPerPixel;
+ if (bpp != 16 && bpp != 32)
+ goto fallback_free_pix;
+
+ bo = drm_armada_bo_create(vivante->bufmgr, w, h, bpp);
+ if (!bo)
+ goto fallback_free_pix;
+
+ ret = drm_armada_bo_map(bo);
+ if (ret)
+ goto fallback_free_bo;
+
+ /*
+ * Do not store our data pointer in the pixmap - only do so (via
+ * vivante_prepare_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);
+
+ vivante_set_pixmap_bo(pixmap, bo);
+ if (!vivante_get_pixmap_priv(pixmap))
+ goto fallback_free_bo;
+
+ drm_armada_bo_put(bo);
+ goto out;
+
+ fallback_free_bo:
+ drm_armada_bo_put(bo);
+ fallback_free_pix:
+ vivante->DestroyPixmap(pixmap);
+ fallback:
+ pixmap = vivante->CreatePixmap(pScreen, w, h, depth, usage);
+
+ 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 vivante_DestroyPixmap(PixmapPtr pixmap)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pixmap->drawable.pScreen);
+ if (pixmap->refcnt == 1) {
+#ifdef DEBUG_PIXMAP
+ dbg("Destroying pixmap %p\n", pixmap);
+#endif
+ vivante_free_pixmap(pixmap);
+ vivante_set_pixmap_priv(pixmap, NULL);
+ }
+ return vivante->DestroyPixmap(pixmap);
+}
+
+static Bool vivante_CreateGC(GCPtr pGC)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pGC->pScreen);
+ Bool ret;
+
+ ret = vivante->CreateGC(pGC);
+ if (ret)
+ pGC->funcs = &vivante_GCFuncs;
+
+ return ret;
+}
+
+/* Commit any pending GPU operations */
+static void
+vivante_BlockHandler(int scrn, pointer data, pointer timeout, pointer readmask)
+{
+ ScreenPtr pScreen = screenInfo.screens[scrn];
+ struct vivante *vivante = vivante_get_screen_priv(pScreen);
+
+ if (vivante->need_commit)
+ vivante_commit(vivante, FALSE);
+
+ pScreen->BlockHandler = vivante->BlockHandler;
+ pScreen->BlockHandler(scrn, data, timeout, readmask);
+ vivante->BlockHandler = pScreen->BlockHandler;
+ pScreen->BlockHandler = vivante_BlockHandler;
+}
+
+#ifdef RENDER
+static void
+vivante_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 vivante *vivante = vivante_get_screen_priv(pDst->pDrawable->pScreen);
+
+ if (vivante->force_fallback ||
+ !vivante_accel_Composite(op, pSrc, pMask, pDst, xSrc, ySrc,
+ xMask, yMask, xDst, yDst, width, height))
+ vivante_unaccel_Composite(op, pSrc, pMask, pDst, xSrc, ySrc,
+ xMask, yMask, xDst, yDst, width, height);
+}
+#endif
+
+Bool vivante_ScreenInit(ScreenPtr pScreen, struct drm_armada_bufmgr *mgr)
+{
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ int drm_fd = GET_DRM_INFO(pScrn)->fd;
+#ifdef RENDER
+ PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);
+#endif
+ struct vivante *vivante;
+
+ if (!vivante_CreateKey(&vivante_pixmap_index, PRIVATE_PIXMAP) ||
+ !vivante_CreateKey(&vivante_screen_index, PRIVATE_SCREEN))
+ return FALSE;
+
+ vivante = calloc(1, sizeof *vivante);
+ if (!vivante)
+ return FALSE;
+
+ vivante->scrnIndex = pScrn->scrnIndex;
+ list_init(&vivante->batch_list);
+ vivante->bufmgr = mgr;
+ vivante->batch_bo = drm_armada_bo_dumb_create(mgr, 64, 64, 32);
+ if (!vivante->batch_bo) {
+ xf86DrvMsg(vivante->scrnIndex, X_ERROR,
+ "vivante: unable to create batch bo: %s\n",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (drm_armada_bo_map(vivante->batch_bo)) {
+ xf86DrvMsg(vivante->scrnIndex, X_ERROR,
+ "vivante: unable to map batch bo: %s\n",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (!vivante_accel_init(vivante))
+ goto fail;
+
+ if (!vivante_map_bo_to_gpu(vivante, vivante->batch_bo,
+ &vivante->batch_info,
+ &vivante->batch_handle))
+ goto fail;
+
+ vivante->batch_ptr = vivante->batch_bo->ptr;
+ vivante->batch_idx_max = vivante->batch_bo->size / sizeof(uint32_t);
+
+// xf86DrvMsg(vivante->scrnIndex, X_INFO,
+// "vivante: created batch at v%p p0x%08x max idx %u\n",
+// vivante->batch_ptr, vivante->batch_handle,
+// vivante->batch_idx_max);
+
+ vivante_set_screen_priv(pScreen, vivante);
+
+#ifdef HAVE_DRI2
+ if (!vivante_dri2_ScreenInit(pScreen, drm_fd))
+ goto fail;
+#endif
+
+ vivante->CloseScreen = pScreen->CloseScreen;
+ pScreen->CloseScreen = vivante_CloseScreen;
+ vivante->GetImage = pScreen->GetImage;
+ pScreen->GetImage = vivante_unaccel_GetImage;
+ vivante->GetSpans = pScreen->GetSpans;
+ pScreen->GetSpans = vivante_unaccel_GetSpans;
+ vivante->ChangeWindowAttributes = pScreen->ChangeWindowAttributes;
+ pScreen->ChangeWindowAttributes = vivante_unaccel_ChangeWindowAttributes;
+ vivante->CopyWindow = pScreen->CopyWindow;
+ pScreen->CopyWindow = vivante_CopyWindow;
+ vivante->CreatePixmap = pScreen->CreatePixmap;
+ pScreen->CreatePixmap = vivante_CreatePixmap;
+ vivante->DestroyPixmap = pScreen->DestroyPixmap;
+ pScreen->DestroyPixmap = vivante_DestroyPixmap;
+ vivante->CreateGC = pScreen->CreateGC;
+ pScreen->CreateGC = vivante_CreateGC;
+ vivante->BitmapToRegion = pScreen->BitmapToRegion;
+ pScreen->BitmapToRegion = vivante_unaccel_BitmapToRegion;
+ vivante->BlockHandler = pScreen->BlockHandler;
+ pScreen->BlockHandler = vivante_BlockHandler;
+
+#ifdef RENDER
+ vivante->Composite = ps->Composite;
+ ps->Composite = vivante_Composite;
+ vivante->Glyphs = ps->Glyphs;
+ ps->Glyphs = vivante_unaccel_Glyphs;
+ vivante->UnrealizeGlyph = ps->UnrealizeGlyph;
+ vivante->Triangles = ps->Triangles;
+ ps->Triangles = vivante_unaccel_Triangles;
+ vivante->Trapezoids = ps->Trapezoids;
+ ps->Trapezoids = vivante_unaccel_Trapezoids;
+ vivante->AddTriangles = ps->AddTriangles;
+ ps->AddTriangles = vivante_unaccel_AddTriangles;
+ vivante->AddTraps = ps->AddTraps;
+ ps->AddTraps = vivante_unaccel_AddTraps;
+#endif
+
+ return TRUE;
+
+fail:
+ vivante_accel_shutdown(vivante);
+ if (vivante->batch_bo)
+ drm_armada_bo_put(vivante->batch_bo);
+ free(vivante);
+ return FALSE;
+}
diff --git a/src/vivante.h b/src/vivante.h
new file mode 100644
index 0000000..f9049ac
--- /dev/null
+++ b/src/vivante.h
@@ -0,0 +1,18 @@
+/*
+ * Vivante GPU Acceleration Xorg driver
+ *
+ * Written by Russell King, 2012, derived in part from the
+ * Intel xorg X server driver.
+ */
+#ifndef VIVANTE_H
+#define VIVANTE_H
+
+struct drm_armada_bufmgr;
+struct drm_armada_bo;
+
+/* Acceleration support */
+Bool vivante_ScreenInit(ScreenPtr pScreen, struct drm_armada_bufmgr *bufmgr);
+void vivante_free_pixmap(PixmapPtr pixmap);
+void vivante_set_pixmap_bo(PixmapPtr pixmap, struct drm_armada_bo *bo);
+
+#endif
diff --git a/src/vivante_accel.c b/src/vivante_accel.c
new file mode 100644
index 0000000..f9144bb
--- /dev/null
+++ b/src/vivante_accel.c
@@ -0,0 +1,1619 @@
+/*
+ * 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 <unistd.h>
+
+#ifdef HAVE_DIX_CONFIG_H
+#include "dix-config.h"
+#endif
+#include "fb.h"
+#include "gcstruct.h"
+#include "xf86.h"
+
+#include "vivante_accel.h"
+#include "vivante_unaccel.h"
+#include "vivante_utils.h"
+
+#include <gc_hal_raster.h>
+#include <gc_hal_enum.h>
+#include <gc_hal.h>
+
+static CARD32 get_first_pixel(DrawablePtr pDraw)
+{
+ union { CARD32 c32; CARD16 c16; CARD8 c8; char c; } pixel;
+
+ pDraw->pScreen->GetImage(pDraw, 0, 0, 1, 1, ZPixmap, ~0, &pixel.c);
+
+ switch (pDraw->bitsPerPixel) {
+ case 32:
+ return pixel.c32;
+ case 16:
+ return pixel.c16;
+ case 8:
+ case 4:
+ case 1:
+ return pixel.c8;
+ default:
+ assert(0);
+ }
+}
+
+static void vivante_disable_alpha_blend(struct vivante *vivante)
+{
+#ifdef RENDER
+ /* If alpha blending was enabled, disable it now */
+ if (vivante->alpha_blend_enabled) {
+ vivante->alpha_blend_enabled = FALSE;
+ gco2D_DisableAlphaBlend(vivante->e2d);
+ }
+#endif
+}
+
+
+
+struct vivante_batch {
+ struct list node;
+ struct list head;
+ uint32_t index;
+ int32_t serial;
+ int32_t *current;
+};
+
+static void vivante_batch_destroy(struct vivante_batch *batch)
+{
+ struct vivante_pixmap *vp, *vn;
+
+ /* Unlink all pixmaps that this batch is connected to */
+ list_for_each_entry_safe(vp, vn, &batch->head, batch_node) {
+ vp->batch = NULL;
+ list_del(&vp->batch_node);
+ }
+
+ list_del(&batch->node);
+ free(batch);
+}
+
+static void vivante_batch_reap(struct vivante *vivante)
+{
+ struct vivante_batch *batch, *n;
+
+ list_for_each_entry_safe(batch, n, &vivante->batch_list, node) {
+ if (*batch->current == batch->serial) {
+#ifdef DEBUG_BATCH
+ dbg("batch %p: reaping at %08x\n",
+ batch, *batch->current);
+#endif
+ vivante_batch_destroy(batch);
+ }
+ }
+}
+
+static void __vivante_batch_wait(struct vivante_batch *batch)
+{
+#ifdef DEBUG_BATCH
+ dbg("batch %p: waiting: %08x %08x\n",
+ batch, *batch->current, batch->serial);
+#endif
+ while (*batch->current != batch->serial)
+ usleep(5);
+ vivante_batch_destroy(batch);
+}
+
+/*
+ * If the pixmap is part of a batch which is not the current batch,
+ * wait for its batch to indicate operations are complete on it.
+ */
+static void
+vivante_batch_wait(struct vivante *vivante, struct vivante_pixmap *vPix)
+{
+ struct vivante_batch *batch = vPix->batch;
+
+ if (batch && batch != vivante->batch)
+ __vivante_batch_wait(batch);
+}
+
+/*
+ * Issue and wait for all outstanding GPU activity for this pixmap to
+ * complete. Essentially, that means if this pixmap is attached to a
+ * batch, it is busy, and if the batch is the current batch, we need
+ * to commit the current batch of operations.
+ */
+void
+vivante_batch_wait_commit(struct vivante *vivante, struct vivante_pixmap *vPix)
+{
+ struct vivante_batch *batch = vPix->batch;
+
+ if (batch) {
+ if (batch == vivante->batch)
+ vivante_commit(vivante, TRUE);
+ __vivante_batch_wait(batch);
+ }
+}
+
+static Bool vivante_batch_new(struct vivante *vivante)
+{
+ struct vivante_batch *batch;
+ int32_t serial;
+
+ vivante_batch_reap(vivante);
+
+ serial = vivante->batch_serial + 1;
+ if (serial <= 0)
+ serial = 1;
+ vivante->batch_serial = serial;
+
+ batch = malloc(sizeof *batch);
+ if (batch) {
+ uint16_t i = vivante->batch_idx;
+
+ batch->index = i;
+ batch->serial = serial;
+ batch->current = vivante->batch_ptr + i;
+ *batch->current = -1;
+ list_init(&batch->head);
+
+ i += 1;
+ if (i >= vivante->batch_idx_max)
+ i = 0;
+
+ vivante->batch_idx = i;
+ }
+
+ vivante->batch = batch;
+
+ return batch ? TRUE : FALSE;
+}
+
+/* Add the pixmap to the current batch, if not already added */
+static void
+vivante_batch_add(struct vivante *vivante, struct vivante_pixmap *vPix)
+{
+ struct vivante_batch *batch = vPix->batch;
+
+ if (!batch) {
+ vPix->batch = batch = vivante->batch;
+ list_add(&vPix->batch_node, &batch->head);
+#ifdef DEBUG_BATCH
+ dbg("Allocated batch %p for vPix %p\n", batch, vPix);
+#endif
+ vivante->need_commit = TRUE;
+ }
+ assert(vPix->batch == vivante->batch);
+}
+
+/* Add the batch to the GPU operations right at the very end of the GPU ops */
+static void vivante_batch_commit(struct vivante *vivante)
+{
+ struct vivante_batch *batch = vivante->batch;
+ uint32_t col = batch->serial;
+ uint32_t handle = vivante->batch_handle;
+ gceSTATUS err;
+ gcsRECT rect;
+
+#define BATCH_PITCH 64
+#define BATCH_WIDTH (BATCH_PITCH / sizeof(uint32_t))
+
+ rect.left = batch->index & (BATCH_WIDTH - 1);
+ rect.top = batch->index / BATCH_WIDTH;
+ rect.right = rect.left + 1;
+ rect.bottom = rect.top + 1;
+
+#ifdef DEBUG_BATCH
+ dbg("batch %p: current %08x next %08x handle %08x index %04x rect [%u,%u,%u,%u]\n",
+ batch, *batch->current, col, handle, batch->index,
+ rect.left, rect.top, rect.right, rect.bottom);
+#endif
+
+ vivante_disable_alpha_blend(vivante);
+
+ err = gco2D_LoadSolidBrush(vivante->e2d, gcvSURF_A8R8G8B8, 0, col, ~0ULL);
+ if (err != gcvSTATUS_OK)
+ goto error;
+
+ err = gco2D_SetClipping(vivante->e2d, &rect);
+ if (err != gcvSTATUS_OK)
+ goto error;
+
+ err = gco2D_SetTarget(vivante->e2d, handle, BATCH_PITCH,
+ gcvSURF_0_DEGREE, 0);
+ if (err != gcvSTATUS_OK)
+ goto error;
+
+ err = gco2D_Blit(vivante->e2d, 1, &rect, 0xf0, 0xf0, gcvSURF_A8R8G8B8);
+ if (err != gcvSTATUS_OK)
+ goto error;
+
+ list_append(&batch->node, &vivante->batch_list);
+ vivante->batch = NULL;
+ return;
+
+ error:
+ vivante_error(vivante, "batch blit", err);
+}
+
+
+enum gpuid {
+ GPU2D_Source,
+ GPU2D_Target,
+};
+
+static Bool
+gal_prepare_gpu(struct vivante *vivante, struct vivante_pixmap *vPix,
+ enum gpuid id)
+{
+ gceSTATUS err;
+
+#ifdef DEBUG_CHECK_DRAWABLE_USE
+ if (vPix->in_use) {
+ fprintf(stderr, "Trying to accelerate: %p %p %u\n",
+ vPix, vPix->bo, vPix->in_use);
+ return FALSE;
+ }
+#endif
+
+ /*
+ * If we don't have a batch already in place, then add one now.
+ * This gives us a chance to error out and fallback to CPU based
+ * blit if this allocation fails.
+ */
+ if (!vivante->batch && !vivante_batch_new(vivante)) {
+ xf86DrvMsg(vivante->scrnIndex, X_ERROR,
+ "[vivante] %s failed\n", "batch allocation");
+ return FALSE;
+ }
+
+ vivante_batch_wait(vivante, vPix);
+
+ if (!vivante_map_gpu(vivante, vPix))
+ return FALSE;
+
+ switch (id) {
+ case GPU2D_Target:
+ err = gco2D_SetTarget(vivante->e2d, vPix->handle, vPix->pitch,
+ gcvSURF_0_DEGREE, 0);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "gco2D_SetTarget", err);
+ return FALSE;
+ }
+ break;
+
+ case GPU2D_Source:
+#if 0
+ err = gco2D_SetColorSourceAdvanced(vivante->e2d, vSrc->handle,
+ vSrc->pitch, vSrc->format, gcvSURF_0_DEGREE,
+ vSrc->width, vSrc->height, gcvFALSE);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "gco2D_SetColourSourceAdvanced", err);
+ return FALSE;
+ }
+#endif
+ break;
+ }
+ return TRUE;
+}
+
+void vivante_commit(struct vivante *vivante, Bool stall)
+{
+ gceSTATUS err;
+
+ if (vivante->batch)
+ vivante_batch_commit(vivante);
+
+ err = gco2D_Flush(vivante->e2d);
+ if (err != gcvSTATUS_OK)
+ vivante_error(vivante, "Flush", err);
+
+ err = gcoHAL_Commit(vivante->hal, stall ? gcvTRUE : gcvFALSE);
+ if (err != gcvSTATUS_OK)
+ vivante_error(vivante, "Commit", err);
+
+ vivante->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 gctUINT8 vivante_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
+};
+
+/*
+ * Generic solid-like blit fill - takes a set of boxes, and fills them
+ * according to the clips in the GC.
+ */
+static Bool vivante_fill(struct vivante *vivante, struct vivante_pixmap *vPix,
+ GCPtr pGC, RegionPtr region)
+{
+ const BoxRec *pBox, *b;
+ unsigned nBox, chunk;
+ gceSTATUS err;
+ gctUINT32 fg;
+ gctUINT8 rop;
+ gcsRECT *rects, *r, clip;
+
+ if (RegionBroken(region)) {
+ xf86DrvMsg(vivante->scrnIndex, X_ERROR,
+ "[vivante] %s: broken region\n", __FUNCTION__);
+ return FALSE;
+ }
+
+ nBox = RegionNumRects(region);
+ pBox = RegionRects(region);
+
+ chunk = vivante->max_rect_count;
+ if (nBox < chunk)
+ chunk = nBox;
+
+ rects = malloc(chunk * sizeof *rects);
+ if (!rects) {
+ xf86DrvMsg(vivante->scrnIndex, X_ERROR,
+ "[vivante] %s: %s failed\n", __FUNCTION__, "malloc rects");
+ return FALSE;
+ }
+
+ if (!gal_prepare_gpu(vivante, vPix, GPU2D_Target)) {
+ free(rects);
+ return FALSE;
+ }
+
+ vivante_disable_alpha_blend(vivante);
+
+ RectBox(&clip, RegionExtents(region), 0, 0);
+ err = gco2D_SetClipping(vivante->e2d, &clip);
+ if (err) {
+ vivante_error(vivante, "gco2D_SetClipping", err);
+ free(rects);
+ return FALSE;
+ }
+
+ if (pGC->fillStyle == FillTiled) {
+ if (pGC->tileIsPixel)
+ fg = pGC->tile.pixel;
+ else
+ fg = get_first_pixel(&pGC->tile.pixmap->drawable);
+ } else {
+ fg = pGC->fgPixel;
+ }
+ err = gco2D_LoadSolidBrush(vivante->e2d, vPix->format, 0, fg, ~0ULL);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "gco2D_LoadSolidBrush", err);
+ free(rects);
+ return FALSE;
+ }
+
+ rop = vivante_fill_rop[pGC->alu];
+ b = pBox;
+ while (nBox) {
+ unsigned i;
+
+ if (nBox < chunk)
+ chunk = nBox;
+
+ for (i = 0, r = rects; i < chunk; i++, r++, b++)
+ RectBox(r, b, 0, 0);
+
+ err = gco2D_Blit(vivante->e2d, chunk, rects, rop, rop, vPix->format);
+ if (err)
+ break;
+
+ nBox -= chunk;
+ }
+ free(rects);
+
+ if (err != gcvSTATUS_OK)
+ vivante_error(vivante, "Blit", err);
+// gcoHAL_Commit(vivante->hal, FALSE);
+
+ vivante_batch_add(vivante, vPix);
+
+ return TRUE;
+}
+
+
+static const gctUINT8 vivante_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
+};
+
+static gceSTATUS
+vivante_blit_copy(struct vivante *vivante, GCPtr pGC, const BoxRec *total,
+ const BoxRec *pbox, int nbox,
+ int src_off_x, int src_off_y, int dst_off_x, int dst_off_y,
+ gceSURF_FORMAT format)
+{
+ gctUINT8 rop = vivante_copy_rop[pGC ? pGC->alu : GXcopy];
+ gceSTATUS err = gcvSTATUS_OK;
+
+ for (; nbox; nbox--, pbox++) {
+ BoxRec clipped;
+ gcsRECT src, dst;
+
+ if (BoxClip(&clipped, total, pbox))
+ continue;
+
+ RectBox(&src, &clipped, src_off_x, src_off_y);
+ RectBox(&dst, &clipped, dst_off_x, dst_off_y);
+
+ err = gco2D_SetClipping(vivante->e2d, &dst);
+ if (err != gcvSTATUS_OK)
+ break;
+
+ err = gco2D_BatchBlit(vivante->e2d, 1, &src, &dst, rop, rop, format);
+ if (err != gcvSTATUS_OK)
+ break;
+ }
+
+ return err;
+}
+
+
+
+Bool vivante_accel_FillSpans(DrawablePtr pDrawable, GCPtr pGC, int n,
+ DDXPointPtr ppt, int *pwidth, int fSorted)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen);
+ struct vivante_pixmap *vPix;
+ PixmapPtr pPix;
+ BoxPtr pBox, p;
+ RegionRec region;
+ int i, off_x, off_y;
+ Bool ret, overlap;
+
+ pPix = vivante_drawable_pixmap_deltas(pDrawable, &off_x, &off_y);
+ vPix = vivante_get_pixmap_priv(pPix);
+ if (!vPix)
+ return FALSE;
+
+ pBox = malloc(n * sizeof *pBox);
+ if (!pBox)
+ return FALSE;
+
+ for (i = n, p = pBox; i; i--, p++, ppt++, pwidth++) {
+ p->x1 = ppt->x + pDrawable->x;
+ p->x2 = ppt->x + *pwidth;
+ p->y1 = ppt->y + pDrawable->y;
+ p->y2 = ppt->y + 1;
+ }
+
+ /* Convert the boxes to a region */
+ RegionInitBoxes(&region, pBox, n);
+ free(pBox);
+
+ if (!fSorted)
+ RegionValidate(&region, &overlap);
+
+ /* Intersect them with the clipping region */
+ RegionIntersect(&region, &region, fbGetCompositeClip(pGC));
+
+ /* Translate them for the drawable offset */
+ RegionTranslate(&region, off_x, off_y);
+
+ ret = vivante_fill(vivante, vPix, pGC, &region);
+
+ RegionUninit(&region);
+
+ return ret;
+}
+
+Bool vivante_accel_PutImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+ int x, int y, int w, int h, int leftPad, int format, char *bits)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen);
+ struct vivante_pixmap *vPix;
+ RegionPtr pClip = fbGetCompositeClip(pGC);
+ PixmapPtr pPix;
+ BoxRec total;
+ unsigned pitch, size;
+ int dst_off_x, dst_off_y, off, src_off_x, src_off_y;
+ gctPOINTER info;
+ gctUINT32 addr;
+ gceSTATUS err;
+ char *buf = bits;
+
+ if (format != ZPixmap)
+ return FALSE;
+
+ pPix = vivante_drawable_pixmap_deltas(pDrawable, &dst_off_x, &dst_off_y);
+ vPix = vivante_get_pixmap_priv(pPix);
+ if (!vPix)
+ return FALSE;
+
+ pitch = PixmapBytePad(w, depth);
+
+ /*
+ * If the image is not appropriately aligned on each scanline, align
+ * it - it's cheaper to align it here, than to fall back and copy it
+ * manually to the scanout buffer. It is unfortunate that we can't
+ * tell the X server/clients about this restriction.
+ */
+ if (pitch & 15) {
+ unsigned i, new_pitch = (pitch + 15) & ~15;
+
+ buf = malloc(new_pitch * h);
+ if (!buf)
+ return FALSE;
+
+ for (i = 0; i < h; i++) {
+ memcpy(buf + new_pitch * i, bits + pitch * i, pitch);
+ memset(buf + new_pitch * i + pitch, 0, new_pitch - pitch);
+ }
+
+ pitch = new_pitch;
+ }
+
+ size = pitch * h;
+
+ err = gcoOS_MapUserMemory(vivante->os, buf, size, &info, &addr);
+ if (err)
+ return FALSE;
+
+ /* Get the 'X' offset required to align the supplied data */
+ off = addr & VIVANTE_ALIGN_MASK;
+
+ if (!gal_prepare_gpu(vivante, vPix, GPU2D_Target))
+ goto unmap;
+
+ vivante_disable_alpha_blend(vivante);
+
+ err = gco2D_SetColorSourceAdvanced(vivante->e2d, addr - off, pitch,
+ vPix->format, gcvSURF_0_DEGREE, w, h, gcvFALSE);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "SetColorSourceAdvanced", err);
+ goto unmap;
+ }
+
+ err = gco2D_LoadSolidBrush(vivante->e2d, vPix->format, 0, 0, ~0ULL);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "LoadSolidBrush", err);
+ goto unmap;
+ }
+
+ total.x1 = x;
+ total.y1 = y;
+ total.x2 = x + w;
+ total.y2 = y + h;
+ src_off_x = -x + off * 8 / BitsPerPixel(depth);
+ src_off_y = -y;
+
+ err = vivante_blit_copy(vivante, pGC, &total, REGION_RECTS(pClip),
+ REGION_NUM_RECTS(pClip), src_off_x, src_off_y,
+ dst_off_x, dst_off_y, vPix->format);
+ if (err != gcvSTATUS_OK)
+ vivante_error(vivante, "Blit", err);
+
+ vivante_batch_add(vivante, vPix);
+
+ /* Ask for the memory to be unmapped upon completion */
+ gcoHAL_ScheduleUnmapUserMemory(vivante->hal, info, size, addr, buf);
+
+ /* We have to wait for this blit to finish... */
+ vivante_batch_wait_commit(vivante, vPix);
+
+ /* And free the buffer we may have allocated */
+ if (buf != bits)
+ free(buf);
+
+ return TRUE;
+
+ unmap:
+ gcoOS_UnmapUserMemory(vivante->os, bits, size, info, addr);
+
+ return FALSE;
+}
+
+void vivante_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 vivante *vivante = vivante_get_screen_priv(pDst->pScreen);
+ struct vivante_pixmap *vSrc, *vDst;
+ PixmapPtr pixSrc, pixDst;
+ int dst_off_x, dst_off_y, src_off_x, src_off_y;
+ BoxRec limits;
+ gceSTATUS err;
+ const char *what;
+
+ if (vivante->force_fallback)
+ goto fallback;
+
+ /* Get the source and destination pixmaps and offsets */
+ pixSrc = vivante_drawable_pixmap_deltas(pSrc, &src_off_x, &src_off_y);
+ pixDst = vivante_drawable_pixmap_deltas(pDst, &dst_off_x, &dst_off_y);
+
+ vSrc = vivante_get_pixmap_priv(pixSrc);
+ vDst = vivante_get_pixmap_priv(pixDst);
+ if (!vSrc || !vDst)
+ goto fallback;
+
+ /* Include the copy delta on the source */
+ src_off_x += dx;
+ src_off_y += dy;
+
+ /* Calculate the overall limits */
+ limits.x1 = -min(src_off_x, dst_off_x);
+ limits.y1 = -min(src_off_y, dst_off_y);
+ limits.x2 = min(pixSrc->drawable.width - src_off_x, pixDst->drawable.width - dst_off_x);
+ limits.y2 = min(pixSrc->drawable.height - src_off_y, pixDst->drawable.height - dst_off_y);
+
+ /* Right, we're all good to go */
+ if (!gal_prepare_gpu(vivante, vDst, GPU2D_Target) ||
+ !gal_prepare_gpu(vivante, vSrc, GPU2D_Source))
+ goto fallback;
+
+ vivante_disable_alpha_blend(vivante);
+
+ err = gco2D_LoadSolidBrush(vivante->e2d, vDst->format, 0, 0, ~0ULL);
+ if (err != gcvSTATUS_OK) {
+ what = "LoadSolidBrush";
+ goto fallback_msg;
+ }
+
+ err = gco2D_SetColorSourceAdvanced(vivante->e2d, vSrc->handle,
+ vSrc->pitch, vSrc->format, gcvSURF_0_DEGREE,
+ vSrc->width, vSrc->height, gcvFALSE);
+ if (err != gcvSTATUS_OK) {
+ what = "SetColorSourceAdvanced";
+ goto fallback_msg;
+ }
+
+ /* Submit the blit operations */
+ err = vivante_blit_copy(vivante, pGC, &limits, pBox, nBox,
+ src_off_x, src_off_y,
+ dst_off_x, dst_off_y, vDst->format);
+ if (err != gcvSTATUS_OK)
+ vivante_error(vivante, "Blit", err);
+
+// gcoHAL_Commit(vivante->hal, FALSE);
+
+ vivante_batch_add(vivante, vSrc);
+ vivante_batch_add(vivante, vDst);
+
+ return;
+
+ fallback_msg:
+ vivante_error(vivante, what, err);
+ fallback:
+ vivante_unaccel_CopyNtoN(pSrc, pDst, pGC, pBox, nBox, dx, dy, reverse,
+ upsidedown, bitPlane, closure);
+}
+
+Bool vivante_accel_PolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode,
+ int npt, DDXPointPtr ppt)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen);
+ struct vivante_pixmap *vPix;
+ PixmapPtr pPix;
+ BoxPtr pBox;
+ RegionRec region;
+ int i, off_x, off_y;
+ Bool ret, overlap;
+
+ pPix = vivante_drawable_pixmap_deltas(pDrawable, &off_x, &off_y);
+ vPix = vivante_get_pixmap_priv(pPix);
+ if (!vPix)
+ return FALSE;
+
+ pBox = malloc(npt * sizeof *pBox);
+ if (!pBox)
+ return FALSE;
+
+ for (i = 0; i < npt; i++) {
+ pBox[i].x1 = ppt[i].x + pDrawable->x;
+ pBox[i].y1 = ppt[i].y + pDrawable->y;
+ if (i > 0 && mode == CoordModePrevious) {
+ pBox[i].x1 += ppt[i - 1].x;
+ pBox[i].y1 += ppt[i - 1].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));
+
+ /* Translate them for the drawable offset */
+ RegionTranslate(&region, off_x, off_y);
+
+ ret = vivante_fill(vivante, vPix, pGC, &region);
+
+ RegionUninit(&region);
+
+ return ret;
+}
+
+Bool vivante_accel_PolyFillRectSolid(DrawablePtr pDrawable, GCPtr pGC, int n,
+ xRectangle * prect)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen);
+ struct vivante_pixmap *vPix;
+ PixmapPtr pPix;
+ RegionPtr rects;
+ int off_x, off_y;
+ Bool ret;
+
+ pPix = vivante_drawable_pixmap_deltas(pDrawable, &off_x, &off_y);
+ vPix = vivante_get_pixmap_priv(pPix);
+ if (!vPix)
+ return 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));
+
+ if (RegionNumRects(rects)) {
+ /* Translate them for the drawable offset */
+ RegionTranslate(rects, off_x, off_y);
+
+ ret = vivante_fill(vivante, vPix, pGC, rects);
+ } else {
+ ret = TRUE;
+ }
+
+ RegionUninit(rects);
+ RegionDestroy(rects);
+
+ return ret;
+}
+
+Bool vivante_accel_PolyFillRectTiled(DrawablePtr pDrawable, GCPtr pGC, int n,
+ xRectangle * prect)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen);
+ struct vivante_pixmap *vPix, *vTile;
+ PixmapPtr pPix, pTile = pGC->tile.pixmap;
+ RegionPtr rects;
+ int off_x, off_y, nbox;
+ Bool ret;
+
+ pPix = vivante_drawable_pixmap_deltas(pDrawable, &off_x, &off_y);
+ vPix = vivante_get_pixmap_priv(pPix);
+ vTile = vivante_get_pixmap_priv(pTile);
+ if (!vPix || !vTile)
+ return 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;
+ BoxPtr pBox;
+ gceSTATUS err = gcvSTATUS_OK;
+
+ /* Translate them for the drawable offset */
+ RegionTranslate(rects, off_x, off_y);
+
+ ret = FALSE;
+
+ /* Right, we're all good to go */
+ if (!gal_prepare_gpu(vivante, vPix, GPU2D_Target) ||
+ !gal_prepare_gpu(vivante, vTile, GPU2D_Source))
+ goto fallback;
+
+ vivante_disable_alpha_blend(vivante);
+
+ err = gco2D_LoadSolidBrush(vivante->e2d, vPix->format, 0, 0, ~0ULL);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "LoadSolidBrush", err);
+ goto fallback;
+ }
+
+ err = gco2D_SetColorSourceAdvanced(vivante->e2d, vTile->handle,
+ vTile->pitch, vTile->format, gcvSURF_0_DEGREE,
+ vTile->width, vTile->height, gcvFALSE);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "SetColorSourceAdvanced", err);
+ goto fallback;
+ }
+
+ /* Calculate the tile offset from the rect coords */
+ off_x += pDrawable->x + pGC->patOrg.x;
+ off_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;
+ gcsRECT clip;
+ gctUINT8 rop = vivante_copy_rop[pGC ? pGC->alu : GXcopy];
+
+ RectBox(&clip, pBox, 0, 0);
+
+ err = gco2D_SetClipping(vivante->e2d, &clip);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "SetClipping", err);
+ break;
+ }
+
+ dst_y = pBox->y1;
+ height = pBox->y2 - dst_y;
+ modulus(dst_y - 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 - off_x, tile_w, tile_x);
+
+ h = tile_h - tile_y;
+ if (h > height)
+ h = height;
+ height -= h;
+
+ while (width > 0) {
+ gcsRECT dst, src;
+ int w;
+
+ w = tile_w - tile_x;
+ if (w > width)
+ w = width;
+ width -= w;
+
+ src.left = tile_x;
+ src.top = tile_y;
+ src.right = tile_x + w;
+ src.bottom = tile_y + h;
+ dst.left = dst_x;
+ dst.top = dst_y;
+ dst.right = dst_x + w;
+ dst.bottom = dst_y + h;
+
+ err = gco2D_BatchBlit(vivante->e2d, 1, &src, &dst, rop, rop, vPix->format);
+ if (err)
+ break;
+
+ dst_x += w;
+ tile_x = 0;
+ }
+ if (err)
+ break;
+ dst_y += h;
+ tile_y = 0;
+ }
+ if (err)
+ break;
+ pBox++;
+ }
+ vivante_batch_add(vivante, vTile);
+ vivante_batch_add(vivante, vPix);
+ ret = err == 0 ? TRUE : FALSE;
+ } else {
+ ret = TRUE;
+ }
+
+ fallback:
+ RegionUninit(rects);
+ RegionDestroy(rects);
+
+ return ret;
+}
+
+#ifdef RENDER
+#include "mipict.h"
+#include "fbpict.h"
+static Bool transform_is_integer_translation(PictTransformPtr t, int *tx, int *ty)
+{
+ if (t == NULL) {
+ *tx = *ty = 0;
+ return TRUE;
+ }
+
+ if (t->matrix[0][0] != IntToxFixed(1) ||
+ t->matrix[0][1] != 0 ||
+ t->matrix[1][0] != 0 ||
+ t->matrix[1][1] != IntToxFixed(1) ||
+ t->matrix[2][0] != 0 ||
+ t->matrix[2][1] != 0 ||
+ t->matrix[2][2] != IntToxFixed(1))
+ return FALSE;
+
+ if (xFixedFrac(t->matrix[0][2]) != 0 ||
+ xFixedFrac(t->matrix[1][2]) != 0)
+ return FALSE;
+
+ *tx = xFixedToInt(t->matrix[0][2]);
+ *ty = xFixedToInt(t->matrix[1][2]);
+
+ return TRUE;
+}
+
+static Bool drawable_contains(DrawablePtr drawable, int x, int y, int w, int h)
+{
+ if (x < 0 || y < 0 || x + w > drawable->width || y + h > drawable->height)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void adjust_repeat(PicturePtr pPict, int x, int y, unsigned w, unsigned h)
+{
+ int tx, ty;
+
+ if (pPict->pDrawable &&
+ pPict->repeat != RepeatNone &&
+ 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 = RepeatNone;
+ }
+}
+
+struct vivante_blend_op {
+ gceSURF_BLEND_FACTOR_MODE src_blend;
+ gceSURF_BLEND_FACTOR_MODE dst_blend;
+};
+
+static const struct vivante_blend_op vivante_composite_op[] = {
+#define OP(op,s,d) \
+ [PictOp##op] = { \
+ .src_blend = gcvSURF_BLEND_##s, \
+ .dst_blend = gcvSURF_BLEND_##d, \
+ }
+ OP(Clear, ZERO, ZERO),
+ OP(Src, ONE, ZERO),
+ OP(Dst, ZERO, ONE),
+ OP(Over, ONE, INVERSED),
+ OP(OverReverse, INVERSED, ONE),
+ OP(In, STRAIGHT, ZERO),
+ OP(InReverse, ZERO, STRAIGHT),
+ OP(Out, INVERSED, ZERO),
+ OP(OutReverse, ZERO, INVERSED),
+ OP(Atop, STRAIGHT, INVERSED),
+ OP(AtopReverse, INVERSED, STRAIGHT),
+ OP(Xor, INVERSED, INVERSED),
+ OP(Add, ONE, ONE),
+// OP(Saturate, SRC_ALPHA_SATURATED, ZERO ),
+#undef OP
+};
+
+static const struct vivante_blend_op vivante_mask_op = {
+ .src_blend = gcvSURF_BLEND_ZERO, // Mask
+ .dst_blend = gcvSURF_BLEND_STRAIGHT, // Source
+};
+
+static Bool vivante_fill_single(struct vivante *vivante,
+ struct vivante_pixmap *vPix, gcsRECT_PTR rect, gceSURF_FORMAT format,
+ uint32_t colour)
+{
+ gceSTATUS err;
+
+ if (!gal_prepare_gpu(vivante, vPix, GPU2D_Target))
+ return FALSE;
+
+ vivante_disable_alpha_blend(vivante);
+
+ err = gco2D_LoadSolidBrush(vivante->e2d, format, 0, colour, ~0ULL);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "gco2D_LoadSolidBrush", err);
+ return FALSE;
+ }
+
+ err = gco2D_SetClipping(vivante->e2d, rect);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "gco2D_SetClipping", err);
+ return FALSE;
+ }
+
+ err = gco2D_Blit(vivante->e2d, 1, rect, 0xf0, 0xf0, format);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "gco2D_Blit", err);
+ return FALSE;
+ }
+
+ vivante_batch_add(vivante, vPix);
+
+ return TRUE;
+}
+
+#if 0
+static void BoxCopy(gcsRECT_PTR src, gcsRECT_PTR dst, int xSrc, int ySrc,
+ int xDst, int yDst, int w, int h)
+{
+ src->left = xSrc;
+ src->top = ySrc;
+ src->right = xSrc + w;
+ src->bottom = ySrc + h;
+
+ dst->left = xDst;
+ dst->top = yDst;
+ dst->right = xDst + w;
+ dst->bottom = yDst + h;
+}
+#endif
+
+static Bool vivante_blend(struct vivante *vivante, gcsRECT_PTR clip,
+ const struct vivante_blend_op *blend,
+ struct vivante_pixmap *vDst, gceSURF_FORMAT fDst, gcsRECT_PTR rDst,
+ struct vivante_pixmap *vSrc, gceSURF_FORMAT fSrc, gcsRECT_PTR rSrc,
+ unsigned nRect)
+{
+ gceSTATUS err;
+
+ if (!gal_prepare_gpu(vivante, vDst, GPU2D_Target) ||
+ !gal_prepare_gpu(vivante, vSrc, GPU2D_Source))
+ return FALSE;
+
+ if (!blend) {
+ vivante_disable_alpha_blend(vivante);
+ } else {
+ err = gco2D_EnableAlphaBlendAdvanced(vivante->e2d,
+ gcvSURF_PIXEL_ALPHA_STRAIGHT, gcvSURF_PIXEL_ALPHA_STRAIGHT,
+ gcvSURF_GLOBAL_ALPHA_OFF, gcvSURF_GLOBAL_ALPHA_OFF,
+ blend->src_blend, blend->dst_blend);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "gco2D_EnableAlphaBlendAdvanced", err);
+ return FALSE;
+ }
+ vivante->alpha_blend_enabled = TRUE;
+ }
+
+ err = gco2D_SetColorSourceAdvanced(vivante->e2d, vSrc->handle,
+ vSrc->pitch, fSrc, gcvSURF_0_DEGREE,
+ vSrc->width, vSrc->height, gcvFALSE);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "gco2D_SetColorSourceAdvanced", err);
+ return FALSE;
+ }
+
+ err = gco2D_SetClipping(vivante->e2d, clip);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "gco2D_SetClipping", err);
+ return FALSE;
+ }
+
+ err = gco2D_BatchBlit(vivante->e2d, nRect, rSrc, rDst, 0xcc, 0xcc, fDst);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "gco2D_BatchBlit", err);
+ return FALSE;
+ }
+
+ vivante_batch_add(vivante, vDst);
+ vivante_batch_add(vivante, vSrc);
+
+ 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 vivante_pixmap *vivante_acquire_src(struct vivante *vivante,
+ PicturePtr pict, int x, int y, int w, int h, gcsRECT_PTR clip,
+ PixmapPtr pix, struct vivante_pixmap *vTemp,
+ gceSURF_FORMAT *fout, INT16 *xout, INT16 *yout)
+{
+ PixmapPtr pPixmap;
+ struct vivante_pixmap *vSrc;
+ DrawablePtr drawable = pict->pDrawable;
+ uint32_t colour;
+ int tx, ty, ox, oy;
+ Bool fill = FALSE;
+
+ if (drawable == NULL) {
+ SourcePict *src = pict->pSourcePict;
+ if (src && src->type == SourcePictTypeSolidFill) {
+ colour = src->solidFill.color;
+ fill = TRUE;
+ } else {
+ return NULL;
+ }
+ } else if (drawable->width == 1 && drawable->height == 1 &&
+ pict->repeat != RepeatNone) {
+ colour = get_first_pixel(pict->pDrawable);
+ fill = TRUE;
+ }
+
+ if (fill) {
+ *xout = 0;
+ *yout = 0;
+ *fout = vivante_format(PICT_a8r8g8b8, FALSE);
+ if (PICT_FORMAT_A(pict->format) == 0)
+ colour |= 0xff000000;
+ if (!vivante_fill_single(vivante, vTemp, clip, *fout, colour))
+ return NULL;
+
+ return vTemp;
+ }
+
+ pPixmap = vivante_drawable_pixmap_deltas(pict->pDrawable, &ox, &oy);
+ vSrc = vivante_get_pixmap_priv(pPixmap);
+ if (!vSrc)
+ return NULL;
+
+ if (pict->repeat == RepeatNone &&
+ transform_is_integer_translation(pict->transform, &tx, &ty)) {
+ *xout = ox + x + tx + drawable->x;
+ *yout = ox + y + ty + drawable->y;
+ *fout = vivante_format(pict->format, FALSE);
+ } else {
+ PictFormatPtr f;
+ PicturePtr dest;
+ int err;
+
+ f = PictureMatchFormat(drawable->pScreen, pict->pFormat->depth,
+ PICT_a8r8g8b8);
+ if (!f)
+ return NULL;
+
+ dest = CreatePicture(0, &pix->drawable, f, 0, 0, serverClient, &err);
+ if (!dest)
+ return NULL;
+ ValidatePicture(dest);
+
+ vivante_unaccel_Composite(PictOpSrc, pict, NULL, dest, x, y, 0, 0, 0, 0, w, h);
+ FreePicture(dest, 0);
+ *xout = 0;
+ *yout = 0;
+ *fout = vivante_format(PICT_a8r8g8b8, FALSE);
+ vSrc = vTemp;
+ }
+
+ return vSrc;
+}
+
+int vivante_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)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDst->pDrawable->pScreen);
+ struct vivante_pixmap *vDst, *vSrc, *vMask, *vTemp = NULL;
+ ScreenPtr pScreen = pDst->pDrawable->pScreen;
+ PixmapPtr pPixmap, pPixTemp = NULL;
+ RegionRec region;
+ gceSURF_FORMAT fDst, fSrc;
+ gcsRECT clipTemp;
+ gcsRECT clip;
+ int oDst_x, oDst_y;
+
+ if (pDst->alphaMap || pSrc->alphaMap ||
+ (pMask && (pMask->alphaMap || pMask->componentAlpha))) {
+// fprintf(stderr, "%s: D:%s S:%s M:%s%s\n", __FUNCTION__,
+// pDst->alphaMap ? "AM" : "", pSrc->alphaMap ? "AM" : "",
+// pMask && pMask->alphaMap ? "AM" : "",
+// pMask && pMask->componentAlpha ? "CA" : "");
+ return FALSE;
+ }
+
+ /* If we can't do the op, there's no point going any further */
+ if (op >= ARRAY_SIZE(vivante_composite_op))
+ return FALSE;
+
+ /* The destination pixmap must have a bo */
+ pPixmap = vivante_drawable_pixmap_deltas(pDst->pDrawable, &oDst_x, &oDst_y);
+ vDst = vivante_get_pixmap_priv(pPixmap);
+ if (!vDst)
+ 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);
+ if (pMask) {
+ adjust_repeat(pMask, xMask, yMask, width, height);
+
+ /* We don't handle mask repeats (yet) */
+ if (pMask->repeat != RepeatNone)
+ goto fallback;
+ }
+
+ /*
+ * Convert the source/destination coordinates according to the
+ * position of the drawables against the backing buffer.
+ */
+ if (pMask && pMask->pDrawable) {
+ xMask += pMask->pDrawable->x;
+ yMask += pMask->pDrawable->y;
+ }
+ xDst += pDst->pDrawable->x;
+ yDst += pDst->pDrawable->y;
+
+ /*
+ * 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.
+ */
+ RectBox(&clipTemp, RegionExtents(&region), -xDst, -yDst);
+
+ /*
+ * Get a temporary pixmap. We don't really know yet whether we're
+ * going to use it or not.
+ */
+ pPixTemp = pScreen->CreatePixmap(pScreen, width, height,
+ pDst->pDrawable->depth, 0);
+ if (!pPixTemp)
+ goto failed;
+
+ vTemp = vivante_get_pixmap_priv(pPixTemp);
+ if (!vTemp)
+ goto failed;
+
+ /*
+ * Get the source. The source image will be described by vSrc with
+ * offset xSrc/ySrc. This may or may not be the temporary image, and
+ * fSrc describes its format, including whether the alpha channel is
+ * valid.
+ */
+ if (op == PictOpClear) {
+ fSrc = vivante_format(pSrc->format, TRUE);
+ if (!vivante_fill_single(vivante, vTemp, &clipTemp, fSrc, 0))
+ goto failed;
+ vSrc = vTemp;
+ xSrc = 0;
+ ySrc = 0;
+ } else {
+ vSrc = vivante_acquire_src(vivante, pSrc, xSrc, ySrc,
+ width, height,
+ &clipTemp, pPixTemp, vTemp,
+ &fSrc, &xSrc, &ySrc);
+ if (!vSrc)
+ goto failed;
+ }
+
+//vivante_batch_wait_commit(vivante, vSrc);
+//dump_vPix(buf, vivante, 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), xSrc, ySrc,
+ 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) {
+ PixmapPtr pPixMask;
+ gcsRECT rsrc, rdst;
+ int oMask_x, oMask_y;
+ gceSURF_FORMAT fMask;
+
+ pPixMask = vivante_drawable_pixmap_deltas(pMask->pDrawable, &oMask_x, &oMask_y);
+ vMask = vivante_get_pixmap_priv(pPixMask);
+ if (!vMask)
+ goto failed;
+
+ oMask_x += xMask;
+ oMask_y += yMask;
+//dump_vPix(buf, vivante, vMask, 1, "A-MASK%02.2x-%p", op, pMask);
+ rdst.left = 0;
+ rdst.top = 0;
+ rdst.right = width;
+ rdst.bottom = height;
+
+ if (vTemp != vSrc) {
+ gceSURF_FORMAT fTemp;
+
+ /* Copy Source to Temp */
+ rsrc.left = xSrc;
+ rsrc.top = ySrc;
+ rsrc.right = xSrc + width;
+ rsrc.bottom = ySrc + height;
+
+ /*
+ * 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.)
+ */
+ fTemp = vivante_format(pSrc->format, TRUE);
+
+ if (!vivante_blend(vivante, &clipTemp, NULL,
+ vTemp, fTemp, &rdst,
+ vSrc, fSrc, &rsrc, 1))
+ goto failed;
+//vivante_batch_wait_commit(vivante, vTemp);
+//dump_vPix(buf, vivante, vTemp, 1, "A-TMSK%02.2x-%p", op, pMask);
+ fSrc = fTemp;
+ }
+
+ rsrc.left = oMask_x;
+ rsrc.top = oMask_y;
+ rsrc.right = oMask_x + width;
+ rsrc.bottom = oMask_y + height;
+
+ fMask = vivante_format(pMask->format, FALSE);
+
+#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, fMask, pMask->format);
+#endif
+
+ if (!vivante_blend(vivante, &clipTemp, &vivante_mask_op,
+ vTemp, fSrc, &rdst,
+ vMask, fMask, &rsrc,
+ 1))
+ goto failed;
+
+ vSrc = vTemp;
+ xSrc = 0;
+ ySrc = 0;
+ }
+
+ /* Get the Vivante destination format early */
+ fDst = vivante_format(pDst->format, FALSE);
+
+//vivante_batch_wait_commit(vivante, vSrc);
+//dump_vPix(buf, vivante, vSrc, 1, "A-TSRC%02.2x-%p", op, pSrc);
+
+ if (1) {
+ gcsRECT *rects, *rsrc, *rdst;
+ int i, nrects;
+
+ xSrc -= xDst;
+ ySrc -= yDst;
+
+#if 0
+fprintf(stderr, "%s: dst %d,%d,%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,
+ xDst, yDst, fDst, pDst->format, vDst->bo);
+#endif
+
+ nrects = REGION_NUM_RECTS(&region);
+ rects = malloc(sizeof(*rects) * nrects * 2);
+ if (!rects) {
+fprintf(stderr, "%s: malloc fail\n", __FUNCTION__);
+ RegionUninit(&region);
+ goto failed;
+ }
+
+ for (i = 0, rsrc = rects, rdst = rsrc + nrects;
+ i < nrects;
+ i++, rsrc++, rdst++) {
+ RectBox(rsrc, REGION_RECTS(&region) + i, xSrc, ySrc);
+ RectBox(rdst, REGION_RECTS(&region) + i, oDst_x, oDst_y);
+//fprintf(stderr, "%s: rect %d,%d,%d,%d -> %d,%d,%d,%d\n", __FUNCTION__,
+// rsrc->left, rsrc->top, rsrc->right, rsrc->bottom,
+// rdst->left, rdst->top, rdst->right, rdst->bottom);
+ }
+
+ RectBox(&clip, RegionExtents(&region), oDst_x, oDst_y);
+ RegionUninit(&region);
+//fprintf(stderr, "%s: clip: %d,%d,%d,%d\n", __FUNCTION__, clip.left, clip.top, clip.right, clip.bottom);
+
+ rsrc = rects;
+ rdst = rsrc + nrects;
+//vivante_batch_wait_commit(vivante, vSrc);
+//dump_vPix(buf, vivante, vSrc, 1, "A-FSRC%02.2x-%p", op, pSrc);
+//dump_vPix(buf, vivante, vDst, 1, "A-FDST%02.2x-%p", op, pDst);
+ if (!vivante_blend(vivante, &clip, &vivante_composite_op[op],
+ vDst, fDst, rdst,
+ vSrc, fSrc, rsrc, nrects)) {
+ free(rects);
+ goto failed;
+ }
+
+ free(rects);
+
+//vivante_batch_wait_commit(vivante, vDst);
+//fprintf(stderr, "%s: success\n", __FUNCTION__);
+//dump_vPix(buf, vivante, vDst, PICT_FORMAT_A(pDst->format) != 0, "A-DEST%02.2x-%p", op, pDst); }
+ goto done;
+ }
+
+ fallback:
+#if 0
+#define C(p,e) ((p) ? (p)->e : 0)
+fprintf(stderr, "%s: op 0x%02x src=%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->pDrawable, pSrc->repeat, C(pSrc->pDrawable, width), C(pSrc->pDrawable, height), xSrc, ySrc,
+ 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
+
+ failed:
+ RegionUninit(&region);
+
+ if (pPixTemp) {
+ ScreenPtr pScreen = pPixTemp->drawable.pScreen;
+ pScreen->DestroyPixmap(pPixTemp);
+ }
+ return FALSE;
+
+ done:
+ if (pPixTemp) {
+ ScreenPtr pScreen = pPixTemp->drawable.pScreen;
+ pScreen->DestroyPixmap(pPixTemp);
+ }
+ return TRUE;
+}
+#endif
+
+Bool vivante_accel_init(struct vivante *vivante)
+{
+ gceCHIPMODEL model;
+ gctUINT32 rev, feat, minfeat;
+ gceSTATUS ret;
+
+ ret = gcoOS_Construct(gcvNULL, &vivante->os);
+ if (ret != gcvSTATUS_OK) {
+ xf86DrvMsg(vivante->scrnIndex, X_ERROR,
+ "vivante: unable to construct %s object: %s\n",
+ "OS", vivante_strerror(ret));
+ return FALSE;
+ }
+
+ ret = gcoHAL_Construct(gcvNULL, vivante->os, &vivante->hal);
+ if (ret != gcvSTATUS_OK) {
+ xf86DrvMsg(vivante->scrnIndex, X_ERROR,
+ "vivante: unable to construct %s object: %s\n",
+ "HAL", vivante_strerror(ret));
+ return FALSE;
+ }
+
+ ret = gcoHAL_QueryChipIdentity(vivante->hal, &model, &rev, &feat, &minfeat);
+ if (ret != gcvSTATUS_OK)
+ return FALSE;
+
+ ret = gcoHAL_Get2DEngine(vivante->hal, &vivante->e2d);
+ if (ret != gcvSTATUS_OK) {
+ xf86DrvMsg(vivante->scrnIndex, X_ERROR,
+ "vivante: unable to construct %s object: %s\n",
+ "2d engine", vivante_strerror(ret));
+ return FALSE;
+ }
+
+ xf86DrvMsg(vivante->scrnIndex, X_PROBED,
+ "Vivante GC%x GPU revision %x\n", model, rev);
+
+ vivante->max_rect_count = gco2D_GetMaximumRectCount();
+
+ return TRUE;
+}
+
+void vivante_accel_shutdown(struct vivante *vivante)
+{
+ if (vivante->hal) {
+ gcoHAL_Commit(vivante->hal, gcvTRUE);
+ gcoHAL_Destroy(vivante->hal);
+ }
+ if (vivante->os)
+ gcoOS_Destroy(vivante->os);
+}
diff --git a/src/vivante_accel.h b/src/vivante_accel.h
new file mode 100644
index 0000000..1d1bb31
--- /dev/null
+++ b/src/vivante_accel.h
@@ -0,0 +1,167 @@
+/*
+ * 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 <gc_hal_base.h>
+#include "vivante_compat.h"
+
+struct drm_armada_bo;
+struct drm_armada_bufmgr;
+struct vivante_dri2_info;
+struct armada_drm_info;
+
+#undef DEBUG
+
+/* Debugging options */
+#define DEBUG_CHECK_DRAWABLE_USE
+#undef DEBUG_BATCH
+#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)
+
+struct vivante {
+ gcoOS os;
+ gcoHAL hal;
+ gco2D e2d;
+ unsigned max_rect_count;
+ struct drm_armada_bo *batch_bo;
+ int32_t *batch_ptr;
+ void *batch_info;
+ uint32_t batch_handle;
+ uint16_t batch_idx_max;
+ uint16_t batch_idx;
+ int32_t batch_serial;
+ struct list batch_list;
+ struct vivante_batch *batch;
+
+ Bool need_commit;
+ Bool force_fallback;
+#ifdef RENDER
+ Bool alpha_blend_enabled;
+#endif
+ struct drm_armada_bufmgr *bufmgr;
+ int scrnIndex;
+#ifdef HAVE_DRI2
+ struct vivante_dri2_info *dri2;
+ int drm_fd;
+#endif
+
+ 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 vivante_pixmap {
+ uint16_t width;
+ uint16_t height;
+ uint32_t handle;
+ unsigned pitch;
+ gceSURF_FORMAT format;
+ gctPOINTER info;
+
+ struct list batch_node;
+ struct vivante_batch *batch;
+
+ enum {
+ NONE,
+ CPU,
+ GPU,
+ } owner;
+#ifdef DEBUG_CHECK_DRAWABLE_USE
+ int in_use;
+#endif
+ struct drm_armada_bo *bo;
+};
+
+/* Addresses must be aligned */
+#define VIVANTE_ALIGN_MASK 63
+
+/* 2D acceleration */
+Bool vivante_accel_FillSpans(DrawablePtr pDrawable, GCPtr pGC, int n,
+ DDXPointPtr ppt, int *pwidth, int fSorted);
+Bool vivante_accel_PutImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+ int x, int y, int w, int h, int leftPad, int format, char *bits);
+void vivante_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 vivante_accel_PolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode,
+ int npt, DDXPointPtr ppt);
+Bool vivante_accel_PolyFillRectSolid(DrawablePtr pDrawable, GCPtr pGC, int n,
+ xRectangle * prect);
+Bool vivante_accel_PolyFillRectTiled(DrawablePtr pDrawable, GCPtr pGC, int n,
+ xRectangle * prect);
+
+/* 3D acceleration */
+int vivante_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 vivante_commit(struct vivante *vivante, Bool stall);
+
+void vivante_batch_wait_commit(struct vivante *vivante, struct vivante_pixmap *vPix);
+
+void vivante_accel_shutdown(struct vivante *);
+Bool vivante_accel_init(struct vivante *);
+
+static inline struct vivante_pixmap *vivante_get_pixmap_priv(PixmapPtr pixmap)
+{
+ extern vivante_Key vivante_pixmap_index;
+ return vivante_GetKeyPriv(&pixmap->devPrivates, &vivante_pixmap_index);
+}
+
+static inline struct vivante *vivante_get_screen_priv(ScreenPtr pScreen)
+{
+ extern vivante_Key vivante_screen_index;
+ return vivante_GetKeyPriv(&pScreen->devPrivates, &vivante_screen_index);
+}
+
+static inline void vivante_set_pixmap_priv(PixmapPtr pixmap, struct vivante_pixmap *g)
+{
+ extern vivante_Key vivante_pixmap_index;
+ dixSetPrivate(&pixmap->devPrivates, &vivante_pixmap_index, g);
+}
+
+static inline void vivante_set_screen_priv(ScreenPtr pScreen, struct vivante *g)
+{
+ extern vivante_Key vivante_screen_index;
+ dixSetPrivate(&pScreen->devPrivates, &vivante_screen_index, g);
+}
+
+#endif
diff --git a/src/vivante_compat.h b/src/vivante_compat.h
new file mode 100644
index 0000000..67f406b
--- /dev/null
+++ b/src/vivante_compat.h
@@ -0,0 +1,26 @@
+/*
+ * 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
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+#if HAS_DEVPRIVATEKEYREC
+#define vivante_CreateKey(key, type) dixRegisterPrivateKey(key, type, 0)
+#define vivante_GetKeyPriv(dp, key) dixGetPrivate(dp, key)
+#define vivante_Key DevPrivateKeyRec
+#else
+#define vivante_CreateKey(key, type) dixRequestPrivate(key, 0)
+#define vivante_GetKeyPriv(dp, key) dixLookupPrivate(dp, key)
+#define vivante_Key int
+#endif
+
+#endif
diff --git a/src/vivante_dri2.c b/src/vivante_dri2.c
new file mode 100644
index 0000000..42c3876
--- /dev/null
+++ b/src/vivante_dri2.c
@@ -0,0 +1,751 @@
+/*
+ * 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 "vivante_accel.h"
+#include "vivante_dri2.h"
+#include "vivante_utils.h"
+
+#if DRI2INFOREC_VERSION < 4
+#error DRI2 is too old!
+#endif
+
+struct vivante_dri2_info {
+ char *devname;
+};
+
+struct vivante_dri2_buffer {
+ DRI2BufferRec dri2;
+ PixmapPtr pixmap;
+ unsigned ref;
+};
+
+enum event_type {
+ DRI2_SWAP,
+ DRI2_SWAP_CHAIN,
+ DRI2_FLIP,
+ DRI2_WAITMSC,
+};
+
+struct vivante_dri_wait {
+ struct vivante_dri_wait *next;
+ struct list drawable_list;
+ struct list client_list;
+ XID drawable_id;
+ ClientPtr client;
+ enum event_type type;
+ unsigned crtc;
+ int frame;
+
+ /* For swaps/flips */
+ DRI2SwapEventPtr event_func;
+ void *event_data;
+ DRI2BufferPtr front;
+ DRI2BufferPtr back;
+};
+
+#if HAS_DEVPRIVATEKEYREC
+typedef DevPrivateKeyRec vivante_dri2_client_key_t;
+#else
+typedef int vivante_dri2_client_key_t;
+#endif
+
+static vivante_dri2_client_key_t vivante_dri2_client_key;
+static RESTYPE wait_client_restype, wait_drawable_restype;
+
+#if HAS_DIXREGISTERPRIVATEKEY
+#define vivante_dri2_get_client_private(c) \
+ dixGetPrivateAddr(&(c)->devPrivates, &vivante_dri2_client_key)
+#define vivante_dri2_register_private() \
+ dixRegisterPrivateKey(&vivante_dri2_client_key, PRIVATE_CLIENT, \
+ sizeof(XID))
+#else
+#define vivante_dri2_get_client_private(c) \
+ dixLookupPrivate(&(c)->devPrivates, &vivante_dri2_client_key)
+#define vivante_dri2_register_private() \
+ dixRequestPrivate(&vivante_dri2_client_key, sizeof(XID))
+#endif
+
+static int vivante_dri2_drawable_crtc(DrawablePtr draw)
+{
+ /* FIXME */
+ return -1;
+}
+
+static void vivante_dri2_buffer_reference(DRI2Buffer2Ptr buffer)
+{
+ struct vivante_dri2_buffer *priv = buffer->driverPrivate;
+
+ priv->ref++;
+}
+
+static DrawablePtr
+vivante_dri2_get_drawable(DRI2BufferPtr buffer, DrawablePtr drawable)
+{
+ struct vivante_dri2_buffer *buf = buffer->driverPrivate;
+
+ return buffer->attachment == DRI2BufferFrontLeft ?
+ drawable : &buf->pixmap->drawable;
+}
+
+static PixmapPtr vivante_dri2_get_front_pixmap(DrawablePtr drawable)
+{
+ PixmapPtr pixmap = vivante_drawable_pixmap(drawable);
+ struct vivante_pixmap *vpix = vivante_get_pixmap_priv(pixmap);
+
+ if (!vpix)
+ return NULL;
+
+ pixmap->refcnt++;
+
+ return pixmap;
+}
+
+static PixmapPtr
+vivante_dri2_get_pixmap(DRI2BufferPtr buffer)
+{
+ struct vivante_dri2_buffer *buf = buffer->driverPrivate;
+ return buf->pixmap;
+}
+
+static int vivante_dri2_client_gone(void *data, XID id)
+{
+ struct list *list = data;
+
+ while (!list_is_empty(list)) {
+ struct vivante_dri_wait *wait;
+
+ wait = list_first_entry(list, struct vivante_dri_wait, client_list);
+ list_del(&wait->client_list);
+ wait->client = NULL;
+ }
+ free(list);
+
+ return Success;
+}
+
+static int vivante_dri2_drawable_gone(void *data, XID id)
+{
+ struct list *list = data;
+
+ while (!list_is_empty(list)) {
+ struct vivante_dri_wait *wait;
+
+ wait = list_first_entry(list, struct vivante_dri_wait, drawable_list);
+ list_del(&wait->drawable_list);
+ wait->drawable_id = None;
+ }
+ free(list);
+
+ return Success;
+}
+
+static XID client_id(ClientPtr client)
+{
+ XID *ptr = vivante_dri2_get_client_private(client);
+
+ if (*ptr == 0)
+ *ptr = FakeClientID(client->index);
+
+ return *ptr;
+}
+
+static Bool add_reslist(RESTYPE type, XID id, struct list *node)
+{
+ struct list *list;
+ void *ptr = NULL;
+
+ dixLookupResourceByType(&ptr, id, type, NULL, DixWriteAccess);
+ list = ptr;
+ if (!list) {
+ list = malloc(sizeof *list);
+ if (!list)
+ return FALSE;
+
+ if (!AddResource(id, type, list)) {
+ free(list);
+ return FALSE;
+ }
+
+ list_init(list);
+ }
+
+ list_add(node, list);
+
+ return TRUE;
+}
+
+static Bool
+can_exchange(DrawablePtr drawable, DRI2BufferPtr front, DRI2BufferPtr back)
+{
+ PixmapPtr front_pix = vivante_dri2_get_pixmap(front);
+ PixmapPtr back_pix = vivante_dri2_get_pixmap(back);
+
+ if (!DRI2CanFlip(drawable))
+ return FALSE;
+
+ /* Front and back must be the same size and bpp */
+ if (front_pix->drawable.width != back_pix->drawable.width ||
+ front_pix->drawable.height != back_pix->drawable.height ||
+ front_pix->drawable.bitsPerPixel != back_pix->drawable.bitsPerPixel)
+ return FALSE;
+
+ return TRUE;
+}
+
+static DRI2Buffer2Ptr
+vivante_dri2_CreateBuffer(DrawablePtr drawable, unsigned int attachment,
+ unsigned int format)
+{
+ struct vivante_dri2_buffer *buf;
+ struct vivante_pixmap *vpix;
+ ScreenPtr pScreen = drawable->pScreen;
+ PixmapPtr pixmap = NULL;
+ uint32_t name;
+
+fprintf(stderr, "%s: %p %u %u\n", __func__, drawable, attachment, format);
+ if (attachment == DRI2BufferFrontLeft) {
+ pixmap = vivante_dri2_get_front_pixmap(drawable);
+ if (!pixmap) {
+ drawable = &pixmap->drawable;
+ pixmap = NULL;
+ }
+ }
+
+ if (pixmap == NULL) {
+ int width = drawable->width;
+ int height = drawable->height;
+ int depth = format ? format : drawable->depth;
+
+ pixmap = pScreen->CreatePixmap(pScreen, width, height, depth, 0);
+ if (!pixmap)
+ goto err;
+ }
+
+ vpix = vivante_get_pixmap_priv(pixmap);
+ if (!vpix)
+ goto err;
+
+ buf = calloc(1, sizeof *buf);
+ if (!buf)
+ goto err;
+
+ if (!vpix->bo || drm_armada_bo_flink(vpix->bo, &name)) {
+ free(buf);
+ goto err;
+ }
+
+ buf->dri2.attachment = attachment;
+ buf->dri2.name = name;
+ buf->dri2.pitch = pixmap->devKind;
+ buf->dri2.cpp = pixmap->drawable.bitsPerPixel / 8;
+ buf->dri2.flags = 0;
+ buf->dri2.format = format;
+ buf->dri2.driverPrivate = buf;
+ buf->pixmap = pixmap;
+ buf->ref = 1;
+
+ return &buf->dri2;
+
+ err:
+ if (pixmap)
+ pScreen->DestroyPixmap(pixmap);
+
+ return NULL;
+}
+
+static void
+vivante_dri2_DestroyBuffer(DrawablePtr drawable, DRI2Buffer2Ptr buffer)
+{
+ if (buffer) {
+ struct vivante_dri2_buffer *buf = buffer->driverPrivate;
+ ScreenPtr screen;
+
+ /* This should never happen... due to how we allocate the private */
+ assert(buf != NULL);
+
+ if (--buf->ref != 0)
+ return;
+
+ screen = buf->pixmap->drawable.pScreen;
+ screen->DestroyPixmap(buf->pixmap);
+
+ free(buf);
+ }
+}
+
+static void
+vivante_dri2_CopyRegion(DrawablePtr drawable, RegionPtr pRegion,
+ DRI2BufferPtr dstBuf, DRI2BufferPtr srcBuf)
+{
+ ScreenPtr screen = drawable->pScreen;
+ DrawablePtr src = vivante_dri2_get_drawable(srcBuf, drawable);
+ DrawablePtr dst = vivante_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);
+}
+
+static struct vivante_dri_wait *
+new_wait_info(ClientPtr client, DrawablePtr draw, enum event_type type)
+{
+ struct vivante_dri_wait *wait = calloc(1, sizeof *wait);
+
+ if (wait) {
+ wait->drawable_id = draw->id;
+ wait->client = client;
+ wait->type = type;
+
+ list_init(&wait->client_list);
+ list_init(&wait->drawable_list);
+
+ if (!add_reslist(wait_drawable_restype, draw->id,
+ &wait->drawable_list) ||
+ !add_reslist(wait_client_restype, client_id(client),
+ &wait->client_list)) {
+ list_del(&wait->client_list);
+ list_del(&wait->drawable_list);
+ free(wait);
+ wait = NULL;
+ }
+ }
+ return wait;
+}
+
+static void del_wait_info(struct vivante_dri_wait *wait)
+{
+ list_del(&wait->client_list);
+ list_del(&wait->drawable_list);
+
+ vivante_dri2_DestroyBuffer(NULL, wait->front);
+ vivante_dri2_DestroyBuffer(NULL, wait->back);
+
+ free(wait);
+}
+
+static Bool
+vivante_dri2_ScheduleFlip(DrawablePtr drawable, struct vivante_dri_wait *wait)
+{
+ return FALSE;
+}
+
+static void
+vivante_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);
+
+ vivante_dri2_CopyRegion(draw, &region, front, back);
+
+ DRI2SwapComplete(client, draw, frame, tv_sec, tv_usec,
+ DRI2_BLIT_COMPLETE, func, data);
+}
+
+void
+vivante_dri2_vblank(int fd, unsigned frame, unsigned tv_sec, unsigned tv_usec,
+ void *event)
+{
+ struct vivante_dri_wait *wait = event;
+ DrawablePtr draw;
+
+ if (!wait->drawable_id)
+ goto out;
+
+ if (dixLookupDrawable(&draw, wait->drawable_id, serverClient, M_ANY,
+ DixWriteAccess) != Success)
+ goto out;
+
+ switch (wait->type) {
+ case DRI2_FLIP:
+ if (can_exchange(draw, wait->front, wait->back) &&
+ vivante_dri2_ScheduleFlip(draw, wait))
+ return;
+ /* FALLTHROUGH */
+
+ case DRI2_SWAP:
+ vivante_dri2_blit(wait->client, draw, wait->front, wait->back,
+ frame, tv_sec, tv_usec,
+ wait->client ? wait->event_func : NULL,
+ wait->event_data);
+ break;
+
+ case DRI2_WAITMSC:
+ if (wait->client)
+ DRI2WaitMSCComplete(wait->client, draw, frame, tv_sec, tv_usec);
+ break;
+
+ default:
+ xf86DrvMsg(xf86Screens[draw->pScreen->myNum]->scrnIndex, X_WARNING,
+ "%s: unknown vblank event received\n",
+ __FUNCTION__);
+ break;
+ }
+
+ out:
+ del_wait_info(wait);
+}
+
+static uint32_t drm_req_crtc(unsigned crtc)
+{
+ /*
+ * We only support newer kernels here - always
+ * encode the CRTC id in the high crtc field.
+ */
+ return crtc << DRM_VBLANK_HIGH_CRTC_SHIFT;
+}
+
+static int
+vivante_dri2_waitvblank(struct vivante *vivante, drmVBlank *vbl, unsigned crtc,
+ const char *func)
+{
+ static int limit = 5;
+ int ret;
+
+ vbl->request.type = DRM_VBLANK_RELATIVE | drm_req_crtc(crtc);
+ vbl->request.sequence = 0;
+
+ ret = drmWaitVBlank(vivante->drm_fd, vbl);
+ if (ret && limit) {
+ xf86DrvMsg(vivante->scrnIndex, X_WARNING,
+ "%s: get vblank counter failed: %s\n",
+ func, strerror(errno));
+ limit--;
+ }
+ return ret;
+}
+
+static int
+vivante_dri2_ScheduleSwap(ClientPtr client, DrawablePtr draw,
+ DRI2BufferPtr front, DRI2BufferPtr back, CARD64 *target_msc,
+ CARD64 divisor, CARD64 remainder, DRI2SwapEventPtr func, void *data)
+{
+ struct vivante *vivante = vivante_get_screen_priv(draw->pScreen);
+ struct vivante_dri_wait *wait;
+ drmVBlank vbl;
+ CARD64 cur_msc;
+ int ret, crtc;
+
+ crtc = vivante_dri2_drawable_crtc(draw);
+
+ /* Drawable not displayed... just complete */
+ if (crtc < 0)
+ goto blit;
+
+ *target_msc &= 0xffffffff;
+ divisor &= 0xffffffff;
+ remainder &= 0xffffffff;
+
+ wait = new_wait_info(client, draw, DRI2_SWAP);
+ if (!wait)
+ goto blit;
+
+ wait->crtc = crtc;
+ wait->event_func = func;
+ wait->event_data = data;
+ wait->front = front;
+ wait->back = back;
+
+ vivante_dri2_buffer_reference(front);
+ vivante_dri2_buffer_reference(back);
+
+ ret = vivante_dri2_waitvblank(vivante, &vbl, crtc, __FUNCTION__);
+ if (ret)
+ goto blit_free;
+
+ cur_msc = vbl.reply.sequence;
+
+ /* Flips need to be submitted one frame before */
+ if (can_exchange(draw, front, back)) {
+ wait->type = DRI2_FLIP;
+ if (*target_msc > 0)
+ *target_msc -= 1;
+ }
+
+ if (divisor == 0 || cur_msc < *target_msc) {
+ if (wait->type == DRI2_FLIP && vivante_dri2_ScheduleFlip(draw, wait))
+ return TRUE;
+
+ /*
+ * If target_msc has been reached or 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;
+ }
+
+ vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | drm_req_crtc(crtc);
+ if (wait->type != DRI2_FLIP)
+ vbl.request.type |= DRM_VBLANK_NEXTONMISS;
+
+ vbl.request.signal = (unsigned long)wait;
+ ret = drmWaitVBlank(vivante->drm_fd, &vbl);
+ if (ret) {
+ xf86DrvMsg(vivante->scrnIndex, X_WARNING,
+ "get vblank counter failed: %s\n",
+ strerror(errno));
+ goto blit_free;
+ }
+
+ *target_msc = vbl.reply.sequence + (wait->type == DRI2_FLIP);
+ wait->frame = *target_msc;
+
+ return TRUE;
+
+ blit_free:
+ del_wait_info(wait);
+ blit:
+ vivante_dri2_blit(client, draw, front, back, 0, 0, 0, func, data);
+ *target_msc = 0;
+ return TRUE;
+}
+
+static int vivante_dri2_GetMSC(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
+{
+ struct vivante *vivante = vivante_get_screen_priv(draw->pScreen);
+ drmVBlank vbl;
+ int ret, crtc;
+
+ crtc = vivante_dri2_drawable_crtc(draw);
+
+ /* Drawable not displayed, make up a value */
+ if (crtc < 0) {
+ *ust = 0;
+ *msc = 0;
+ return TRUE;
+ }
+
+ ret = vivante_dri2_waitvblank(vivante, &vbl, crtc, __FUNCTION__);
+ if (ret)
+ return FALSE;
+
+ *ust = ((CARD64)vbl.reply.tval_sec * 1000000) + vbl.reply.tval_usec;
+ *msc = vbl.reply.sequence;
+
+ return TRUE;
+}
+
+static int
+vivante_dri2_ScheduleWaitMSC(ClientPtr client, DrawablePtr draw,
+ CARD64 target_msc, CARD64 divisor, CARD64 remainder)
+{
+ struct vivante *vivante = vivante_get_screen_priv(draw->pScreen);
+ struct vivante_dri_wait *wait;
+ drmVBlank vbl;
+ int ret, crtc;
+ CARD64 cur_msc;
+
+ target_msc &= 0xffffffff;
+ divisor &= 0xffffffff;
+ remainder &= 0xffffffff;
+
+ crtc = vivante_dri2_drawable_crtc(draw);
+
+ /* Drawable not displayed, just complete */
+ if (crtc < 0)
+ goto out;
+
+ wait = new_wait_info(client, draw, DRI2_WAITMSC);
+ if (!wait)
+ goto out;
+
+ /* Get current count */
+ ret = vivante_dri2_waitvblank(vivante, &vbl, crtc, __FUNCTION__);
+ if (ret)
+ goto out_free;
+
+ cur_msc = vbl.reply.sequence;
+
+ /*
+ * If the divisor is zero, or cur_msc is smaller than target_msc, we
+ * just need to make sure target_msc passes before waking up the client.
+ */
+ if (divisor == 0 || cur_msc < target_msc) {
+ if (cur_msc >= target_msc)
+ target_msc = cur_msc;
+
+ vbl.request.sequence = target_msc;
+ } else {
+ /*
+ * If we get here, target_msc has already passed or we
+ * don't have one, so queue an event that will satisfy
+ * the divisor/remainder equation.
+ */
+ vbl.request.sequence = cur_msc - (cur_msc % divisor) + remainder;
+
+ /*
+ * If calculated remainder is larger than requested
+ * remainder, it means we've passed the point where
+ * seq % divisor == remainder, so we need to wait for
+ * the next time that will happen.
+ */
+ if ((cur_msc & divisor) >= remainder)
+ vbl.request.sequence += divisor;
+ }
+
+ vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | drm_req_crtc(crtc);
+ vbl.request.signal = (unsigned long)wait;
+ ret = drmWaitVBlank(vivante->drm_fd, &vbl);
+ if (ret) {
+ xf86DrvMsg(vivante->scrnIndex, X_WARNING,
+ "%s: get vblank counter failed: %s\n",
+ __FUNCTION__, strerror(errno));
+ goto out_free;
+ }
+
+ wait->frame = vbl.reply.sequence;
+ DRI2BlockClient(client, draw);
+ return TRUE;
+
+ out_free:
+ del_wait_info(wait);
+ out:
+ DRI2WaitMSCComplete(client, draw, target_msc, 0, 0);
+ return TRUE;
+}
+
+static int dri2_server_generation;
+
+Bool vivante_dri2_ScreenInit(ScreenPtr pScreen, int drm_fd)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pScreen);
+ struct vivante_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 < 1)) {
+ xf86DrvMsg(vivante->scrnIndex, X_WARNING,
+ "DRI2 requires DRI2 module version 1.1.0 or later\n");
+ return FALSE;
+ }
+
+ if (!vivante_dri2_register_private())
+ return FALSE;
+
+ if (dri2_server_generation != serverGeneration) {
+ dri2_server_generation = serverGeneration;
+
+ wait_client_restype = CreateNewResourceType(vivante_dri2_client_gone,
+ "Frame Event Client");
+ wait_drawable_restype = CreateNewResourceType(vivante_dri2_drawable_gone,
+ "Frame Event Drawable");
+
+ if (!wait_client_restype || !wait_drawable_restype) {
+ xf86DrvMsg(vivante->scrnIndex, X_WARNING,
+ "Can not register DRI2 frame event resources\n");
+ return FALSE;
+ }
+ }
+
+ dri = xnfcalloc(1, sizeof *dri);
+ dri->devname = drmGetDeviceNameFromFd(drm_fd);
+
+ vivante->dri2 = dri;
+ vivante->drm_fd = drm_fd;
+
+ memset(&info, 0, sizeof(info));
+ info.version = 4;
+ info.fd = drm_fd;
+ info.driverName = "galdri";
+ info.deviceName = dri->devname;
+
+ info.CreateBuffer = vivante_dri2_CreateBuffer;
+ info.DestroyBuffer = vivante_dri2_DestroyBuffer;
+ info.CopyRegion = vivante_dri2_CopyRegion;
+
+ info.ScheduleSwap = vivante_dri2_ScheduleSwap;
+ info.GetMSC = vivante_dri2_GetMSC;
+ info.ScheduleWaitMSC = vivante_dri2_ScheduleWaitMSC;
+ info.numDrivers = 1;
+ info.driverNames = driverNames;
+ driverNames[0] = info.driverName;
+
+ return DRI2ScreenInit(pScreen, &info);
+}
+
+void vivante_dri2_CloseScreen(int scrnIndex, ScreenPtr pScreen)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pScreen);
+ struct vivante_dri2_info *dri = vivante->dri2;
+
+ if (dri) {
+ DRI2CloseScreen(pScreen);
+
+ vivante->dri2 = NULL;
+ drmFree(dri->devname);
+ free(dri);
+ }
+}
diff --git a/src/vivante_dri2.h b/src/vivante_dri2.h
new file mode 100644
index 0000000..615d528
--- /dev/null
+++ b/src/vivante_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 vivante_dri2_ScreenInit(ScreenPtr pScreen, int drm_fd);
+void vivante_dri2_CloseScreen(int scrnIndex, ScreenPtr pScreen);
+void vivante_dri2_vblank(int fd, unsigned frame, unsigned tv_sec,
+ unsigned tv_usec, void *event);
+
+#endif
diff --git a/src/vivante_unaccel.c b/src/vivante_unaccel.c
new file mode 100644
index 0000000..c7de2bd
--- /dev/null
+++ b/src/vivante_unaccel.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2012-2013 Russell King.
+ *
+ * Based in part on code from the Intel Xorg driver.
+ *
+ * Unaccelerated drawing functions for Vivante GPU. These prepare
+ * access to the drawable prior to passing on the call to the Xorg
+ * Server's fb layer (or pixman layer.)
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "fb.h"
+#include "fbpict.h"
+
+#include "vivante_unaccel.h"
+#include "vivante_utils.h"
+
+static void vivante_prepare_gc(GCPtr pGC)
+{
+ if (pGC->stipple)
+ vivante_prepare_drawable(&pGC->stipple->drawable, ACCESS_RO);
+ if (pGC->fillStyle == FillTiled)
+ vivante_prepare_drawable(&pGC->tile.pixmap->drawable, ACCESS_RO);
+}
+
+static void vivante_finish_gc(GCPtr pGC)
+{
+ if (pGC->fillStyle == FillTiled)
+ vivante_finish_drawable(&pGC->tile.pixmap->drawable, ACCESS_RO);
+ if (pGC->stipple)
+ vivante_finish_drawable(&pGC->stipple->drawable, ACCESS_RO);
+}
+
+static void vivante_prepare_window(WindowPtr pWin)
+{
+ if (pWin->backgroundState == BackgroundPixmap)
+ vivante_prepare_drawable(&pWin->background.pixmap->drawable, ACCESS_RO);
+ if (!pWin->borderIsPixel)
+ vivante_prepare_drawable(&pWin->border.pixmap->drawable, ACCESS_RO);
+}
+
+static void vivante_finish_window(WindowPtr pWin)
+{
+ if (!pWin->borderIsPixel)
+ vivante_finish_drawable(&pWin->border.pixmap->drawable, ACCESS_RO);
+ if (pWin->backgroundState == BackgroundPixmap)
+ vivante_finish_drawable(&pWin->background.pixmap->drawable, ACCESS_RO);
+}
+
+void vivante_unaccel_FillSpans(DrawablePtr pDrawable, GCPtr pGC, int nspans,
+ DDXPointPtr ppt, int *pwidth, int fSorted)
+{
+ vivante_prepare_drawable(pDrawable, ACCESS_RW);
+ vivante_prepare_gc(pGC);
+ fbFillSpans(pDrawable, pGC, nspans, ppt, pwidth, fSorted);
+ vivante_finish_gc(pGC);
+ vivante_finish_drawable(pDrawable, ACCESS_RW);
+}
+
+void vivante_unaccel_SetSpans(DrawablePtr pDrawable, GCPtr pGC, char *psrc,
+ DDXPointPtr ppt, int *pwidth, int nspans, int fSorted)
+{
+ vivante_prepare_drawable(pDrawable, ACCESS_RW);
+ vivante_prepare_gc(pGC);
+ fbSetSpans(pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted);
+ vivante_finish_gc(pGC);
+ vivante_finish_drawable(pDrawable, ACCESS_RW);
+}
+
+void vivante_unaccel_PutImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+ int x, int y, int w, int h, int leftPad, int format, char *bits)
+{
+ vivante_prepare_drawable(pDrawable, ACCESS_RW);
+ vivante_prepare_gc(pGC);
+ fbPutImage(pDrawable, pGC, depth, x, y, w, h, leftPad, format, bits);
+ vivante_finish_gc(pGC);
+ vivante_finish_drawable(pDrawable, ACCESS_RW);
+}
+
+RegionPtr vivante_unaccel_CopyArea(DrawablePtr pSrc, DrawablePtr pDst,
+ GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty)
+{
+ RegionPtr ret;
+
+ vivante_prepare_drawable(pDst, ACCESS_RW);
+ vivante_prepare_drawable(pSrc, ACCESS_RO);
+ ret = fbCopyArea(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty);
+ vivante_finish_drawable(pSrc, ACCESS_RO);
+ vivante_finish_drawable(pDst, ACCESS_RW);
+
+ return ret;
+}
+
+RegionPtr vivante_unaccel_CopyPlane(DrawablePtr pSrc, DrawablePtr pDst,
+ GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty,
+ unsigned long bitPlane)
+{
+ RegionPtr ret;
+
+ vivante_prepare_drawable(pDst, ACCESS_RW);
+ vivante_prepare_drawable(pSrc, ACCESS_RO);
+ ret = fbCopyPlane(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, bitPlane);
+ vivante_finish_drawable(pSrc, ACCESS_RO);
+ vivante_finish_drawable(pDst, ACCESS_RW);
+
+ return ret;
+}
+
+void vivante_unaccel_PolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode,
+ int npt, DDXPointPtr pptInit)
+{
+ vivante_prepare_drawable(pDrawable, ACCESS_RW);
+ fbPolyPoint(pDrawable, pGC, mode, npt, pptInit);
+ vivante_finish_drawable(pDrawable, ACCESS_RW);
+}
+
+void vivante_unaccel_PolyLines(DrawablePtr pDrawable, GCPtr pGC, int mode,
+ int npt, DDXPointPtr ppt)
+{
+ if (pGC->lineWidth == 0) {
+ vivante_prepare_drawable(pDrawable, ACCESS_RW);
+ vivante_prepare_gc(pGC);
+ fbPolyLine(pDrawable, pGC, mode, npt, ppt);
+ vivante_finish_gc(pGC);
+ vivante_finish_drawable(pDrawable, ACCESS_RW);
+ } else {
+ fbPolyLine(pDrawable, pGC, mode, npt, ppt);
+ }
+}
+
+void vivante_unaccel_PolySegment(DrawablePtr pDrawable, GCPtr pGC,
+ int nsegInit, xSegment * pSegInit)
+{
+ if (pGC->lineWidth == 0) {
+ vivante_prepare_drawable(pDrawable, ACCESS_RW);
+ vivante_prepare_gc(pGC);
+ fbPolySegment(pDrawable, pGC, nsegInit, pSegInit);
+ vivante_finish_gc(pGC);
+ vivante_finish_drawable(pDrawable, ACCESS_RW);
+ } else {
+ fbPolySegment(pDrawable, pGC, nsegInit, pSegInit);
+ }
+}
+
+void vivante_unaccel_PolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrect,
+ xRectangle * prect)
+{
+ vivante_prepare_drawable(pDrawable, ACCESS_RW);
+ vivante_prepare_gc(pGC);
+ fbPolyFillRect(pDrawable, pGC, nrect, prect);
+ vivante_finish_gc(pGC);
+ vivante_finish_drawable(pDrawable, ACCESS_RW);
+}
+
+void vivante_unaccel_ImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC,
+ int x, int y, unsigned int nglyph, CharInfoPtr * ppci, pointer pglyphBase)
+{
+ vivante_prepare_drawable(pDrawable, ACCESS_RW);
+ vivante_prepare_gc(pGC);
+ fbImageGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase);
+ vivante_finish_gc(pGC);
+ vivante_finish_drawable(pDrawable, ACCESS_RW);
+}
+
+void vivante_unaccel_PolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC,
+ int x, int y, unsigned int nglyph, CharInfoPtr * ppci, pointer pglyphBase)
+{
+ vivante_prepare_drawable(pDrawable, ACCESS_RW);
+ vivante_prepare_gc(pGC);
+ fbPolyGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase);
+ vivante_finish_gc(pGC);
+ vivante_finish_drawable(pDrawable, ACCESS_RW);
+}
+
+void vivante_unaccel_PushPixels(GCPtr pGC, PixmapPtr pBitmap,
+ DrawablePtr pDrawable, int w, int h, int x, int y)
+{
+ vivante_prepare_drawable(pDrawable, ACCESS_RW);
+ vivante_prepare_drawable(&pBitmap->drawable, ACCESS_RO);
+ vivante_prepare_gc(pGC);
+ fbPushPixels(pGC, pBitmap, pDrawable, w, h, x, y);
+ vivante_finish_gc(pGC);
+ vivante_finish_drawable(&pBitmap->drawable, ACCESS_RO);
+ vivante_finish_drawable(pDrawable, ACCESS_RW);
+}
+
+/* Non-GC ops */
+
+void vivante_unaccel_GetSpans(DrawablePtr pDrawable, int wMax, DDXPointPtr ppt,
+ int *pwidth, int nspans, char *pdstStart)
+{
+ vivante_prepare_drawable(pDrawable, ACCESS_RO);
+ fbGetSpans(pDrawable, wMax, ppt, pwidth, nspans, pdstStart);
+ vivante_finish_drawable(pDrawable, ACCESS_RO);
+}
+
+void vivante_unaccel_GetImage(DrawablePtr pDrawable, int x, int y,
+ int w, int h, unsigned int format, unsigned long planeMask, char *d)
+{
+ vivante_prepare_drawable(pDrawable, ACCESS_RO);
+ fbGetImage(pDrawable, x, y, w, h, format, planeMask, d);
+ vivante_finish_drawable(pDrawable, ACCESS_RO);
+}
+
+Bool vivante_unaccel_ChangeWindowAttributes(WindowPtr pWin, unsigned long mask)
+{
+ Bool ret;
+ vivante_prepare_window(pWin);
+ ret = fbChangeWindowAttributes(pWin, mask);
+ vivante_finish_window(pWin);
+ return ret;
+}
+
+RegionPtr vivante_unaccel_BitmapToRegion(PixmapPtr pixmap)
+{
+ RegionPtr ret;
+ vivante_prepare_drawable(&pixmap->drawable, ACCESS_RO);
+ ret = fbPixmapToRegion(pixmap);
+ vivante_finish_drawable(&pixmap->drawable, ACCESS_RO);
+ return ret;
+}
+
+void vivante_unaccel_CopyNtoN(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC,
+ BoxPtr pBox, int nBox, int dx, int dy, Bool reverse, Bool upsidedown,
+ Pixel bitPlane, void *closure)
+{
+ vivante_prepare_drawable(pDst, ACCESS_RW);
+ if (pDst != pSrc)
+ vivante_prepare_drawable(pSrc, ACCESS_RO);
+ fbCopyNtoN(pSrc, pDst, pGC, pBox, nBox, dx, dy, reverse, upsidedown,
+ bitPlane, closure);
+ if (pDst != pSrc)
+ vivante_finish_drawable(pSrc, ACCESS_RO);
+ vivante_finish_drawable(pDst, ACCESS_RW);
+}
diff --git a/src/vivante_unaccel.h b/src/vivante_unaccel.h
new file mode 100644
index 0000000..0101ce9
--- /dev/null
+++ b/src/vivante_unaccel.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 Russell King.
+ *
+ * Based in part on code from the Intel Xorg driver.
+ *
+ * Unaccelerated drawing functions for Vivante GPU. These prepare
+ * access to the drawable prior to passing on the call to the Xorg
+ * Server's fb layer (or pixman layer.)
+ */
+#ifndef VIVANTE_UNACCEL_H
+#define VIVANTE_UNACCEL_H
+
+void vivante_unaccel_FillSpans(DrawablePtr pDrawable, GCPtr pGC, int nspans,
+ DDXPointPtr ppt, int *pwidth, int fSorted);
+void vivante_unaccel_SetSpans(DrawablePtr pDrawable, GCPtr pGC, char *psrc,
+ DDXPointPtr ppt, int *pwidth, int nspans, int fSorted);
+void vivante_unaccel_PutImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+ int x, int y, int w, int h, int leftPad, int format, char *bits);
+RegionPtr vivante_unaccel_CopyArea(DrawablePtr pSrc, DrawablePtr pDst,
+ GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty);
+RegionPtr vivante_unaccel_CopyPlane(DrawablePtr pSrc, DrawablePtr pDst,
+ GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty,
+ unsigned long bitPlane);
+void vivante_unaccel_PolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode,
+ int npt, DDXPointPtr pptInit);
+void vivante_unaccel_PolyLines(DrawablePtr pDrawable, GCPtr pGC, int mode,
+ int npt, DDXPointPtr ppt);
+void vivante_unaccel_PolySegment(DrawablePtr pDrawable, GCPtr pGC,
+ int nsegInit, xSegment * pSegInit);
+void vivante_unaccel_PolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrect,
+ xRectangle * prect);
+void vivante_unaccel_ImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC,
+ int x, int y, unsigned int nglyph, CharInfoPtr * ppci, pointer pglyphBase);
+void vivante_unaccel_PolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC,
+ int x, int y, unsigned int nglyph, CharInfoPtr * ppci, pointer pglyphBase);
+void vivante_unaccel_PushPixels(GCPtr pGC, PixmapPtr pBitmap,
+ DrawablePtr pDrawable, int w, int h, int x, int y);
+
+void vivante_unaccel_GetSpans(DrawablePtr pDrawable, int wMax, DDXPointPtr ppt,
+ int *pwidth, int nspans, char *pdstStart);
+void vivante_unaccel_GetImage(DrawablePtr pDrawable, int x, int y, int w, int h,
+ unsigned int format, unsigned long planeMask, char *d);
+Bool vivante_unaccel_ChangeWindowAttributes(WindowPtr pWin, unsigned long mask);
+RegionPtr vivante_unaccel_BitmapToRegion(PixmapPtr pixmap);
+void vivante_unaccel_CopyNtoN(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC,
+ BoxPtr pBox, int nBox, int dx, int dy, Bool reverse, Bool upsidedown,
+ Pixel bitPlane, void *closure);
+
+void vivante_unaccel_Composite(CARD8 op, PicturePtr pSrc, PicturePtr pMask,
+ PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask,
+ INT16 xDst, INT16 yDst, CARD16 w, CARD16 h);
+void vivante_unaccel_Glyphs(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
+ PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int nlist,
+ GlyphListPtr list, GlyphPtr * glyphs);
+void vivante_unaccel_Triangles(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
+ PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int ntri,
+ xTriangle * tris);
+void vivante_unaccel_Trapezoids(CARD8 op, PicturePtr src, PicturePtr dst,
+ PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int ntrap,
+ xTrapezoid * traps);
+void vivante_unaccel_AddTriangles(PicturePtr pPicture, INT16 x_off, INT16 y_off,
+ int ntri, xTriangle *tris);
+void vivante_unaccel_AddTraps(PicturePtr pPicture, INT16 x_off, INT16 y_off,
+ int ntrap, xTrap *traps);
+
+#endif
diff --git a/src/vivante_unaccel_render.c b/src/vivante_unaccel_render.c
new file mode 100644
index 0000000..42aaa4a
--- /dev/null
+++ b/src/vivante_unaccel_render.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2012 Russell King.
+ *
+ * Based in part on code from the Intel Xorg driver.
+ *
+ * Unaccelerated drawing functions for Vivante GPU. These prepare
+ * access to the drawable prior to passing on the call to the Xorg
+ * Server's fb layer (or pixman layer.)
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "fb.h"
+#include "fbpict.h"
+#include "mipict.h"
+
+#include "vivante_unaccel.h"
+#include "vivante_utils.h"
+
+static void vivante_prepare_picture(PicturePtr pPicture, int access)
+{
+ if (pPicture->pDrawable) {
+ vivante_prepare_drawable(pPicture->pDrawable, access);
+ if (pPicture->alphaMap)
+ vivante_prepare_drawable(pPicture->alphaMap->pDrawable, access);
+ }
+}
+
+static void vivante_finish_picture(PicturePtr pPicture, int access)
+{
+ if (pPicture->pDrawable) {
+ if (pPicture->alphaMap)
+ vivante_finish_drawable(pPicture->alphaMap->pDrawable, access);
+ vivante_finish_drawable(pPicture->pDrawable, access);
+ }
+}
+
+static void GlyphExtents(int nlist, GlyphListPtr list, GlyphPtr *glyphs,
+ BoxPtr extents)
+{
+ int x1, y1, x2, y2, n, x, y;
+ GlyphPtr glyph;
+
+ x = y = 0;
+ extents->x1 = extents->y1 = MAXSHORT;
+ extents->x2 = extents->y2 = MINSHORT;
+ while (nlist--) {
+ x += list->xOff;
+ y += list->yOff;
+ n = list->len;
+ list++;
+ while (n--) {
+ glyph = *glyphs++;
+ x1 = x - glyph->info.x;
+ if (x1 < MINSHORT)
+ x1 = MINSHORT;
+ y1 = y - glyph->info.y;
+ if (y1 < MINSHORT)
+ y1 = MINSHORT;
+ x2 = x1 + glyph->info.width;
+ if (x2 > MAXSHORT)
+ x2 = MAXSHORT;
+ y2 = y1 + glyph->info.height;
+ if (y2 > MAXSHORT)
+ y2 = MAXSHORT;
+ if (x1 < extents->x1)
+ extents->x1 = x1;
+ if (x2 > extents->x2)
+ extents->x2 = x2;
+ if (y1 < extents->y1)
+ extents->y1 = y1;
+ if (y2 > extents->y2)
+ extents->y2 = y2;
+ x += glyph->info.xOff;
+ y += glyph->info.yOff;
+ }
+ }
+}
+
+#define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0)
+#ifndef GLYPH_HAS_GLYPH_PICTURE_ACCESSOR
+#define GetGlyphPicture(g, s) GlyphPicture((g))[(s)->myNum]
+#define SetGlyphPicture(g, s, p) GlyphPicture((g))[(s)->myNum] = p
+#endif
+
+void vivante_unaccel_Glyphs(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
+ PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int nlist,
+ GlyphListPtr list, GlyphPtr * glyphs)
+{
+ ScreenPtr pScreen = pDst->pDrawable->pScreen;
+ PixmapPtr pMaskPixmap = NULL;
+ PicturePtr pMask;
+ int width = 0, height = 0, x, y, n;
+ int xDst = list->xOff, yDst = list->yOff;
+ BoxRec extents = { 0, 0, 0, 0 };
+
+ if (maskFormat) {
+ xRectangle rect;
+ CARD32 component_alpha;
+ GCPtr pGC;
+ int error;
+
+ GlyphExtents(nlist, list, glyphs, &extents);
+ if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1)
+ return;
+
+ width = extents.x2 - extents.x1;
+ height = extents.y2 - extents.y1;
+ pMaskPixmap = pScreen->CreatePixmap(pScreen, width, height,
+ maskFormat->depth,
+ CREATE_PIXMAP_USAGE_SCRATCH);
+ if (!pMaskPixmap)
+ return;
+
+ component_alpha = NeedsComponent(maskFormat->format);
+ pMask = CreatePicture(0, &pMaskPixmap->drawable, maskFormat,
+ CPComponentAlpha, &component_alpha, serverClient, &error);
+ if (!pMask) {
+ pScreen->DestroyPixmap(pMaskPixmap);
+ return;
+ }
+
+ pGC = GetScratchGC(pMaskPixmap->drawable.depth, pScreen);
+ ValidateGC(&pMaskPixmap->drawable, pGC);
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = width;
+ rect.height = height;
+ pGC->ops->PolyFillRect(&pMaskPixmap->drawable, pGC, 1, &rect);
+ FreeScratchGC(pGC);
+ x = -extents.x1;
+ y = -extents.y1;
+ } else {
+ pMask = pDst;
+ x = 0;
+ y = 0;
+ }
+
+ while (nlist--) {
+ x += list->xOff;
+ y += list->yOff;
+ n = list->len;
+ while (n--) {
+ GlyphPtr glyph = *glyphs++;
+ PicturePtr g = GetGlyphPicture(glyph, pScreen);
+
+ if (g) {
+ int dstx = x - glyph->info.x;
+ int dsty = y - glyph->info.y;
+ if (maskFormat) {
+ CompositePicture(PictOpAdd, g, NULL, pMask,
+ 0, 0, 0, 0, dstx, dsty,
+ glyph->info.width, glyph->info.height);
+ } else {
+ CompositePicture(op, pSrc, g, pDst,
+ xSrc + dstx - xDst,
+ ySrc + dsty - yDst,
+ 0, 0, dstx, dsty,
+ glyph->info.width, glyph->info.height);
+ }
+ }
+ x += glyph->info.xOff;
+ y += glyph->info.yOff;
+ }
+ list++;
+ }
+
+ if (maskFormat) {
+ x = extents.x1;
+ y = extents.y1;
+ CompositePicture(op, pSrc, pMask, pDst,
+ xSrc + x - xDst, ySrc + y - yDst, 0, 0, x, y,
+ width, height);
+ FreePicture(pMask, 0);
+ pScreen->DestroyPixmap(pMaskPixmap);
+ }
+}
+
+void vivante_unaccel_Triangles(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
+ PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int ntri, xTriangle *tri)
+{
+ vivante_prepare_picture(pDst, ACCESS_RW);
+ vivante_prepare_picture(pSrc, ACCESS_RO);
+ fbTriangles(op, pSrc, pDst, maskFormat, xSrc, ySrc, ntri, tri);
+ vivante_finish_picture(pSrc, ACCESS_RO);
+ vivante_finish_picture(pDst, ACCESS_RW);
+}
+
+void vivante_unaccel_Trapezoids(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
+ PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int ntrap,
+ xTrapezoid * traps)
+{
+ vivante_prepare_picture(pDst, ACCESS_RW);
+ vivante_prepare_picture(pSrc, ACCESS_RO);
+ fbTrapezoids(op, pSrc, pDst, maskFormat, xSrc, ySrc, ntrap, traps);
+ vivante_finish_picture(pSrc, ACCESS_RO);
+ vivante_finish_picture(pDst, ACCESS_RW);
+}
+
+void vivante_unaccel_Composite(CARD8 op, PicturePtr pSrc, PicturePtr pMask,
+ PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask,
+ INT16 xDst, INT16 yDst, CARD16 w, CARD16 h)
+{
+ vivante_prepare_picture(pDst, ACCESS_RW);
+ vivante_prepare_picture(pSrc, ACCESS_RO);
+ if (pMask)
+ vivante_prepare_picture(pMask, ACCESS_RO);
+ fbComposite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask,
+ xDst, yDst, w, h);
+ if (pMask)
+ vivante_finish_picture(pMask, ACCESS_RO);
+ vivante_finish_picture(pSrc, ACCESS_RO);
+ vivante_finish_picture(pDst, ACCESS_RW);
+}
+
+void vivante_unaccel_AddTriangles(PicturePtr pPicture, INT16 x_off, INT16 y_off,
+ int ntri, xTriangle *tris)
+{
+ vivante_prepare_picture(pPicture, ACCESS_RW);
+ fbAddTriangles(pPicture, x_off, y_off, ntri, tris);
+ vivante_finish_picture(pPicture, ACCESS_RW);
+}
+
+void vivante_unaccel_AddTraps(PicturePtr pPicture, INT16 x_off, INT16 y_off,
+ int ntrap, xTrap *traps)
+{
+ vivante_prepare_picture(pPicture, ACCESS_RW);
+ fbAddTraps(pPicture, x_off, y_off, ntrap, traps);
+ vivante_finish_picture(pPicture, ACCESS_RW);
+}
diff --git a/src/vivante_utils.c b/src/vivante_utils.c
new file mode 100644
index 0000000..527ee12
--- /dev/null
+++ b/src/vivante_utils.c
@@ -0,0 +1,352 @@
+/*
+ * 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 "fb.h"
+#include "gcstruct.h"
+#include "xf86.h"
+
+#include <armada_bufmgr.h>
+
+#include "vivante_accel.h"
+#include "vivante_utils.h"
+
+static const char *vivante_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 *vivante_strerror(int err)
+{
+ const char *str = NULL;
+
+ if (err < 0) {
+ if (err >= gcvSTATUS_GPU_NOT_RESPONDING)
+ str = vivante_errors[-1 - err];
+ }
+ return str;
+}
+
+void __vivante_error(struct vivante *vivante, const char *fn, const char *w, int err)
+{
+ xf86DrvMsg(vivante->scrnIndex, X_ERROR,
+ "[vivante] %s: %s failed: %s\n", fn, w,
+ vivante_strerror(err));
+}
+
+
+PixmapPtr vivante_drawable_pixmap_deltas(DrawablePtr pDrawable, int *x, int *y)
+{
+ PixmapPtr pPixmap;
+
+ *x = *y = 0;
+ if (OnScreenDrawable(pDrawable->type)) {
+ WindowPtr pWin = container_of(pDrawable, struct _Window, drawable);
+
+ pPixmap = pDrawable->pScreen->GetWindowPixmap(pWin);
+#ifdef COMPOSITE
+ *x = -pPixmap->screen_x;
+ *y = -pPixmap->screen_y;
+#endif
+ } else {
+ pPixmap = container_of(pDrawable, struct _Pixmap, drawable);
+ }
+ return pPixmap;
+}
+
+
+/*
+ * Unmap a bo from the GPU. Note that we must wait for any outstanding
+ * GPU operations to complete before unmapping the pixmap from the GPU.
+ */
+void vivante_unmap_gpu(struct vivante *vivante, struct vivante_pixmap *vPix)
+{
+ struct drm_armada_bo *bo = vPix->bo;
+
+ if (bo->type == DRM_ARMADA_BO_SHMEM) {
+ gceSTATUS err;
+#ifdef DEBUG_MAP
+ dbg("Unmapping vPix %p bo %p\n", vPix, bo);
+#endif
+ err = gcoOS_UnmapUserMemory(vivante->os, bo->ptr, bo->size,
+ vPix->info, vPix->handle);
+ if (err != gcvSTATUS_OK)
+ vivante_error(vivante, "gcoOS_UnmapUserMemory", err);
+
+ vPix->handle = -1;
+ vPix->info = NULL;
+ }
+}
+
+/*
+ * Map a bo to the GPU, and mark the GPU as owning this BO.
+ */
+Bool vivante_map_gpu(struct vivante *vivante, struct vivante_pixmap *vPix)
+{
+ if (vPix->owner != GPU) {
+ struct drm_armada_bo *bo = vPix->bo;
+
+#ifdef DEBUG_CHECK_DRAWABLE_USE
+ assert(vPix->in_use == 0);
+#endif
+ if (bo->type == DRM_ARMADA_BO_SHMEM) {
+ gceSTATUS err;
+ gctUINT32 addr;
+
+ err = gcoOS_MapUserMemory(vivante->os, bo->ptr, bo->size,
+ &vPix->info, &addr);
+ if (err != gcvSTATUS_OK) {
+ vivante_error(vivante, "gcoOS_MapUserMemory", err);
+ return FALSE;
+ }
+
+#ifdef DEBUG_MAP
+ dbg("Mapped vPix %p bo %p to 0x%08x\n", vPix, bo, addr);
+#endif
+
+ vPix->handle = addr;
+ }
+ vPix->owner = GPU;
+ }
+
+ /*
+ * This should never happen - if it does, and we proceeed, we will
+ * take the machine out, so assert and kill ourselves instead.
+ */
+ assert(vPix->handle != 0 && vPix->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 vivante_finish_drawable(DrawablePtr pDrawable, int access)
+{
+ PixmapPtr pixmap = vivante_drawable_pixmap(pDrawable);
+ struct vivante_pixmap *vPix = vivante_get_pixmap_priv(pixmap);
+
+ if (vPix) {
+#ifdef DEBUG_CHECK_DRAWABLE_USE
+ vPix->in_use--;
+#endif
+ if (vPix->bo->type == DRM_ARMADA_BO_SHMEM)
+ 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 vivante_prepare_drawable(DrawablePtr pDrawable, int access)
+{
+ PixmapPtr pixmap = vivante_drawable_pixmap(pDrawable);
+ struct vivante_pixmap *vPix = vivante_get_pixmap_priv(pixmap);
+
+ if (vPix) {
+ struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen);
+
+ /* Ensure that the drawable is up to date with all GPU operations */
+ vivante_batch_wait_commit(vivante, vPix);
+
+ if (vPix->bo->type == DRM_ARMADA_BO_SHMEM) {
+ if (vPix->owner == GPU)
+ vivante_unmap_gpu(vivante, vPix);
+
+ pixmap->devPrivate.ptr = vPix->bo->ptr;
+ }
+#ifdef DEBUG_CHECK_DRAWABLE_USE
+ assert(vPix->in_use == 0);
+ vPix->in_use++;
+#endif
+ vPix->owner = CPU;
+ }
+}
+
+#ifdef RENDER
+gceSURF_FORMAT vivante_format(PictFormatShort format, Bool force)
+{
+ switch (format) {
+#define C(pf,vf,af) case PICT_##pf: return force ? gcvSURF_##af : gcvSURF_##vf
+ C(a2r10g10b10, A2R10G10B10, A2R10G10B10);
+ C(x2r10g10b10, X2R10G10B10, A2R10G10B10);
+ C(a2b10g10r10, A2B10G10R10, A2B10G10R10);
+ C(x2b10g10r10, UNKNOWN, A2B10G10R10);
+ C(a8r8g8b8, A8R8G8B8, A8R8G8B8);
+ C(x8r8g8b8, X8R8G8B8, A8R8G8B8);
+ C(a8b8g8r8, A8B8G8R8, A8B8G8R8);
+ C(x8b8g8r8, X8B8G8R8, A8B8G8R8);
+ C(b8g8r8a8, B8G8R8A8, B8G8R8A8);
+ C(b8g8r8x8, B8G8R8X8, B8G8R8A8);
+ C(r8g8b8, R8G8B8, UNKNOWN);
+ C(b8g8r8, B8G8R8, UNKNOWN);
+ C(r5g6b5, R5G6B5, UNKNOWN);
+ C(b5g6r5, B5G6R5, UNKNOWN);
+ C(a1r5g5b5, A1R5G5B5, A1R5G5B5);
+ C(x1r5g5b5, X1R5G5B5, A1R5G5B5);
+ C(a1b5g5r5, A1B5G5R5, A1B5G5R5);
+ C(x1b5g5r5, X1B5G5R5, A1B5G5R5);
+ C(a4r4g4b4, A4R4G4B4, A4R4G4B4);
+ C(x4r4g4b4, X4R4G4B4, A4R4G4B4);
+ C(a4b4g4r4, A4B4G4R4, A4B4G4R4);
+ C(x4b4g4r4, X4B4G4R4, A4B4G4R4);
+ C(a8, A8, A8);
+ C(r3g3b2, R3G3B2, UNKNOWN);
+ C(b2g3r3, UNKNOWN, UNKNOWN);
+ C(a2r2g2b2, A2R2G2B2, A2R2G2B2);
+ C(a2b2g2r2, UNKNOWN, A2R2G2B2);
+ C(c8, INDEX8, UNKNOWN);
+ 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);
+#undef C
+ }
+ return gcvSURF_UNKNOWN;
+}
+#endif
+
+
+#if 1 //def DEBUG
+#include <stdarg.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+static void dump_pix(struct vivante *vivante, struct vivante_pixmap *vPix,
+ int x1, int y1, int x2, int y2, int alpha,
+ const char *fmt, va_list ap)
+{
+ struct drm_armada_bo *bo = vPix->bo;
+ static int idx;
+ char fn[160], n[80];
+ int fd;
+
+ if (vPix->owner == GPU)
+ vivante_unmap_gpu(vivante, vPix);
+
+ vsprintf(n, fmt, ap);
+ sprintf(fn, "/tmp/X.%04u.%s-%u.%u.%u.%u.pam",
+ idx++, n, x1, y1, x2, y2);
+ fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd >= 0) {
+ char buf[16*1024 + 16384];
+ uint32_t *bo_p;
+ int x, y, i;
+
+ sprintf(buf, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH %u\nMAXVAL 255\nTUPLTYPE RGB%s\nENDHDR\n",
+ x2 - x1, y2 - y1, 3 + alpha, alpha ? "_ALPHA" : "");
+ write(fd, buf, strlen(buf));
+
+ for (y = y1, i = 0; y < y2; y++) {
+ bo_p = (((void *)bo->ptr) + (y * vPix->pitch));
+ for (x = x1; x < x2; x++) {
+ buf[i++] = bo_p[x] >> 16; // R
+ buf[i++] = bo_p[x] >> 8; // G
+ buf[i++] = bo_p[x]; // B
+ if (alpha)
+ buf[i++] = bo_p[x] >> 24; // A
+ }
+ if (i >= 16*1024) {
+ write(fd, buf, i);
+ i = 0;
+ }
+ }
+ if (i)
+ write(fd, buf, i);
+ close(fd);
+ }
+
+ if (vPix->owner == GPU && vPix->bo->type == DRM_ARMADA_BO_SHMEM) {
+ vPix->owner = CPU;
+ vivante_map_gpu(vivante, vPix);
+ }
+}
+
+void dump_Drawable(DrawablePtr pDraw, const char *fmt, ...)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDraw->pScreen);
+ struct vivante_pixmap *vPix;
+ PixmapPtr pPix;
+ int off_x, off_y;
+ va_list ap;
+
+ pPix = vivante_drawable_pixmap_deltas(pDraw, &off_x, &off_y);
+ vPix = vivante_get_pixmap_priv(pPix);
+
+ if (!vPix)
+ return;
+
+ va_start(ap, fmt);
+ dump_pix(vivante, vPix, 0, 0, pDraw->width, pDraw->height, 0, fmt, ap);
+ va_end(ap);
+}
+
+void dump_Picture(PicturePtr pDst, const char *fmt, ...)
+{
+ struct vivante *vivante = vivante_get_screen_priv(pDst->pDrawable->pScreen);
+ struct vivante_pixmap *vPix;
+ PixmapPtr pPix;
+ int alpha, off_x, off_y;
+ va_list ap;
+
+ pPix = vivante_drawable_pixmap_deltas(pDst->pDrawable, &off_x, &off_y);
+ vPix = vivante_get_pixmap_priv(pPix);
+
+ if (!vPix)
+ return;
+
+ alpha = PICT_FORMAT_A(pDst->format) != 0;
+
+ va_start(ap, fmt);
+ dump_pix(vivante, vPix, 0, 0, vPix->width, vPix->height, alpha, fmt, ap);
+ va_end(ap);
+}
+
+void dump_vPix(struct vivante *vivante, struct vivante_pixmap *vPix,
+ int alpha, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ dump_pix(vivante, vPix, 0, 0, vPix->width, vPix->height, alpha, fmt, ap);
+ va_end(ap);
+}
+#endif
diff --git a/src/vivante_utils.h b/src/vivante_utils.h
new file mode 100644
index 0000000..2c34dc9
--- /dev/null
+++ b/src/vivante_utils.h
@@ -0,0 +1,80 @@
+/*
+ * 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
+
+/* Needed for gcsRECT */
+#include <gc_hal_base.h>
+
+struct vivante;
+struct vivante_pixmap;
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+const char *vivante_strerror(int err);
+#define vivante_error(v,w,e) __vivante_error(v,__func__,w,e)
+void __vivante_error(struct vivante *, const char *, const char *, int);
+
+static inline PixmapPtr vivante_drawable_pixmap(DrawablePtr pDrawable)
+{
+ if (OnScreenDrawable(pDrawable->type)) {
+ WindowPtr pWin = container_of(pDrawable, struct _Window, drawable);
+
+ return pDrawable->pScreen->GetWindowPixmap(pWin);
+ } else {
+ return container_of(pDrawable, struct _Pixmap, drawable);
+ }
+}
+
+PixmapPtr vivante_drawable_pixmap_deltas(DrawablePtr pDrawable, int *x, int *y);
+void vivante_unmap_gpu(struct vivante *vivante, struct vivante_pixmap *vPix);
+Bool vivante_map_gpu(struct vivante *vivante, struct vivante_pixmap *vPix);
+
+enum {
+ ACCESS_RO,
+ ACCESS_RW,
+};
+
+void vivante_finish_drawable(DrawablePtr pDrawable, int access);
+void vivante_prepare_drawable(DrawablePtr pDrawable, int access);
+
+gceSURF_FORMAT vivante_format(PictFormatShort format, Bool force);
+
+/*
+ * The following functions are here to allow the compiler to inline them
+ * if it so wishes; otherwise it will include a copy of these functions
+ * in each object including this file.
+ */
+
+/*
+ * Calculate the intersection of two boxes. Returns TRUE if
+ * they do not overlap.
+ */
+static inline Bool BoxClip(BoxPtr out, const BoxRec *in, const BoxRec *clip)
+{
+ out->x1 = max(in->x1, clip->x1);
+ out->y1 = max(in->y1, clip->y1);
+ out->x2 = min(in->x2, clip->x2);
+ out->y2 = min(in->y2, clip->y2);
+ return out->x1 >= out->x2 || out->y1 >= out->y2;
+}
+
+/* Translate the box by off_x, off_y, and convert to a vivante rectangle */
+static inline void RectBox(gcsRECT_PTR rect, const BoxRec *box, int off_x, int off_y)
+{
+ rect->left = box->x1 + off_x;
+ rect->top = box->y1 + off_y;
+ rect->right = box->x2 + off_x;
+ rect->bottom = box->y2 + off_y;
+}
+
+void dump_Drawable(DrawablePtr pDraw, const char *, ...);
+void dump_Picture(PicturePtr pDst, const char *, ...);
+void dump_vPix(struct vivante *vivante, struct vivante_pixmap *vPix,
+ int alpha, const char *fmt, ...);
+
+#endif