From 204ea08a1598909ae05c217e1e7ca709776efbde Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 23 May 2013 00:40:18 +0100 Subject: Initial checkin of Marvell Armada xorg driver Signed-off-by: Russell King --- .gitignore | 29 + Makefile.am | 22 + autogen.sh | 16 + configure.ac | 146 +++ debian/changelog | 5 + debian/compat | 1 + debian/control | 54 + debian/rules | 21 + debian/xserver-xorg-video-armada.install | 2 + man/Makefile.am | 59 ++ man/armada.man | 84 ++ src/Makefile.am | 34 + src/armada_drm.c | 1529 ++++++++++++++++++++++++++++ src/armada_drm.h | 73 ++ src/armada_drm_xv.c | 907 +++++++++++++++++ src/armada_ioctl.h | 138 +++ src/armada_module.c | 138 +++ src/vivante.c | 661 ++++++++++++ src/vivante.h | 18 + src/vivante_accel.c | 1619 ++++++++++++++++++++++++++++++ src/vivante_accel.h | 167 +++ src/vivante_compat.h | 26 + src/vivante_dri2.c | 751 ++++++++++++++ src/vivante_dri2.h | 15 + src/vivante_unaccel.c | 237 +++++ src/vivante_unaccel.h | 66 ++ src/vivante_unaccel_render.c | 231 +++++ src/vivante_utils.c | 352 +++++++ src/vivante_utils.h | 80 ++ 29 files changed, 7481 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile.am create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100755 debian/rules create mode 100644 debian/xserver-xorg-video-armada.install create mode 100644 man/Makefile.am create mode 100644 man/armada.man create mode 100644 src/Makefile.am create mode 100644 src/armada_drm.c create mode 100644 src/armada_drm.h create mode 100644 src/armada_drm_xv.c create mode 100644 src/armada_ioctl.h create mode 100644 src/armada_module.c create mode 100644 src/vivante.c create mode 100644 src/vivante.h create mode 100644 src/vivante_accel.c create mode 100644 src/vivante_accel.h create mode 100644 src/vivante_compat.h create mode 100644 src/vivante_dri2.c create mode 100644 src/vivante_dri2.h create mode 100644 src/vivante_unaccel.c create mode 100644 src/vivante_unaccel.h create mode 100644 src/vivante_unaccel_render.c create mode 100644 src/vivante_utils.c create mode 100644 src/vivante_utils.h 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 ]) +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 ]) + 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 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 +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: + + + + . + 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 +#include +#include + +#include + +#include "armada_drm.h" +#include "xf86_OSproc.h" +#include "xf86Crtc.h" +#include "xf86cmap.h" +#include "fb.h" +#include "mibstore.h" +#include "micmap.h" +#include +#include +#include + +#ifdef HAVE_UDEV +#include +#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 +#include + +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 +#include +#include + +#include + +#include "armada_drm.h" +#include "xf86Crtc.h" +#include "xf86xv.h" +#include "fourcc.h" +#include +#include + +#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 +#include +#include +#include + +#include + +#include "armada_drm.h" + +#include "fb.h" +#include "gcstruct.h" +#include "xf86.h" + +#include + +#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 + +#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 +#include +#include + +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(®ion, pBox, n); + free(pBox); + + if (!fSorted) + RegionValidate(®ion, &overlap); + + /* Intersect them with the clipping region */ + RegionIntersect(®ion, ®ion, fbGetCompositeClip(pGC)); + + /* Translate them for the drawable offset */ + RegionTranslate(®ion, off_x, off_y); + + ret = vivante_fill(vivante, vPix, pGC, ®ion); + + RegionUninit(®ion); + + 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(®ion, pBox, npt); + free(pBox); + + RegionValidate(®ion, &overlap); + + /* Intersect them with the clipping region */ + RegionIntersect(®ion, ®ion, fbGetCompositeClip(pGC)); + + /* Translate them for the drawable offset */ + RegionTranslate(®ion, off_x, off_y); + + ret = vivante_fill(vivante, vPix, pGC, ®ion); + + RegionUninit(®ion); + + 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(®ion, 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(®ion, 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(®ion), -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(®ion); + rects = malloc(sizeof(*rects) * nrects * 2); + if (!rects) { +fprintf(stderr, "%s: malloc fail\n", __FUNCTION__); + RegionUninit(®ion); + goto failed; + } + + for (i = 0, rsrc = rects, rdst = rsrc + nrects; + i < nrects; + i++, rsrc++, rdst++) { + RectBox(rsrc, REGION_RECTS(®ion) + i, xSrc, ySrc); + RectBox(rdst, REGION_RECTS(®ion) + 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(®ion), oDst_x, oDst_y); + RegionUninit(®ion); +//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(®ion); + + 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 +#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 + +/* xorg includes */ +#include "dixstruct.h" +#include "fb.h" +#include "gcstruct.h" +#include "xf86.h" +#include "dri2.h" + +/* drm includes */ +#include +#include + +#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(®ion, &box, 0); + + vivante_dri2_CopyRegion(draw, ®ion, 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 + +#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 +#include +#include +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 + +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 -- cgit v1.2.3