diff options
37 files changed, 8436 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..38da47d --- /dev/null +++ b/.gitignore @@ -0,0 +1,81 @@ +# +# X.Org module default exclusion patterns +# The next section if for module specific patterns +# +# Do not edit the following section +# GNU Build System (Autotools) +aclocal.m4 +autom4te.cache/ +autoscan.log +ChangeLog +compile +config.guess +config.h +config.h.in +config.log +config-ml.in +config.py +config.status +config.status.lineno +config.sub +configure +configure.scan +depcomp +.deps/ +INSTALL +install-sh +.libs/ +libtool +libtool.m4 +ltmain.sh +lt~obsolete.m4 +ltoptions.m4 +ltsugar.m4 +ltversion.m4 +Makefile +Makefile.in +mdate-sh +missing +mkinstalldirs +*.pc +py-compile +stamp-h? +symlink-tree +texinfo.tex +ylwrap + +# Do not edit the following section +# Edit Compile Debug Document Distribute +*~ +*.[0-9] +*.[0-9]x +*.bak +*.bin +core +*.dll +*.exe +*-ISO*.bdf +*-JIS*.bdf +*-KOI8*.bdf +*.kld +*.ko +*.ko.cmd +*.lai +*.l[oa] +*.[oa] +*.obj +*.patch +*.so +*.pcf.gz +*.pdb +*.tar.bz2 +*.tar.gz +*.swp +*.out +tags +# +# Add & Override patterns for xf86-video-ati +# +# Edit the following section as needed +# For example, !report.pc overrides *.pc. See 'man gitignore' +# @@ -0,0 +1,129 @@ +Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org + +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, and +that the name of Marc Aurele La France not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. Marc Aurele La France makes no representations +about the suitability of this software for any purpose. It is provided +"as-is" without express or implied warranty. + +MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO +EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +Copyright (C) 2005 Bogdan D. bogdand@users.sourceforge.net +Copyright (c) 1995-2003 by The XFree86 Project, Inc. + +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 +the rights to use, copy, modify, merge, publish, distribute, sublicense, +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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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 holder(s) +and author(s) 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 holder(s) and author(s). + +Copyright 2006-2007 Advanced Micro Devices, Inc. +Copyright 2007 Egbert Eich <eich@novell.com> +Copyright 2007 Matthias Hopf <mhopf@novell.com> +Copyright (C) 1999-2001 Brian Paul All Rights Reserved. +Copyright 2007 Luc Verhaegen <lverhaegen@novell.com> + +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 +the rights to use, copy, modify, merge, publish, distribute, sublicense, +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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + +Copyright 2007 Advanced Micro Devices, Inc. +Copyright (C) 2008-2009 Advanced Micro Devices, Inc. +Copyright 2004 Eric Anholt +Copyright 2005 Eric Anholt +Copyright 2000 ATI Technologies Inc., Markham, Ontario, and +Copyright 2004 ATI Technologies Inc., Markham, Ontario +Copyright (C) 2008-2009 Alexander Deucher +Copyright 2008 Jérôme Glisse +Copyright 2005 Benjamin Herrenschmidt +Copyright 2008 Kristian Høgsberg +Copyright (C) 2008-2009 Matthias Hopf +Copyright (c) 2006 Itronix Inc. +Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. +Copyright © 2007 Red Hat, Inc. +Copyright © 2009 Red Hat, Inc. +Copyright 2007 George Sapountzis +Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. +Copyright 2006 Tungsten Graphics, Inc. +Copyright 2000 VA Linux Systems, Inc., Fremont, California. + +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 +the rights to use, copy, modify, merge, publish, distribute, sublicense, +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 NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS 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. + +Copyright © 2006 Keith Packard + +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, and +that the name of the copyright holders not be used in advertising or +publicity pertaining to distribution of the software without specific, +written prior permission. The copyright holders make no representations +about the suitability of this software for any purpose. It is provided "as +is" without express or implied warranty. + +THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +OF THIS SOFTWARE. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..f4f4233 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,32 @@ +# 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. + +SUBDIRS = src man +MAINTAINERCLEANFILES = ChangeLog INSTALL + +.PHONY: ChangeLog INSTALL + +INSTALL: + $(INSTALL_CMD) + +ChangeLog: + $(CHANGELOG_CMD) + +EXTRA_DIST = ChangeLog INSTALL @@ -0,0 +1,25 @@ +xf86-video-ati - ATI Radeon video driver for the Xorg X server + +All questions regarding this software should be directed at the +Xorg mailing list: + + http://lists.freedesktop.org/mailman/listinfo/xorg + +Please submit bug reports to the Xorg bugzilla: + + https://bugs.freedesktop.org/enter_bug.cgi?product=xorg + +The master development code repository can be found at: + + git://anongit.freedesktop.org/git/xorg/driver/xf86-video-ati + + http://cgit.freedesktop.org/xorg/driver/xf86-video-ati + +For patch submission instructions, see: + + http://www.x.org/wiki/Development/Documentation/SubmittingPatches + +For more information on the git code manager, see: + + http://wiki.x.org/wiki/GitPage + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..b47abdc --- /dev/null +++ b/autogen.sh @@ -0,0 +1,14 @@ +#! /bin/sh + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir + +autoreconf -v --install || exit 1 +cd $ORIGDIR || exit $? + +if test -z "$NOCONFIGURE"; then + $srcdir/configure --enable-maintainer-mode "$@" +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..1766d9c --- /dev/null +++ b/configure.ac @@ -0,0 +1,234 @@ +# 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 + +# Initialize Autoconf +AC_PREREQ([2.60]) +AC_INIT([xf86-video-amdgpu], + [0.0.01], + [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg], + [xf86-video-amdgpu]) + +AC_CONFIG_SRCDIR([Makefile.am]) +AC_CONFIG_HEADERS([config.h]) + +AC_CONFIG_AUX_DIR(.) + +# Initialize Automake +AM_INIT_AUTOMAKE([foreign dist-bzip2]) +AC_SYS_LARGEFILE + +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])]) +XORG_MACROS_VERSION(1.8) +XORG_DEFAULT_OPTIONS + +# Initialize libtool +AC_DISABLE_STATIC +AC_PROG_LIBTOOL + +# Checks for programs. +AM_PROG_CC_C_O + +if test "x$GCC" = "xyes"; then + CPPFLAGS="$CPPFLAGS -Wall" +fi + +AH_TOP([#include "xorg-server.h"]) + +# Define a configure option for an alternate module directory +AC_ARG_WITH(xorg-module-dir, + AS_HELP_STRING([--with-xorg-module-dir=DIR], + [Default xorg module directory [[default=$libdir/xorg/modules]]]), + [moduledir="$withval"], + [moduledir="$libdir/xorg/modules"]) + +# Store the list of server defined optional extensions in REQUIRED_MODULES +XORG_DRIVER_CHECK_EXT(RANDR, randrproto) +XORG_DRIVER_CHECK_EXT(RENDER, renderproto) +XORG_DRIVER_CHECK_EXT(XV, videoproto) +XORG_DRIVER_CHECK_EXT(DPMSExtension, xextproto) + +# Checks for libraries. +PKG_CHECK_MODULES(LIBDRM, [libdrm >= 2.4.46]) +PKG_CHECK_MODULES(LIBDRM_AMDGPU, [libdrm_amdgpu]) + +# Obtain compiler/linker options for the driver dependencies +PKG_CHECK_MODULES(XORG, [xorg-server >= 1.7 xproto fontsproto xf86driproto $REQUIRED_MODULES]) +PKG_CHECK_MODULES(XEXT, [xextproto >= 7.0.99.1], + HAVE_XEXTPROTO_71="yes"; AC_DEFINE(HAVE_XEXTPROTO_71, 1, [xextproto 7.1 available]), + HAVE_XEXTPROTO_71="no") +AM_CONDITIONAL(HAVE_XEXTPROTO_71, [ test "$HAVE_XEXTPROTO_71" = "yes" ]) + +AC_ARG_ENABLE([udev], + AS_HELP_STRING([--disable-udev], [Disable libudev support [default=auto]]), + [enable_udev="$enableval"], + [enable_udev=auto]) +if test "x$enable_udev" != "xno"; then + PKG_CHECK_MODULES(LIBUDEV, [libudev], [LIBUDEV=yes], [LIBUDEV=no]) + if test "x$LIBUDEV" = xyes; then + AC_DEFINE(HAVE_LIBUDEV, 1,[libudev support]) + elif test "x$enable_udev" != "xauto"; then + AC_MSG_ERROR([Building with udev requested but libudev not found]) + fi +fi +AM_CONDITIONAL(LIBUDEV, test x$LIBUDEV = xyes) + +SAVE_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $XORG_CFLAGS" + +AC_MSG_CHECKING([whether to include GLAMOR support]) +AC_ARG_ENABLE(glamor, + AS_HELP_STRING([--disable-glamor], + [Disable glamor, a new GL-based acceleration [default=enabled]]), + [GLAMOR="$enableval"], + [GLAMOR=yes]) + +if test "x$GLAMOR" != "xno"; then + AC_CHECK_HEADERS([glamor.h], [GLAMOR_H="yes"], [GLAMOR_H="no"], [#include "xorg-server.h"]) + + if test "x$GLAMOR_H" = xyes; then + AC_CHECK_DECL(GLAMOR_NO_DRI3, + [GLAMOR_XSERVER="yes"], [GLAMOR_XSERVER="no"], + [#include "xorg-server.h" + #include "glamor.h"]) + fi + + if test "x$GLAMOR_XSERVER" != xyes; then + PKG_CHECK_MODULES(LIBGLAMOR, [glamor >= 0.6.0]) + PKG_CHECK_MODULES(LIBGLAMOR_EGL, [glamor-egl]) + fi + AC_DEFINE(USE_GLAMOR, 1, [Enable glamor acceleration]) +else + AC_MSG_RESULT([$GLAMOR]) +fi +AM_CONDITIONAL(GLAMOR, test x$GLAMOR != xno) + +AC_CHECK_HEADERS([list.h], + [have_list_h="yes"], [have_list_h="no"], + [#include <X11/Xdefs.h> + #include "xorg-server.h"]) + +if test "x$have_list_h" = xyes; then + AC_CHECK_DECL(xorg_list_init, + [AC_DEFINE(HAVE_XORG_LIST, 1, [Have xorg_list API])], [], + [#include <X11/Xdefs.h> + #include "xorg-server.h" + #include "list.h"]) +fi + +CPPFLAGS="$SAVE_CPPFLAGS" + +PKG_CHECK_MODULES([PCIACCESS], [pciaccess >= 0.8.0]) +XORG_CFLAGS="$XORG_CFLAGS $PCIACCESS_CFLAGS" + +# Checks for headers/macros for byte swapping +# Known variants: +# <byteswap.h> bswap_16, bswap_32, bswap_64 (glibc) +# <sys/endian.h> __swap16, __swap32, __swap64 (OpenBSD) +# <sys/endian.h> bswap16, bswap32, bswap64 (other BSD's) +# and a fallback to local macros if none of the above are found + +# if <byteswap.h> is found, assume it's the correct version +AC_CHECK_HEADERS([byteswap.h]) + +# if <sys/endian.h> is found, have to check which version +AC_CHECK_HEADER([sys/endian.h], [HAVE_SYS_ENDIAN_H="yes"], [HAVE_SYS_ENDIAN_H="no"]) + +if test "x$HAVE_SYS_ENDIAN_H" = "xyes" ; then + AC_MSG_CHECKING([for __swap16 variant of <sys/endian.h> byteswapping macros]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#include <sys/types.h> +#include <sys/endian.h> + ], [ +int a = 1, b; +b = __swap16(a); + ]) +], [SYS_ENDIAN__SWAP='yes'], [SYS_ENDIAN__SWAP='no']) + AC_MSG_RESULT([$SYS_ENDIAN__SWAP]) + + AC_MSG_CHECKING([for bswap16 variant of <sys/endian.h> byteswapping macros]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#include <sys/types.h> +#include <sys/endian.h> + ], [ +int a = 1, b; +b = bswap16(a); + ]) +], [SYS_ENDIAN_BSWAP='yes'], [SYS_ENDIAN_BSWAP='no']) + AC_MSG_RESULT([$SYS_ENDIAN_BSWAP]) + + if test "$SYS_ENDIAN_BSWAP" = "yes" ; then + USE_SYS_ENDIAN_H=yes + BSWAP=bswap + else + if test "$SYS_ENDIAN__SWAP" = "yes" ; then + USE_SYS_ENDIAN_H=yes + BSWAP=__swap + else + USE_SYS_ENDIAN_H=no + fi + fi + + if test "$USE_SYS_ENDIAN_H" = "yes" ; then + AC_DEFINE([USE_SYS_ENDIAN_H], 1, + [Define to use byteswap macros from <sys/endian.h>]) + AC_DEFINE_UNQUOTED([bswap_16], ${BSWAP}16, + [Define to 16-bit byteswap macro]) + AC_DEFINE_UNQUOTED([bswap_32], ${BSWAP}32, + [Define to 32-bit byteswap macro]) + AC_DEFINE_UNQUOTED([bswap_64], ${BSWAP}64, + [Define to 64-bit byteswap macro]) + fi +fi + +AC_SUBST([moduledir]) + +DRIVER_NAME=amdgpu +AC_SUBST([DRIVER_NAME]) + +AC_CONFIG_FILES([ + Makefile + src/Makefile + man/Makefile +]) +AC_OUTPUT + +dnl +dnl Output some configuration info for the user +dnl +echo "" +echo " prefix: $prefix" +echo " exec_prefix: $exec_prefix" +echo " libdir: $libdir" +echo " includedir: $includedir" + +echo "" +echo " CFLAGS: $CFLAGS" +echo " CXXFLAGS: $CXXFLAGS" +echo " Macros: $DEFINES" + +echo "" +echo " Run '${MAKE-make}' to build xf86-video-amdgpu" +echo "" diff --git a/man/Makefile.am b/man/Makefile.am new file mode 100644 index 0000000..b3688ce --- /dev/null +++ b/man/Makefile.am @@ -0,0 +1,41 @@ +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# +# 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 +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# 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 NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS 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. +# + +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) + + +# String replacements in MAN_SUBSTS now come from xorg-macros.m4 via configure + + +SUFFIXES = .$(DRIVER_MAN_SUFFIX) .man + +.man.$(DRIVER_MAN_SUFFIX): + $(AM_V_GEN)$(SED) $(MAN_SUBSTS) < $< > $@ diff --git a/man/amdgpu.man b/man/amdgpu.man new file mode 100644 index 0000000..37007b7 --- /dev/null +++ b/man/amdgpu.man @@ -0,0 +1,105 @@ +.ds q \N'34' +.TH AMDGPU __drivermansuffix__ __vendorversion__ +.SH NAME +amdgpu \- AMD RADEON GPU video driver +.SH SYNOPSIS +.nf +.B "Section \*qDevice\*q" +.BI " Identifier \*q" devname \*q +.B " Driver \*qamdgpu\*q" +\ \ ... +.B EndSection +.fi +.SH DESCRIPTION +.B amdgpu +is an __xservername__ driver for AMD RADEON-based video cards with the +following features: +.PP +.PD 0 +.TP 2 +\(bu +Support for 24-bit pixel depth; +.TP +\(bu +RandR support up to version 1.4; +.TP +\(bu +3D acceleration; +.PD +.SH SUPPORTED HARDWARE +The +.B amdgpu +driver supports CI and newer families' video cards. +.PD +.SH CONFIGURATION DETAILS +Please refer to __xconfigfile__(__filemansuffix__) for general configuration +details. This section only covers configuration details specific to this +driver. +.PP +The following driver +.B Options +are supported: +.TP +.BI "Option \*qSWcursor\*q \*q" boolean \*q +Selects software cursor. The default is +.B off. +.TP +.BI "Option \*qNoAccel\*q \*q" boolean \*q +Enables or disables all hardware acceleration. +.br +The default is to +.B enable +hardware acceleration. +.TP +.BI "Option \*qZaphodHeads\*q \*q" string \*q +Specify the RandR output(s) to use with zaphod mode for a particular driver +instance. If you use this option you must use this option for all instances +of the driver. +.br +For example: +.B +Option \*qZaphodHeads\*q \*qLVDS,VGA-0\*q +will assign xrandr outputs LVDS and VGA-0 to this instance of the driver. +.TP +.BI "Option \*qEnablePageFlip\*q \*q" boolean \*q +Enable DRI2 page flipping. The default is +.B on. + +.SH SEE ALSO +__xservername__(__appmansuffix__), __xconfigfile__(__filemansuffix__), Xserver(__appmansuffix__), X(__miscmansuffix__) +.IP " 1." 4 +Wiki page: +.RS 4 +http://www.x.org/wiki/radeon +.RE +.IP " 2." 4 +Overview about amdgpu development code: +.RS 4 +http://cgit.freedesktop.org/xorg/driver/xf86-video-amdgpu/ +.RE +.IP " 3." 4 +Mailing list: +.RS 4 +http://lists.x.org/mailman/listinfo/xorg-driver-ati +.RE +.IP " 4." 4 +IRC channel: +.RS 4 +#radeon on irc.freenode.net +.RE +.IP " 5." 4 +Query the bugtracker for amdgpu bugs: +.RS 4 +https://bugs.freedesktop.org/query.cgi?product=xorg&component=Driver/AMDgpu +.RE +.IP " 6." 4 +Submit bugs & patches: +.RS 4 +https://bugs.freedesktop.org/enter_bug.cgi?product=xorg&component=Driver/AMDgpu +.RE + +.SH AUTHORS +.nf +Authors include: +Michel D\(:anzer \fImichel@daenzer.net\fP +Alex Deucher \fIalexdeucher@gmail.com\fP diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..817da2d --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,74 @@ +# Copyright 2005 Adam Jackson. +# Copyright 2005 Red Hat, Inc. +# +# 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. + + +# this is obnoxious: +# -module lets us name the module exactly how we want +# -avoid-version prevents gratuitous .0.0.0 version numbers on the end +# _ladir passes a dummy rpath to libtool so the thing will actually link +# TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc. + +amdgpu_drv_la_LIBADD = $(PCIACCESS_LIBS) $(LIBDRM_AMDGPU_LIBS) + +AMDGPU_KMS_SRCS=amdgpu_dri2.c amdgpu_kms.c drmmode_display.c amdgpu_bo_helper.c + +AM_CFLAGS = \ + @LIBDRM_AMDGPU_CFLAGS@ \ + @XORG_CFLAGS@ \ + @LIBUDEV_CFLAGS@ + +if LIBUDEV +amdgpu_drv_la_LIBADD += $(LIBUDEV_LIBS) +endif + +amdgpu_drv_la_LTLIBRARIES = amdgpu_drv.la +amdgpu_drv_la_LDFLAGS = -module -avoid-version +amdgpu_drv_ladir = @moduledir@/drivers +amdgpu_drv_la_SOURCES = \ + amdgpu_video.c \ + amdgpu_misc.c amdgpu_probe.c \ + $(AMDGPU_KMS_SRCS) + +AM_CFLAGS += @LIBGLAMOR_CFLAGS@ +amdgpu_drv_la_LIBADD += @LIBGLAMOR_LIBS@ +amdgpu_drv_la_SOURCES += \ + amdgpu_glamor.c \ + amdgpu_pixmap.c + +EXTRA_DIST = \ + compat-api.h \ + amdgpu_bo_helper.h \ + amdgpu_glamor.h \ + amdgpu_drv.h \ + amdgpu_probe.h \ + amdgpu_version.h \ + amdgpu_video.h \ + simple_list.h \ + amdpciids.h \ + ati_pciids_gen.h \ + amdgpu_chipinfo_gen.h \ + amdgpu_chipset_gen.h \ + amdgpu_pci_chipset_gen.h \ + amdgpu_pci_device_match_gen.h \ + pcidb/ati_pciids.csv \ + pcidb/parse_pci_ids.pl \ + amdgpu_dri2.h \ + drmmode_display.h diff --git a/src/amdgpu_bo_helper.c b/src/amdgpu_bo_helper.c new file mode 100644 index 0000000..10f5856 --- /dev/null +++ b/src/amdgpu_bo_helper.c @@ -0,0 +1,326 @@ +/* + * Copyright 2012 Advanced Micro Devices, Inc. + * + * 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 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <sys/mman.h> +#include <gbm.h> +#include "amdgpu_drv.h" +#include "amdgpu_bo_helper.h" +#include "amdgpu_pixmap.h" + +/* Calculate appropriate pitch for a pixmap and allocate a BO that can hold it. + */ +struct amdgpu_buffer *amdgpu_alloc_pixmap_bo(ScrnInfoPtr pScrn, int width, + int height, int depth, int usage_hint, + int bitsPerPixel, int *new_pitch) +{ + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + struct amdgpu_buffer *pixmap_buffer; + + if (info->gbm) { + uint32_t bo_use = GBM_BO_USE_RENDERING; + uint32_t gbm_format; + + switch (depth) { +#ifdef GBM_FORMAT_R8 + case 8: + gbm_format = GBM_FORMAT_R8; + break; +#endif + case 16: + gbm_format = GBM_FORMAT_RGB565; + break; + case 32: + gbm_format = GBM_FORMAT_ARGB8888; + break; + case 24: + if (bitsPerPixel == 32) { + gbm_format = GBM_FORMAT_XRGB8888; + break; + } + /* fall through */ + default: + ErrorF("%s: Unsupported depth/bpp %d/%d\n", __func__, + depth, bitsPerPixel); + return NULL; + } + + pixmap_buffer = (struct amdgpu_buffer *)calloc(1, sizeof(struct amdgpu_buffer)); + if (!pixmap_buffer) { + return NULL; + } + pixmap_buffer->ref_count = 1; + + if ( bitsPerPixel == pScrn->bitsPerPixel) + bo_use |= GBM_BO_USE_SCANOUT; + +#ifdef CREATE_PIXMAP_USAGE_SHARED + if (usage_hint == CREATE_PIXMAP_USAGE_SHARED) { + /* XXX: Need to tell GBM to disable tiling in this case */ + } +#endif + + if (usage_hint & AMDGPU_CREATE_PIXMAP_LINEAR) { + bo_use |= GBM_BO_USE_LINEAR; + } + + pixmap_buffer->bo.gbm = gbm_bo_create(info->gbm, width, height, + gbm_format, + bo_use); + if (!pixmap_buffer->bo.gbm) { + free(pixmap_buffer); + return NULL; + } + + pixmap_buffer->flags |= AMDGPU_BO_FLAGS_GBM; + + if (new_pitch) + *new_pitch = gbm_bo_get_stride(pixmap_buffer->bo.gbm); + } else { + AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); + unsigned cpp = (bitsPerPixel + 7) / 8; + unsigned pitch = cpp * + AMDGPU_ALIGN(width, drmmode_get_pitch_align(pScrn, cpp)); + + pixmap_buffer = amdgpu_bo_open(pAMDGPUEnt->pDev, pitch * height, + 4096, AMDGPU_GEM_DOMAIN_VRAM); + + if (new_pitch) + *new_pitch = pitch; + } + + return pixmap_buffer; +} + +int amdgpu_bo_map(ScrnInfoPtr pScrn, struct amdgpu_buffer *bo) +{ + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + int ret = 0; + + if (info->use_glamor) + return 0; + + if (bo->flags & AMDGPU_BO_FLAGS_GBM) { + uint32_t handle, stride, height; + union drm_amdgpu_gem_mmap args; + int fd; + void *ptr; + + handle = gbm_bo_get_handle(bo->bo.gbm).u32; + stride = gbm_bo_get_stride(bo->bo.gbm); + height = gbm_bo_get_height(bo->bo.gbm); + fd = info->dri2.drm_fd; + + memset(&args, 0, sizeof(union drm_amdgpu_gem_mmap)); + args.in.handle = handle; + + ret = drmCommandWriteRead(fd, DRM_AMDGPU_GEM_MMAP, + &args, sizeof(args)); + if (ret) { + ErrorF("Failed to get the mmap offset\n"); + return ret; + } + + ptr = mmap(NULL, stride * height, + PROT_READ | PROT_WRITE, MAP_SHARED, + fd, args.out.addr_ptr); + + if (ptr == NULL) { + ErrorF("Failed to mmap the bo\n"); + return -1; + } + + bo->cpu_ptr = ptr; + } else + ret = amdgpu_bo_cpu_map(bo->bo.amdgpu, &bo->cpu_ptr); + + return ret; +} + +void amdgpu_bo_unmap(struct amdgpu_buffer *bo) +{ + if (bo->cpu_ptr == NULL) + return; + + if (bo->flags & AMDGPU_BO_FLAGS_GBM) { + uint32_t stride, height; + stride = gbm_bo_get_stride(bo->bo.gbm); + height = gbm_bo_get_height(bo->bo.gbm); + munmap(bo->cpu_ptr, stride * height); + } else + amdgpu_bo_cpu_unmap(bo->bo.amdgpu); +} + +struct amdgpu_buffer *amdgpu_bo_open(amdgpu_device_handle pDev, + uint32_t alloc_size, + uint32_t phys_alignment, + uint32_t domains) +{ + struct amdgpu_bo_alloc_request alloc_request; + struct amdgpu_bo_alloc_result buffer; + struct amdgpu_buffer *bo = NULL; + + memset(&alloc_request, 0, sizeof(struct amdgpu_bo_alloc_request)); + memset(&buffer, 0, sizeof(struct amdgpu_bo_alloc_result)); + + bo = (struct amdgpu_buffer *)calloc(1, sizeof(struct amdgpu_buffer)); + if (bo == NULL) { + return NULL; + } + + alloc_request.alloc_size = alloc_size; + alloc_request.phys_alignment = phys_alignment; + alloc_request.preferred_heap = domains; + + if (amdgpu_bo_alloc(pDev, &alloc_request, &buffer)) { + free(bo); + return NULL; + } + + bo->bo.amdgpu = buffer.buf_handle; + bo->ref_count = 1; + + return bo; +} + +void amdgpu_bo_ref(struct amdgpu_buffer *buffer) +{ + buffer->ref_count++; +} + +void amdgpu_bo_unref(struct amdgpu_buffer **buffer) +{ + struct amdgpu_buffer *buf = *buffer; + + buf->ref_count--; + if (buf->ref_count) { + return; + } + + amdgpu_bo_unmap(buf); + + if (buf->flags & AMDGPU_BO_FLAGS_GBM) { + gbm_bo_destroy(buf->bo.gbm); + } else { + amdgpu_bo_free(buf->bo.amdgpu); + } + free(buf); + *buffer = NULL; +} + +int amdgpu_query_bo_size(amdgpu_bo_handle buf_handle, uint32_t *size) +{ + struct amdgpu_bo_info buffer_info; + memset(&buffer_info, 0, sizeof(struct amdgpu_bo_info)); + int ret; + + ret = amdgpu_bo_query_info(buf_handle, &buffer_info); + if (ret) + *size = 0; + else + *size = (uint32_t)(buffer_info.alloc_size); + + return ret; +} + +int amdgpu_query_heap_size(amdgpu_device_handle pDev, + uint32_t heap, + uint64_t *heap_size, + uint64_t *max_allocation) +{ + struct amdgpu_heap_info heap_info; + memset(&heap_info, 0, sizeof(struct amdgpu_heap_info)); + int ret; + + ret = amdgpu_query_heap_info(pDev, heap, 0, &heap_info); + if (ret) { + *heap_size = 0; + *max_allocation = 0; + } else { + *heap_size = heap_info.heap_size; + *max_allocation = heap_info.max_allocation; + } + + return ret; +} + +struct amdgpu_buffer *amdgpu_gem_bo_open_prime(amdgpu_device_handle pDev, + int fd_handle, + uint32_t size) +{ + struct amdgpu_buffer *bo = NULL; + struct amdgpu_bo_import_result buffer = {0}; + + bo = (struct amdgpu_buffer *)calloc(1, sizeof(struct amdgpu_buffer)); + if (bo == NULL) { + return NULL; + } + + if (amdgpu_bo_import(pDev, amdgpu_bo_handle_type_dma_buf_fd, + (uint32_t)fd_handle, &buffer)) { + free(bo); + return FALSE; + } + bo->bo.amdgpu = buffer.buf_handle; + bo->ref_count = 1; + + return bo; +} + +#ifdef AMDGPU_PIXMAP_SHARING + +Bool amdgpu_share_pixmap_backing(struct amdgpu_buffer *bo, void **handle_p) +{ + int handle; + + amdgpu_bo_export(bo->bo.amdgpu, amdgpu_bo_handle_type_dma_buf_fd, + (uint32_t *)&handle); + + *handle_p = (void *)(long)handle; + return TRUE; +} + +Bool amdgpu_set_shared_pixmap_backing(PixmapPtr ppix, void *fd_handle) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(ppix->drawable.pScreen); + AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); + struct amdgpu_buffer *pixmap_buffer = NULL; + int ihandle = (int)(long)fd_handle; + uint32_t size = ppix->devKind * ppix->drawable.height; + + pixmap_buffer = amdgpu_gem_bo_open_prime(pAMDGPUEnt->pDev, ihandle, size); + if (!pixmap_buffer) { + return FALSE; + } + + amdgpu_set_pixmap_bo(ppix, pixmap_buffer); + + close(ihandle); + /* we have a reference from the alloc and one from set pixmap bo, + drop one */ + amdgpu_bo_unref(&pixmap_buffer); + return TRUE; +} + +#endif /* AMDGPU_PIXMAP_SHARING */ diff --git a/src/amdgpu_bo_helper.h b/src/amdgpu_bo_helper.h new file mode 100644 index 0000000..8270d39 --- /dev/null +++ b/src/amdgpu_bo_helper.h @@ -0,0 +1,103 @@ +/* + * Copyright 2012 Advanced Micro Devices, Inc. + * + * 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 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + */ + +#ifndef AMDGPU_BO_HELPER_H +#define AMDGPU_BO_HELPER_H 1 + +#include "amdgpu_drv.h" + +extern struct amdgpu_buffer *amdgpu_alloc_pixmap_bo(ScrnInfoPtr pScrn, int width, + int height, int depth, int usage_hint, + int bitsPerPixel, int *new_pitch); + +extern int amdgpu_bo_map(ScrnInfoPtr pScrn, struct amdgpu_buffer *bo); + +extern void amdgpu_bo_unmap(struct amdgpu_buffer *bo); + +extern Bool amdgpu_share_pixmap_backing(struct amdgpu_buffer *bo, void **handle_p); + +extern Bool +amdgpu_set_shared_pixmap_backing(PixmapPtr ppix, void *fd_handle); + +/* helper function to allocate memory to be used for GPU operations + * + * \param pDev - \c [in] device handle + * \param alloc_size - \c [in] allocation size + * \param phys_alignment - \c [in] requested alignment. 0 means no alignment requirement + * \param domains - \c [in] GEM domains + * + * \return pointer to amdgpu_buffer on success + * NULL on failure +*/ +extern struct amdgpu_buffer *amdgpu_bo_open(amdgpu_device_handle pDev, + uint32_t alloc_size, + uint32_t phys_alignment, + uint32_t domains); + +/* helper function to add the ref_count of a amdgpu_buffer + * \param buffer - \c [in] amdgpu_buffer +*/ +extern void amdgpu_bo_ref(struct amdgpu_buffer *buffer); + +/* helper function to dec the ref_count of a amdgpu_buffer + * \param buffer - \c [in] amdgpu_buffer +*/ +extern void amdgpu_bo_unref(struct amdgpu_buffer **buffer); + +/* helper function to query the buffer size + * \param buf_handle - \c [in] amdgpu bo handle + * \param size - \c [out] pointer to buffer size + * + * \return 0 on success + >0 - AMD specific error code \n + <0 - Negative POSIX error code +*/ +int amdgpu_query_bo_size(amdgpu_bo_handle buf_handle, uint32_t *size); + +/* helper function to query the heap information + * \param pDev - \c [in] amdgpu device handle + * \param heap - \c [in] heap type + * \param heap_size - \c [out] theoretical max available memory + * \param max_allcoation - \c [out] theoretical possible max. size of buffer + * + * \return 0 on success + >0 - AMD specific error code \n + <0 - Negative POSIX error code +*/ +int amdgpu_query_heap_size(amdgpu_device_handle pDev, + uint32_t heap, + uint64_t *heap_size, + uint64_t *max_allocation); + +/* helper function to convert a DMA buf handle to a KMS handle + * \param pDev - \c [in] amdgpu device handle + * \param fd_handle - \c [in] dma-buf fd handle + * \size size - \c [in] buffer size + * + * \return pointer to amdgpu_buffer on success + NULL on failure +*/ +struct amdgpu_buffer *amdgpu_gem_bo_open_prime(amdgpu_device_handle pDev, + int fd_handle, + uint32_t size); + +#endif /* AMDGPU_BO_HELPER_H */ diff --git a/src/amdgpu_chipinfo_gen.h b/src/amdgpu_chipinfo_gen.h new file mode 100644 index 0000000..ca28a98 --- /dev/null +++ b/src/amdgpu_chipinfo_gen.h @@ -0,0 +1,76 @@ +/* This file is autogenerated please do not edit */ +static AMDGPUCardInfo AMDGPUCards[] = { + { 0x6640, CHIP_FAMILY_BONAIRE }, + { 0x6641, CHIP_FAMILY_BONAIRE }, + { 0x6649, CHIP_FAMILY_BONAIRE }, + { 0x6650, CHIP_FAMILY_BONAIRE }, + { 0x6651, CHIP_FAMILY_BONAIRE }, + { 0x6658, CHIP_FAMILY_BONAIRE }, + { 0x665C, CHIP_FAMILY_BONAIRE }, + { 0x665D, CHIP_FAMILY_BONAIRE }, + { 0x9830, CHIP_FAMILY_KABINI }, + { 0x9831, CHIP_FAMILY_KABINI }, + { 0x9832, CHIP_FAMILY_KABINI }, + { 0x9833, CHIP_FAMILY_KABINI }, + { 0x9834, CHIP_FAMILY_KABINI }, + { 0x9835, CHIP_FAMILY_KABINI }, + { 0x9836, CHIP_FAMILY_KABINI }, + { 0x9837, CHIP_FAMILY_KABINI }, + { 0x9838, CHIP_FAMILY_KABINI }, + { 0x9839, CHIP_FAMILY_KABINI }, + { 0x983A, CHIP_FAMILY_KABINI }, + { 0x983B, CHIP_FAMILY_KABINI }, + { 0x983C, CHIP_FAMILY_KABINI }, + { 0x983D, CHIP_FAMILY_KABINI }, + { 0x983E, CHIP_FAMILY_KABINI }, + { 0x983F, CHIP_FAMILY_KABINI }, + { 0x1304, CHIP_FAMILY_KAVERI }, + { 0x1305, CHIP_FAMILY_KAVERI }, + { 0x1306, CHIP_FAMILY_KAVERI }, + { 0x1307, CHIP_FAMILY_KAVERI }, + { 0x1309, CHIP_FAMILY_KAVERI }, + { 0x130A, CHIP_FAMILY_KAVERI }, + { 0x130B, CHIP_FAMILY_KAVERI }, + { 0x130C, CHIP_FAMILY_KAVERI }, + { 0x130D, CHIP_FAMILY_KAVERI }, + { 0x130E, CHIP_FAMILY_KAVERI }, + { 0x130F, CHIP_FAMILY_KAVERI }, + { 0x1310, CHIP_FAMILY_KAVERI }, + { 0x1311, CHIP_FAMILY_KAVERI }, + { 0x1312, CHIP_FAMILY_KAVERI }, + { 0x1313, CHIP_FAMILY_KAVERI }, + { 0x1315, CHIP_FAMILY_KAVERI }, + { 0x1316, CHIP_FAMILY_KAVERI }, + { 0x1317, CHIP_FAMILY_KAVERI }, + { 0x131B, CHIP_FAMILY_KAVERI }, + { 0x131C, CHIP_FAMILY_KAVERI }, + { 0x131D, CHIP_FAMILY_KAVERI }, + { 0x67A0, CHIP_FAMILY_HAWAII }, + { 0x67A1, CHIP_FAMILY_HAWAII }, + { 0x67A2, CHIP_FAMILY_HAWAII }, + { 0x67A8, CHIP_FAMILY_HAWAII }, + { 0x67A9, CHIP_FAMILY_HAWAII }, + { 0x67AA, CHIP_FAMILY_HAWAII }, + { 0x67B0, CHIP_FAMILY_HAWAII }, + { 0x67B1, CHIP_FAMILY_HAWAII }, + { 0x67B8, CHIP_FAMILY_HAWAII }, + { 0x67B9, CHIP_FAMILY_HAWAII }, + { 0x67BA, CHIP_FAMILY_HAWAII }, + { 0x67BE, CHIP_FAMILY_HAWAII }, + { 0x6900, CHIP_FAMILY_TOPAZ }, + { 0x6901, CHIP_FAMILY_TOPAZ }, + { 0x6902, CHIP_FAMILY_TOPAZ }, + { 0x6903, CHIP_FAMILY_TOPAZ }, + { 0x6907, CHIP_FAMILY_TOPAZ }, + { 0x6920, CHIP_FAMILY_TONGA }, + { 0x6921, CHIP_FAMILY_TONGA }, + { 0x6938, CHIP_FAMILY_TONGA }, + { 0x6939, CHIP_FAMILY_TONGA }, + { 0x692B, CHIP_FAMILY_TONGA }, + { 0x692F, CHIP_FAMILY_TONGA }, + { 0x9870, CHIP_FAMILY_CARRIZO }, + { 0x9874, CHIP_FAMILY_CARRIZO }, + { 0x9875, CHIP_FAMILY_CARRIZO }, + { 0x9876, CHIP_FAMILY_CARRIZO }, + { 0x9877, CHIP_FAMILY_CARRIZO }, +}; diff --git a/src/amdgpu_chipset_gen.h b/src/amdgpu_chipset_gen.h new file mode 100644 index 0000000..258ee22 --- /dev/null +++ b/src/amdgpu_chipset_gen.h @@ -0,0 +1,77 @@ +/* This file is autogenerated please do not edit */ +SymTabRec AMDGPUChipsets[] = { + { PCI_CHIP_BONAIRE_6640, "BONAIRE" }, + { PCI_CHIP_BONAIRE_6641, "BONAIRE" }, + { PCI_CHIP_BONAIRE_6649, "BONAIRE" }, + { PCI_CHIP_BONAIRE_6650, "BONAIRE" }, + { PCI_CHIP_BONAIRE_6651, "BONAIRE" }, + { PCI_CHIP_BONAIRE_6658, "BONAIRE" }, + { PCI_CHIP_BONAIRE_665C, "BONAIRE" }, + { PCI_CHIP_BONAIRE_665D, "BONAIRE" }, + { PCI_CHIP_KABINI_9830, "KABINI" }, + { PCI_CHIP_KABINI_9831, "KABINI" }, + { PCI_CHIP_KABINI_9832, "KABINI" }, + { PCI_CHIP_KABINI_9833, "KABINI" }, + { PCI_CHIP_KABINI_9834, "KABINI" }, + { PCI_CHIP_KABINI_9835, "KABINI" }, + { PCI_CHIP_KABINI_9836, "KABINI" }, + { PCI_CHIP_KABINI_9837, "KABINI" }, + { PCI_CHIP_KABINI_9838, "KABINI" }, + { PCI_CHIP_KABINI_9839, "KABINI" }, + { PCI_CHIP_KABINI_983A, "KABINI" }, + { PCI_CHIP_KABINI_983B, "KABINI" }, + { PCI_CHIP_KABINI_983C, "KABINI" }, + { PCI_CHIP_KABINI_983D, "KABINI" }, + { PCI_CHIP_KABINI_983E, "KABINI" }, + { PCI_CHIP_KABINI_983F, "KABINI" }, + { PCI_CHIP_KAVERI_1304, "KAVERI" }, + { PCI_CHIP_KAVERI_1305, "KAVERI" }, + { PCI_CHIP_KAVERI_1306, "KAVERI" }, + { PCI_CHIP_KAVERI_1307, "KAVERI" }, + { PCI_CHIP_KAVERI_1309, "KAVERI" }, + { PCI_CHIP_KAVERI_130A, "KAVERI" }, + { PCI_CHIP_KAVERI_130B, "KAVERI" }, + { PCI_CHIP_KAVERI_130C, "KAVERI" }, + { PCI_CHIP_KAVERI_130D, "KAVERI" }, + { PCI_CHIP_KAVERI_130E, "KAVERI" }, + { PCI_CHIP_KAVERI_130F, "KAVERI" }, + { PCI_CHIP_KAVERI_1310, "KAVERI" }, + { PCI_CHIP_KAVERI_1311, "KAVERI" }, + { PCI_CHIP_KAVERI_1312, "KAVERI" }, + { PCI_CHIP_KAVERI_1313, "KAVERI" }, + { PCI_CHIP_KAVERI_1315, "KAVERI" }, + { PCI_CHIP_KAVERI_1316, "KAVERI" }, + { PCI_CHIP_KAVERI_1317, "KAVERI" }, + { PCI_CHIP_KAVERI_131B, "KAVERI" }, + { PCI_CHIP_KAVERI_131C, "KAVERI" }, + { PCI_CHIP_KAVERI_131D, "KAVERI" }, + { PCI_CHIP_HAWAII_67A0, "HAWAII" }, + { PCI_CHIP_HAWAII_67A1, "HAWAII" }, + { PCI_CHIP_HAWAII_67A2, "HAWAII" }, + { PCI_CHIP_HAWAII_67A8, "HAWAII" }, + { PCI_CHIP_HAWAII_67A9, "HAWAII" }, + { PCI_CHIP_HAWAII_67AA, "HAWAII" }, + { PCI_CHIP_HAWAII_67B0, "HAWAII" }, + { PCI_CHIP_HAWAII_67B1, "HAWAII" }, + { PCI_CHIP_HAWAII_67B8, "HAWAII" }, + { PCI_CHIP_HAWAII_67B9, "HAWAII" }, + { PCI_CHIP_HAWAII_67BA, "HAWAII" }, + { PCI_CHIP_HAWAII_67BE, "HAWAII" }, + { PCI_CHIP_TOPAZ_6900, "TOPAZ" }, + { PCI_CHIP_TOPAZ_6901, "TOPAZ" }, + { PCI_CHIP_TOPAZ_6902, "TOPAZ" }, + { PCI_CHIP_TOPAZ_6903, "TOPAZ" }, + { PCI_CHIP_TOPAZ_6907, "TOPAZ" }, + { PCI_CHIP_TONGA_6920, "TONGA" }, + { PCI_CHIP_TONGA_6921, "TONGA" }, + { PCI_CHIP_TONGA_6938, "TONGA" }, + { PCI_CHIP_TONGA_6939, "TONGA" }, + { PCI_CHIP_TONGA_692B, "TONGA" }, + { PCI_CHIP_TONGA_692F, "TONGA" }, + { PCI_CHIP_CARRIZO_9870, "CARRIZO" }, + { PCI_CHIP_CARRIZO_9874, "CARRIZO" }, + { PCI_CHIP_CARRIZO_9875, "CARRIZO" }, + { PCI_CHIP_CARRIZO_9876, "CARRIZO" }, + { PCI_CHIP_CARRIZO_9877, "CARRIZO" }, + { -1, NULL } +}; diff --git a/src/amdgpu_dri2.c b/src/amdgpu_dri2.c new file mode 100644 index 0000000..26906d3 --- /dev/null +++ b/src/amdgpu_dri2.c @@ -0,0 +1,1583 @@ +/* + * Copyright 2008 Kristian Høgsberg + * Copyright 2008 Jérôme Glisse + * + * All Rights Reserved. + * + * 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, sublicense, 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 ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "amdgpu_drv.h" +#include "amdgpu_dri2.h" +#include "amdgpu_video.h" +#include "amdgpu_pixmap.h" + +#ifdef DRI2 + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#include <gbm.h> + +#include "amdgpu_version.h" + +#if HAVE_LIST_H +#include "list.h" +#if !HAVE_XORG_LIST +#define xorg_list list +#define xorg_list_init list_init +#define xorg_list_add list_add +#define xorg_list_del list_del +#define xorg_list_for_each_entry list_for_each_entry +#endif +#endif + +#if DRI2INFOREC_VERSION >= 4 && HAVE_LIST_H +#define USE_DRI2_SCHEDULING +#endif + +#if DRI2INFOREC_VERSION >= 9 +#define USE_DRI2_PRIME +#endif + +#define FALLBACK_SWAP_DELAY 16 + +#include <glamor.h> + +typedef DRI2BufferPtr BufferPtr; + +struct dri2_buffer_priv { + PixmapPtr pixmap; + unsigned int attachment; + unsigned int refcnt; +}; + +static PixmapPtr get_drawable_pixmap(DrawablePtr drawable) +{ + if (drawable->type == DRAWABLE_PIXMAP) + return (PixmapPtr) drawable; + else + return (*drawable->pScreen-> + GetWindowPixmap) ((WindowPtr) drawable); +} + +static PixmapPtr fixup_glamor(DrawablePtr drawable, PixmapPtr pixmap) +{ + PixmapPtr old = get_drawable_pixmap(drawable); + ScreenPtr screen = drawable->pScreen; + struct amdgpu_pixmap *priv = amdgpu_get_pixmap_private(pixmap); + GCPtr gc; + + /* With a glamor pixmap, 2D pixmaps are created in texture + * and without a static BO attached to it. To support DRI, + * we need to create a new textured-drm pixmap and + * need to copy the original content to this new textured-drm + * pixmap, and then convert the old pixmap to a coherent + * textured-drm pixmap which has a valid BO attached to it + * and also has a valid texture, thus both glamor and DRI2 + * can access it. + * + */ + + /* Copy the current contents of the pixmap to the bo. */ + gc = GetScratchGC(drawable->depth, screen); + if (gc) { + ValidateGC(&pixmap->drawable, gc); + gc->ops->CopyArea(&old->drawable, &pixmap->drawable, + gc, + 0, 0, + old->drawable.width, + old->drawable.height, 0, 0); + FreeScratchGC(gc); + } + + amdgpu_set_pixmap_private(pixmap, NULL); + + /* And redirect the pixmap to the new bo (for 3D). */ + glamor_egl_exchange_buffers(old, pixmap); + amdgpu_set_pixmap_private(old, priv); + screen->DestroyPixmap(pixmap); + old->refcnt++; + + screen->ModifyPixmapHeader(old, + old->drawable.width, + old->drawable.height, + 0, 0, priv->stride, NULL); + + return old; +} + +static BufferPtr +amdgpu_dri2_create_buffer2(ScreenPtr pScreen, + DrawablePtr drawable, + unsigned int attachment, unsigned int format) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + BufferPtr buffers; + struct dri2_buffer_priv *privates; + PixmapPtr pixmap, depth_pixmap; + struct amdgpu_buffer *bo = NULL; + unsigned front_width; + unsigned aligned_width = drawable->width; + unsigned height = drawable->height; + Bool is_glamor_pixmap = FALSE; + int depth; + int cpp; + + if (format) { + depth = format; + + switch (depth) { + case 15: + cpp = 2; + break; + case 24: + cpp = 4; + break; + default: + cpp = depth / 8; + } + } else { + depth = drawable->depth; + cpp = drawable->bitsPerPixel / 8; + } + + pixmap = pScreen->GetScreenPixmap(pScreen); + front_width = pixmap->drawable.width; + + pixmap = depth_pixmap = NULL; + + if (attachment == DRI2BufferFrontLeft) { + pixmap = get_drawable_pixmap(drawable); + if (pScreen != pixmap->drawable.pScreen) + pixmap = NULL; + else if (info->use_glamor && !amdgpu_get_pixmap_bo(pixmap)) { + is_glamor_pixmap = TRUE; + aligned_width = pixmap->drawable.width; + height = pixmap->drawable.height; + pixmap = NULL; + } else + pixmap->refcnt++; + } else if (attachment == DRI2BufferStencil && depth_pixmap) { + pixmap = depth_pixmap; + pixmap->refcnt++; + } + + if (!pixmap && (is_glamor_pixmap || attachment != DRI2BufferFrontLeft)) { + if (aligned_width == front_width) + aligned_width = pScrn->virtualX; + + pixmap = (*pScreen->CreatePixmap) (pScreen, + aligned_width, + height, + depth, + AMDGPU_CREATE_PIXMAP_DRI2); + } + + buffers = calloc(1, sizeof *buffers); + if (buffers == NULL) + goto error; + + if (attachment == DRI2BufferDepth) { + depth_pixmap = pixmap; + } + + if (pixmap) { + struct drm_gem_flink flink; + union gbm_bo_handle bo_handle; + + if (is_glamor_pixmap) + pixmap = fixup_glamor(drawable, pixmap); + bo = amdgpu_get_pixmap_bo(pixmap); + if (!bo) { + goto error; + } + + if (bo->flags & AMDGPU_BO_FLAGS_GBM) { + bo_handle = gbm_bo_get_handle(bo->bo.gbm); + flink.handle = bo_handle.u32; + if (ioctl(info->dri2.drm_fd, DRM_IOCTL_GEM_FLINK, &flink) < 0) + goto error; + buffers->name = flink.name; + } else { + amdgpu_bo_export(bo->bo.amdgpu, + amdgpu_bo_handle_type_gem_flink_name, + &buffers->name); + } + } + + privates = calloc(1, sizeof(struct dri2_buffer_priv)); + if (privates == NULL) + goto error; + + buffers->attachment = attachment; + if (pixmap) { + buffers->pitch = pixmap->devKind; + buffers->cpp = cpp; + } + buffers->driverPrivate = privates; + buffers->format = format; + buffers->flags = 0; /* not tiled */ + privates->pixmap = pixmap; + privates->attachment = attachment; + privates->refcnt = 1; + + return buffers; + +error: + free(buffers); + if (pixmap) + (*pScreen->DestroyPixmap) (pixmap); + return NULL; +} + +DRI2BufferPtr +amdgpu_dri2_create_buffer(DrawablePtr pDraw, unsigned int attachment, + unsigned int format) +{ + return amdgpu_dri2_create_buffer2(pDraw->pScreen, pDraw, + attachment, format); +} + +static void +amdgpu_dri2_destroy_buffer2(ScreenPtr pScreen, + DrawablePtr drawable, BufferPtr buffers) +{ + if (buffers) { + struct dri2_buffer_priv *private = buffers->driverPrivate; + + /* Trying to free an already freed buffer is unlikely to end well */ + if (private->refcnt == 0) { + ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen); + + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "Attempted to destroy previously destroyed buffer.\ + This is a programming error\n"); + return; + } + + private->refcnt--; + if (private->refcnt == 0) { + if (private->pixmap) + (*pScreen->DestroyPixmap) (private->pixmap); + + free(buffers->driverPrivate); + free(buffers); + } + } +} + +void amdgpu_dri2_destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buf) +{ + amdgpu_dri2_destroy_buffer2(pDraw->pScreen, pDraw, buf); +} + +static inline PixmapPtr GetDrawablePixmap(DrawablePtr drawable) +{ + if (drawable->type == DRAWABLE_PIXMAP) + return (PixmapPtr) drawable; + else { + struct _Window *pWin = (struct _Window *)drawable; + return drawable->pScreen->GetWindowPixmap(pWin); + } +} + +static void +amdgpu_dri2_copy_region2(ScreenPtr pScreen, + DrawablePtr drawable, + RegionPtr region, + BufferPtr dest_buffer, BufferPtr src_buffer) +{ + struct dri2_buffer_priv *src_private = src_buffer->driverPrivate; + struct dri2_buffer_priv *dst_private = dest_buffer->driverPrivate; + DrawablePtr src_drawable; + DrawablePtr dst_drawable; + RegionPtr copy_clip; + GCPtr gc; + Bool translate = FALSE; + int off_x = 0, off_y = 0; + + src_drawable = &src_private->pixmap->drawable; + dst_drawable = &dst_private->pixmap->drawable; + + if (src_private->attachment == DRI2BufferFrontLeft) { + src_drawable = drawable; + } + if (dst_private->attachment == DRI2BufferFrontLeft) { +#ifdef USE_DRI2_PRIME + if (drawable->pScreen != pScreen) { + dst_drawable = DRI2UpdatePrime(drawable, dest_buffer); + if (!dst_drawable) + return; + if (dst_drawable != drawable) + translate = TRUE; + } else +#endif + dst_drawable = drawable; + } + + if (translate && drawable->type == DRAWABLE_WINDOW) { + PixmapPtr pPix = GetDrawablePixmap(drawable); + + off_x = drawable->x - pPix->screen_x; + off_y = drawable->y - pPix->screen_y; + } + gc = GetScratchGC(dst_drawable->depth, pScreen); + copy_clip = REGION_CREATE(pScreen, NULL, 0); + REGION_COPY(pScreen, copy_clip, region); + + if (translate) { + REGION_TRANSLATE(pScreen, copy_clip, off_x, off_y); + } + + (*gc->funcs->ChangeClip) (gc, CT_REGION, copy_clip, 0); + ValidateGC(dst_drawable, gc); + + /* If this is a full buffer swap or frontbuffer flush, throttle on the + * previous one + */ + if (dst_private->attachment == DRI2BufferFrontLeft) { + if (REGION_NUM_RECTS(region) == 1) { + BoxPtr extents = REGION_EXTENTS(pScreen, region); + + if (extents->x1 == 0 && extents->y1 == 0 && + extents->x2 == drawable->width && + extents->y2 == drawable->height) { + char pixel[4]; + + /* XXX: This is a pretty big hammer... */ + pScreen->GetImage(drawable, 0, 0, 1, 1, + ZPixmap, ~0, pixel); + } + } + } + + (*gc->ops->CopyArea) (src_drawable, dst_drawable, gc, + 0, 0, drawable->width, drawable->height, off_x, + off_y); + + FreeScratchGC(gc); +} + +void +amdgpu_dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion, + DRI2BufferPtr pDstBuffer, DRI2BufferPtr pSrcBuffer) +{ + return amdgpu_dri2_copy_region2(pDraw->pScreen, pDraw, pRegion, + pDstBuffer, pSrcBuffer); +} + +#ifdef USE_DRI2_SCHEDULING + +enum DRI2FrameEventType { + DRI2_SWAP, + DRI2_FLIP, + DRI2_WAITMSC, +}; + +typedef struct _DRI2FrameEvent { + XID drawable_id; + ClientPtr client; + enum DRI2FrameEventType type; + int frame; + xf86CrtcPtr crtc; + + /* for swaps & flips only */ + DRI2SwapEventPtr event_complete; + void *event_data; + DRI2BufferPtr front; + DRI2BufferPtr back; + + Bool valid; + + struct xorg_list link; +} DRI2FrameEventRec, *DRI2FrameEventPtr; + +typedef struct _DRI2ClientEvents { + struct xorg_list reference_list; +} DRI2ClientEventsRec, *DRI2ClientEventsPtr; + +#if HAS_DEVPRIVATEKEYREC + +static int DRI2InfoCnt; + +static DevPrivateKeyRec DRI2ClientEventsPrivateKeyRec; +#define DRI2ClientEventsPrivateKey (&DRI2ClientEventsPrivateKeyRec) + +#else + +static int DRI2ClientEventsPrivateKeyIndex; +DevPrivateKey DRI2ClientEventsPrivateKey = &DRI2ClientEventsPrivateKeyIndex; + +#endif /* HAS_DEVPRIVATEKEYREC */ + +#define GetDRI2ClientEvents(pClient) ((DRI2ClientEventsPtr) \ + dixLookupPrivate(&(pClient)->devPrivates, DRI2ClientEventsPrivateKey)) + +static int ListAddDRI2ClientEvents(ClientPtr client, struct xorg_list *entry) +{ + DRI2ClientEventsPtr pClientPriv; + pClientPriv = GetDRI2ClientEvents(client); + + if (!pClientPriv) { + return BadAlloc; + } + + xorg_list_add(entry, &pClientPriv->reference_list); + return 0; +} + +static void ListDelDRI2ClientEvents(ClientPtr client, struct xorg_list *entry) +{ + DRI2ClientEventsPtr pClientPriv; + pClientPriv = GetDRI2ClientEvents(client); + + if (!pClientPriv) { + return; + } + xorg_list_del(entry); +} + +static void amdgpu_dri2_ref_buffer(BufferPtr buffer) +{ + struct dri2_buffer_priv *private = buffer->driverPrivate; + private->refcnt++; +} + +static void amdgpu_dri2_unref_buffer(BufferPtr buffer) +{ + if (buffer) { + struct dri2_buffer_priv *private = buffer->driverPrivate; + amdgpu_dri2_destroy_buffer(&(private->pixmap->drawable), + buffer); + } +} + +static void +amdgpu_dri2_client_state_changed(CallbackListPtr * ClientStateCallback, + pointer data, pointer calldata) +{ + DRI2ClientEventsPtr pClientEventsPriv; + DRI2FrameEventPtr ref; + NewClientInfoRec *clientinfo = calldata; + ClientPtr pClient = clientinfo->client; + pClientEventsPriv = GetDRI2ClientEvents(pClient); + + switch (pClient->clientState) { + case ClientStateInitial: + xorg_list_init(&pClientEventsPriv->reference_list); + break; + case ClientStateRunning: + break; + + case ClientStateRetained: + case ClientStateGone: + if (pClientEventsPriv) { + xorg_list_for_each_entry(ref, + &pClientEventsPriv-> + reference_list, link) { + ref->valid = FALSE; + amdgpu_dri2_unref_buffer(ref->front); + amdgpu_dri2_unref_buffer(ref->back); + } + } + break; + default: + break; + } +} + +static +xf86CrtcPtr amdgpu_dri2_drawable_crtc(DrawablePtr pDraw, Bool consider_disabled) +{ + ScreenPtr pScreen = pDraw->pScreen; + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + xf86CrtcPtr crtc; + + crtc = amdgpu_pick_best_crtc(pScrn, consider_disabled, + pDraw->x, + pDraw->x + pDraw->width, + pDraw->y, pDraw->y + pDraw->height); + + /* Make sure the CRTC is valid and this is the real front buffer */ + if (crtc != NULL && !crtc->rotatedData) + return crtc; + else + return NULL; +} + +static Bool +amdgpu_dri2_schedule_flip(ScrnInfoPtr scrn, ClientPtr client, + DrawablePtr draw, DRI2BufferPtr front, + DRI2BufferPtr back, DRI2SwapEventPtr func, + void *data, unsigned int target_msc) +{ + struct dri2_buffer_priv *back_priv; + struct amdgpu_buffer *bo = NULL; + DRI2FrameEventPtr flip_info; + /* Main crtc for this drawable shall finally deliver pageflip event. */ + xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw, FALSE); + int ref_crtc_hw_id = crtc ? drmmode_get_crtc_id(crtc) : -1; + + flip_info = calloc(1, sizeof(DRI2FrameEventRec)); + if (!flip_info) + return FALSE; + + flip_info->drawable_id = draw->id; + flip_info->client = client; + flip_info->type = DRI2_SWAP; + flip_info->event_complete = func; + flip_info->event_data = data; + flip_info->frame = target_msc; + flip_info->crtc = crtc; + + xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "%s:%d fevent[%p]\n", __func__, __LINE__, flip_info); + + /* Page flip the full screen buffer */ + back_priv = back->driverPrivate; + bo = amdgpu_get_pixmap_bo(back_priv->pixmap); + + return amdgpu_do_pageflip(scrn, bo, flip_info, ref_crtc_hw_id); +} + +static Bool update_front(DrawablePtr draw, DRI2BufferPtr front) +{ + ScreenPtr screen = draw->pScreen; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + AMDGPUInfoPtr info = AMDGPUPTR(scrn); + PixmapPtr pixmap; + struct dri2_buffer_priv *priv = front->driverPrivate; + struct amdgpu_buffer *bo = NULL; + union gbm_bo_handle bo_handle; + struct drm_gem_flink flink; + + pixmap = get_drawable_pixmap(draw); + pixmap->refcnt++; + + bo = amdgpu_get_pixmap_bo(pixmap); + if (bo->flags & AMDGPU_BO_FLAGS_GBM) { + bo_handle = gbm_bo_get_handle(bo->bo.gbm); + flink.handle = bo_handle.u32; + if (ioctl(info->dri2.drm_fd, DRM_IOCTL_GEM_FLINK, &flink) < 0) + return FALSE; + front->name = flink.name; + } else { + amdgpu_bo_export(bo->bo.amdgpu, + amdgpu_bo_handle_type_gem_flink_name, + &front->name); + } + (*draw->pScreen->DestroyPixmap) (priv->pixmap); + front->pitch = pixmap->devKind; + front->cpp = pixmap->drawable.bitsPerPixel / 8; + priv->pixmap = pixmap; + + return TRUE; +} + +static Bool +can_exchange(ScrnInfoPtr pScrn, DrawablePtr draw, + DRI2BufferPtr front, DRI2BufferPtr back) +{ + struct dri2_buffer_priv *front_priv = front->driverPrivate; + struct dri2_buffer_priv *back_priv = back->driverPrivate; + PixmapPtr front_pixmap; + PixmapPtr back_pixmap = back_priv->pixmap; + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); + int i; + + for (i = 0; i < xf86_config->num_crtc; i++) { + xf86CrtcPtr crtc = xf86_config->crtc[i]; + if (crtc->enabled && crtc->rotatedData) + return FALSE; + } + + if (!update_front(draw, front)) + return FALSE; + + front_pixmap = front_priv->pixmap; + + if (front_pixmap->drawable.width != back_pixmap->drawable.width) + return FALSE; + + if (front_pixmap->drawable.height != back_pixmap->drawable.height) + return FALSE; + + if (front_pixmap->drawable.bitsPerPixel != + back_pixmap->drawable.bitsPerPixel) + return FALSE; + + if (front_pixmap->devKind != back_pixmap->devKind) + return FALSE; + + return TRUE; +} + +static Bool +can_flip(ScrnInfoPtr pScrn, DrawablePtr draw, + DRI2BufferPtr front, DRI2BufferPtr back) +{ + return draw->type == DRAWABLE_WINDOW && + AMDGPUPTR(pScrn)->allowPageFlip && + pScrn->vtSema && + DRI2CanFlip(draw) && can_exchange(pScrn, draw, front, back); +} + +static void +amdgpu_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front, + DRI2BufferPtr back) +{ + struct dri2_buffer_priv *front_priv = front->driverPrivate; + struct dri2_buffer_priv *back_priv = back->driverPrivate; + struct amdgpu_buffer *front_bo = NULL; + struct amdgpu_buffer *back_bo = NULL; + ScreenPtr screen; + AMDGPUInfoPtr info; + RegionRec region; + int tmp; + + region.extents.x1 = region.extents.y1 = 0; + region.extents.x2 = front_priv->pixmap->drawable.width; + region.extents.y2 = front_priv->pixmap->drawable.width; + region.data = NULL; + DamageRegionAppend(&front_priv->pixmap->drawable, ®ion); + + /* Swap BO names so DRI works */ + tmp = front->name; + front->name = back->name; + back->name = tmp; + + /* Swap pixmap bos */ + front_bo = amdgpu_get_pixmap_bo(front_priv->pixmap); + back_bo = amdgpu_get_pixmap_bo(back_priv->pixmap); + amdgpu_set_pixmap_bo(front_priv->pixmap, back_bo); + amdgpu_set_pixmap_bo(back_priv->pixmap, front_bo); + + /* Do we need to update the Screen? */ + screen = draw->pScreen; + info = AMDGPUPTR(xf86ScreenToScrn(screen)); + if (front_bo == info->front_buffer) { + amdgpu_bo_ref(back_bo); + amdgpu_bo_unref(&info->front_buffer); + info->front_buffer = back_bo; + amdgpu_set_pixmap_bo(screen->GetScreenPixmap(screen), back_bo); + } + + amdgpu_glamor_exchange_buffers(front_priv->pixmap, back_priv->pixmap); + + DamageRegionProcessPending(&front_priv->pixmap->drawable); +} + +void amdgpu_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + DRI2FrameEventPtr event = event_data; + DrawablePtr drawable; + ScreenPtr screen; + ScrnInfoPtr scrn; + int status; + int swap_type; + BoxRec box; + RegionRec region; + + if (!event->valid) + goto cleanup; + + status = dixLookupDrawable(&drawable, event->drawable_id, serverClient, + M_ANY, DixWriteAccess); + if (status != Success) + goto cleanup; + if (!event->crtc) + goto cleanup; + frame += amdgpu_get_interpolated_vblanks(event->crtc); + + screen = drawable->pScreen; + scrn = xf86ScreenToScrn(screen); + + switch (event->type) { + case DRI2_FLIP: + if (can_flip(scrn, drawable, event->front, event->back) && + amdgpu_dri2_schedule_flip(scrn, + event->client, + drawable, + event->front, + event->back, + event->event_complete, + event->event_data, + event->frame)) { + amdgpu_dri2_exchange_buffers(drawable, event->front, + event->back); + break; + } + /* else fall through to exchange/blit */ + case DRI2_SWAP: + if (DRI2CanExchange(drawable) && + can_exchange(scrn, drawable, event->front, event->back)) { + amdgpu_dri2_exchange_buffers(drawable, event->front, + event->back); + swap_type = DRI2_EXCHANGE_COMPLETE; + } else { + box.x1 = 0; + box.y1 = 0; + box.x2 = drawable->width; + box.y2 = drawable->height; + REGION_INIT(pScreen, ®ion, &box, 0); + amdgpu_dri2_copy_region(drawable, ®ion, event->front, + event->back); + swap_type = DRI2_BLIT_COMPLETE; + } + + DRI2SwapComplete(event->client, drawable, frame, tv_sec, + tv_usec, swap_type, event->event_complete, + event->event_data); + + break; + case DRI2_WAITMSC: + DRI2WaitMSCComplete(event->client, drawable, frame, tv_sec, + tv_usec); + break; + default: + /* Unknown type */ + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "%s: unknown vblank event received\n", __func__); + break; + } + +cleanup: + if (event->valid) { + amdgpu_dri2_unref_buffer(event->front); + amdgpu_dri2_unref_buffer(event->back); + ListDelDRI2ClientEvents(event->client, &event->link); + } + free(event); +} + +drmVBlankSeqType amdgpu_populate_vbl_request_type(xf86CrtcPtr crtc) +{ + drmVBlankSeqType type = 0; + int crtc_id = drmmode_get_crtc_id(crtc); + + if (crtc_id == 1) + type |= DRM_VBLANK_SECONDARY; + else if (crtc_id > 1) +#ifdef DRM_VBLANK_HIGH_CRTC_SHIFT + type |= (crtc_id << DRM_VBLANK_HIGH_CRTC_SHIFT) & + DRM_VBLANK_HIGH_CRTC_MASK; +#else + ErrorF("amdgpu driver bug: %s called for CRTC %d > 1, but " + "DRM_VBLANK_HIGH_CRTC_MASK not defined at build time\n", + __func__, crtc_id); +#endif + + return type; +} + +/* + * This function should be called on a disabled CRTC only (i.e., CRTC + * in DPMS-off state). It will calculate the delay necessary to reach + * target_msc from present time if the CRTC were running. + */ +static +CARD32 amdgpu_dri2_extrapolate_msc_delay(xf86CrtcPtr crtc, CARD64 * target_msc, + CARD64 divisor, CARD64 remainder) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + ScrnInfoPtr pScrn = crtc->scrn; + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + int nominal_frame_rate = drmmode_crtc->dpms_last_fps; + CARD64 last_vblank_ust = drmmode_crtc->dpms_last_ust; + uint32_t last_vblank_seq = drmmode_crtc->dpms_last_seq; + int interpolated_vblanks = drmmode_crtc->interpolated_vblanks; + int target_seq; + CARD64 now, target_time, delta_t; + int64_t d, delta_seq; + int ret; + CARD32 d_ms; + + if (!last_vblank_ust) { + *target_msc = 0; + return FALLBACK_SWAP_DELAY; + } + ret = drmmode_get_current_ust(info->dri2.drm_fd, &now); + if (ret) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "%s cannot get current time\n", __func__); + *target_msc = 0; + return FALLBACK_SWAP_DELAY; + } + target_seq = (int)*target_msc - interpolated_vblanks; + delta_seq = (int64_t) target_seq - (int64_t) last_vblank_seq; + delta_seq *= 1000000; + target_time = last_vblank_ust; + target_time += delta_seq / nominal_frame_rate; + d = target_time - now; + if (d < 0) { + /* we missed the event, adjust target_msc, do the divisor magic */ + CARD64 current_msc; + current_msc = last_vblank_seq + interpolated_vblanks; + delta_t = now - last_vblank_ust; + delta_seq = delta_t * nominal_frame_rate; + current_msc += delta_seq / 1000000; + current_msc &= 0xffffffff; + if (divisor == 0) { + *target_msc = current_msc; + d = 0; + } else { + *target_msc = + current_msc - (current_msc % divisor) + remainder; + if ((current_msc % divisor) >= remainder) + *target_msc += divisor; + *target_msc &= 0xffffffff; + target_seq = (int)*target_msc - interpolated_vblanks; + delta_seq = + (int64_t) target_seq - (int64_t) last_vblank_seq; + delta_seq *= 1000000; + target_time = last_vblank_ust; + target_time += delta_seq / nominal_frame_rate; + d = target_time - now; + } + } + /* + * convert delay to milliseconds and add margin to prevent the client + * from coming back early (due to timer granularity and rounding + * errors) and getting the same MSC it just got + */ + d_ms = (CARD32) d / 1000; + if ((CARD32) d - d_ms * 1000 > 0) + d_ms += 2; + else + d_ms++; + return d_ms; +} + +/* + * Get current frame count and frame count timestamp, based on drawable's + * crtc. + */ +static int amdgpu_dri2_get_msc(DrawablePtr draw, CARD64 * ust, CARD64 * msc) +{ + ScreenPtr screen = draw->pScreen; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + AMDGPUInfoPtr info = AMDGPUPTR(scrn); + drmVBlank vbl; + int ret; + xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw, TRUE); + + /* Drawable not displayed, make up a value */ + if (crtc == NULL) { + *ust = 0; + *msc = 0; + return TRUE; + } + if (amdgpu_crtc_is_enabled(crtc)) { + /* CRTC is running, read vblank counter and timestamp */ + vbl.request.type = DRM_VBLANK_RELATIVE; + vbl.request.type |= amdgpu_populate_vbl_request_type(crtc); + vbl.request.sequence = 0; + + ret = drmWaitVBlank(info->dri2.drm_fd, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "get vblank counter failed: %s\n", + strerror(errno)); + return FALSE; + } + + *ust = + ((CARD64) vbl.reply.tval_sec * 1000000) + + vbl.reply.tval_usec; + *msc = + vbl.reply.sequence + amdgpu_get_interpolated_vblanks(crtc); + *msc &= 0xffffffff; + } else { + /* CRTC is not running, extrapolate MSC and timestamp */ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + CARD64 now, delta_t, delta_seq; + + if (!drmmode_crtc->dpms_last_ust) + return FALSE; + ret = drmmode_get_current_ust(info->dri2.drm_fd, &now); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "%s cannot get current time\n", __func__); + return FALSE; + } + delta_t = now - drmmode_crtc->dpms_last_ust; + delta_seq = delta_t * drmmode_crtc->dpms_last_fps; + delta_seq /= 1000000; + *ust = drmmode_crtc->dpms_last_ust; + delta_t = delta_seq * 1000000; + delta_t /= drmmode_crtc->dpms_last_fps; + *ust += delta_t; + *msc = drmmode_crtc->dpms_last_seq; + *msc += drmmode_crtc->interpolated_vblanks; + *msc += delta_seq; + *msc &= 0xffffffff; + } + return TRUE; +} + +static +CARD32 amdgpu_dri2_deferred_event(OsTimerPtr timer, CARD32 now, pointer data) +{ + DRI2FrameEventPtr event_info = (DRI2FrameEventPtr) data; + DrawablePtr drawable; + ScreenPtr screen; + ScrnInfoPtr scrn; + AMDGPUInfoPtr info; + int status; + CARD64 drm_now; + int ret; + unsigned int tv_sec, tv_usec; + CARD64 delta_t, delta_seq, frame; + drmmode_crtc_private_ptr drmmode_crtc; + TimerFree(timer); + + /* + * This is emulated event, so its time is current time, which we + * have to get in DRM-compatible form (which is a bit messy given + * the information that we have at this point). Can't use now argument + * because DRM event time may come from monotonic clock, while + * DIX timer facility uses real-time clock. + */ + if (!event_info->crtc) { + ErrorF("%s no crtc\n", __func__); + amdgpu_dri2_frame_event_handler(0, 0, 0, data); + return 0; + } + status = + dixLookupDrawable(&drawable, event_info->drawable_id, serverClient, + M_ANY, DixWriteAccess); + if (status != Success) { + ErrorF("%s cannot lookup drawable\n", __func__); + amdgpu_dri2_frame_event_handler(0, 0, 0, data); + return 0; + } + screen = drawable->pScreen; + scrn = xf86ScreenToScrn(screen); + info = AMDGPUPTR(scrn); + ret = drmmode_get_current_ust(info->dri2.drm_fd, &drm_now); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "%s cannot get current time\n", __func__); + amdgpu_dri2_frame_event_handler(0, 0, 0, data); + return 0; + } + tv_sec = (unsigned int)(drm_now / 1000000); + tv_usec = (unsigned int)(drm_now - (CARD64) tv_sec * 1000000); + /* + * calculate the frame number from current time + * that would come from CRTC if it were running + */ + drmmode_crtc = event_info->crtc->driver_private; + delta_t = drm_now - (CARD64) drmmode_crtc->dpms_last_ust; + delta_seq = delta_t * drmmode_crtc->dpms_last_fps; + delta_seq /= 1000000; + frame = (CARD64) drmmode_crtc->dpms_last_seq + delta_seq; + frame &= 0xffffffff; + amdgpu_dri2_frame_event_handler((unsigned int)frame, tv_sec, tv_usec, + data); + return 0; +} + +static +void amdgpu_dri2_schedule_event(CARD32 delay, pointer arg) +{ + OsTimerPtr timer; + + timer = TimerSet(NULL, 0, delay, amdgpu_dri2_deferred_event, arg); + if (delay == 0) { + CARD32 now = GetTimeInMillis(); + amdgpu_dri2_deferred_event(timer, now, arg); + } +} + +/* + * Request a DRM event when the requested conditions will be satisfied. + * + * We need to handle the event and ask the server to wake up the client when + * we receive it. + */ +static int amdgpu_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, + CARD64 target_msc, CARD64 divisor, + CARD64 remainder) +{ + ScreenPtr screen = draw->pScreen; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + AMDGPUInfoPtr info = AMDGPUPTR(scrn); + DRI2FrameEventPtr wait_info = NULL; + xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw, TRUE); + drmVBlank vbl; + int ret; + CARD64 current_msc; + + /* Truncate to match kernel interfaces; means occasional overflow + * misses, but that's generally not a big deal */ + target_msc &= 0xffffffff; + divisor &= 0xffffffff; + remainder &= 0xffffffff; + + /* Drawable not visible, return immediately */ + if (crtc == NULL) + goto out_complete; + + wait_info = calloc(1, sizeof(DRI2FrameEventRec)); + if (!wait_info) + goto out_complete; + + wait_info->drawable_id = draw->id; + wait_info->client = client; + wait_info->type = DRI2_WAITMSC; + wait_info->valid = TRUE; + wait_info->crtc = crtc; + + if (ListAddDRI2ClientEvents(client, &wait_info->link)) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "add events to client private failed.\n"); + free(wait_info); + wait_info = NULL; + goto out_complete; + } + + /* + * CRTC is in DPMS off state, calculate wait time from current time, + * target_msc and last vblank time/sequence when CRTC was turned off + */ + if (!amdgpu_crtc_is_enabled(crtc)) { + CARD32 delay; + delay = amdgpu_dri2_extrapolate_msc_delay(crtc, &target_msc, + divisor, remainder); + wait_info->frame = target_msc; + amdgpu_dri2_schedule_event(delay, wait_info); + DRI2BlockClient(client, draw); + return TRUE; + } + + /* Get current count */ + vbl.request.type = DRM_VBLANK_RELATIVE; + vbl.request.type |= amdgpu_populate_vbl_request_type(crtc); + vbl.request.sequence = 0; + ret = drmWaitVBlank(info->dri2.drm_fd, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "get vblank counter failed: %s\n", strerror(errno)); + goto out_complete; + } + + current_msc = + vbl.reply.sequence + amdgpu_get_interpolated_vblanks(crtc); + current_msc &= 0xffffffff; + + /* + * If divisor is zero, or current_msc is smaller than target_msc, + * we just need to make sure target_msc passes before waking up the + * client. + */ + if (divisor == 0 || current_msc < target_msc) { + /* If target_msc already reached or passed, set it to + * current_msc to ensure we return a reasonable value back + * to the caller. This keeps the client from continually + * sending us MSC targets from the past by forcibly updating + * their count on this call. + */ + if (current_msc >= target_msc) + target_msc = current_msc; + vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; + vbl.request.type |= amdgpu_populate_vbl_request_type(crtc); + vbl.request.sequence = target_msc; + vbl.request.sequence -= amdgpu_get_interpolated_vblanks(crtc); + vbl.request.signal = (unsigned long)wait_info; + ret = drmWaitVBlank(info->dri2.drm_fd, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "get vblank counter failed: %s\n", + strerror(errno)); + goto out_complete; + } + + wait_info->frame = vbl.reply.sequence; + wait_info->frame += amdgpu_get_interpolated_vblanks(crtc); + DRI2BlockClient(client, draw); + return TRUE; + } + + /* + * If we get here, target_msc has already passed or we don't have one, + * so we queue an event that will satisfy the divisor/remainder equation. + */ + vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; + vbl.request.type |= amdgpu_populate_vbl_request_type(crtc); + + vbl.request.sequence = current_msc - (current_msc % divisor) + + remainder; + + /* + * If calculated remainder is larger than requested remainder, + * it means we've passed the last point where + * seq % divisor == remainder, so we need to wait for the next time + * that will happen. + */ + if ((current_msc % divisor) >= remainder) + vbl.request.sequence += divisor; + vbl.request.sequence -= amdgpu_get_interpolated_vblanks(crtc); + + vbl.request.signal = (unsigned long)wait_info; + ret = drmWaitVBlank(info->dri2.drm_fd, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "get vblank counter failed: %s\n", strerror(errno)); + goto out_complete; + } + + wait_info->frame = vbl.reply.sequence; + wait_info->frame += amdgpu_get_interpolated_vblanks(crtc); + DRI2BlockClient(client, draw); + + return TRUE; + +out_complete: + if (wait_info) { + ListDelDRI2ClientEvents(wait_info->client, &wait_info->link); + free(wait_info); + } + DRI2WaitMSCComplete(client, draw, target_msc, 0, 0); + return TRUE; +} + +void amdgpu_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + DRI2FrameEventPtr flip = event_data; + DrawablePtr drawable; + ScreenPtr screen; + ScrnInfoPtr scrn; + int status; + PixmapPtr pixmap; + + status = dixLookupDrawable(&drawable, flip->drawable_id, serverClient, + M_ANY, DixWriteAccess); + if (status != Success) { + free(flip); + return; + } + if (!flip->crtc) { + free(flip); + return; + } + frame += amdgpu_get_interpolated_vblanks(flip->crtc); + + screen = drawable->pScreen; + scrn = xf86ScreenToScrn(screen); + + pixmap = screen->GetScreenPixmap(screen); + xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "%s:%d fevent[%p] width %d pitch %d (/4 %d)\n", + __func__, __LINE__, flip, pixmap->drawable.width, + pixmap->devKind, pixmap->devKind / 4); + + /* We assume our flips arrive in order, so we don't check the frame */ + switch (flip->type) { + case DRI2_SWAP: + /* Check for too small vblank count of pageflip completion, taking wraparound + * into account. This usually means some defective kms pageflip completion, + * causing wrong (msc, ust) return values and possible visual corruption. + */ + if ((frame < flip->frame) && (flip->frame - frame < 5)) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "%s: Pageflip completion event has impossible msc %d < target_msc %d\n", + __func__, frame, flip->frame); + /* All-Zero values signal failure of (msc, ust) timestamping to client. */ + frame = tv_sec = tv_usec = 0; + } + + DRI2SwapComplete(flip->client, drawable, frame, tv_sec, tv_usec, + DRI2_FLIP_COMPLETE, flip->event_complete, + flip->event_data); + break; + default: + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "%s: unknown vblank event received\n", __func__); + /* Unknown type */ + break; + } + + free(flip); +} + +/* + * ScheduleSwap is responsible for requesting a DRM vblank event for the + * appropriate frame. + * + * In the case of a blit (e.g. for a windowed swap) or buffer exchange, + * the vblank requested can simply be the last queued swap frame + the swap + * interval for the drawable. + * + * In the case of a page flip, we request an event for the last queued swap + * frame + swap interval - 1, since we'll need to queue the flip for the frame + * immediately following the received event. + * + * The client will be blocked if it tries to perform further GL commands + * after queueing a swap, though in the Intel case after queueing a flip, the + * client is free to queue more commands; they'll block in the kernel if + * they access buffers busy with the flip. + * + * When the swap is complete, the driver should call into the server so it + * can send any swap complete events that have been requested. + */ +static int amdgpu_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, + DRI2BufferPtr front, DRI2BufferPtr back, + CARD64 * target_msc, CARD64 divisor, + CARD64 remainder, DRI2SwapEventPtr func, + void *data) +{ + ScreenPtr screen = draw->pScreen; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + AMDGPUInfoPtr info = AMDGPUPTR(scrn); + xf86CrtcPtr crtc = amdgpu_dri2_drawable_crtc(draw, TRUE); + drmVBlank vbl; + int ret, flip = 0; + DRI2FrameEventPtr swap_info = NULL; + enum DRI2FrameEventType swap_type = DRI2_SWAP; + CARD64 current_msc; + BoxRec box; + RegionRec region; + + /* Truncate to match kernel interfaces; means occasional overflow + * misses, but that's generally not a big deal */ + *target_msc &= 0xffffffff; + divisor &= 0xffffffff; + remainder &= 0xffffffff; + + /* amdgpu_dri2_frame_event_handler will get called some unknown time in the + * future with these buffers. Take a reference to ensure that they won't + * get destroyed before then. + */ + amdgpu_dri2_ref_buffer(front); + amdgpu_dri2_ref_buffer(back); + + /* either off-screen or CRTC not usable... just complete the swap */ + if (crtc == NULL) + goto blit_fallback; + + swap_info = calloc(1, sizeof(DRI2FrameEventRec)); + if (!swap_info) + goto blit_fallback; + + swap_info->drawable_id = draw->id; + swap_info->client = client; + swap_info->event_complete = func; + swap_info->event_data = data; + swap_info->front = front; + swap_info->back = back; + swap_info->valid = TRUE; + swap_info->crtc = crtc; + if (ListAddDRI2ClientEvents(client, &swap_info->link)) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "add events to client private failed.\n"); + free(swap_info); + swap_info = NULL; + goto blit_fallback; + } + + /* + * CRTC is in DPMS off state, fallback to blit, but calculate + * wait time from current time, target_msc and last vblank + * time/sequence when CRTC was turned off + */ + if (!amdgpu_crtc_is_enabled(crtc)) { + CARD32 delay; + delay = amdgpu_dri2_extrapolate_msc_delay(crtc, target_msc, + divisor, remainder); + swap_info->frame = *target_msc; + amdgpu_dri2_schedule_event(delay, swap_info); + return TRUE; + } + + /* Get current count */ + vbl.request.type = DRM_VBLANK_RELATIVE; + vbl.request.type |= amdgpu_populate_vbl_request_type(crtc); + vbl.request.sequence = 0; + ret = drmWaitVBlank(info->dri2.drm_fd, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "first get vblank counter failed: %s\n", + strerror(errno)); + *target_msc = 0; + amdgpu_dri2_schedule_event(FALLBACK_SWAP_DELAY, swap_info); + return TRUE; + } + + current_msc = + vbl.reply.sequence + amdgpu_get_interpolated_vblanks(crtc); + current_msc &= 0xffffffff; + + /* Flips need to be submitted one frame before */ + if (can_flip(scrn, draw, front, back)) { + swap_type = DRI2_FLIP; + flip = 1; + } + + swap_info->type = swap_type; + + /* Correct target_msc by 'flip' if swap_type == DRI2_FLIP. + * Do it early, so handling of different timing constraints + * for divisor, remainder and msc vs. target_msc works. + */ + if (*target_msc > 0) + *target_msc -= flip; + + /* + * If divisor is zero, or current_msc is smaller than target_msc + * we just need to make sure target_msc passes before initiating + * the swap. + */ + if (divisor == 0 || current_msc < *target_msc) { + vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; + /* If non-pageflipping, but blitting/exchanging, we need to use + * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later + * on. + */ + if (flip == 0) + vbl.request.type |= DRM_VBLANK_NEXTONMISS; + vbl.request.type |= amdgpu_populate_vbl_request_type(crtc); + + /* If target_msc already reached or passed, set it to + * current_msc to ensure we return a reasonable value back + * to the caller. This makes swap_interval logic more robust. + */ + if (current_msc >= *target_msc) + *target_msc = current_msc; + + vbl.request.sequence = *target_msc; + vbl.request.sequence -= amdgpu_get_interpolated_vblanks(crtc); + vbl.request.signal = (unsigned long)swap_info; + ret = drmWaitVBlank(info->dri2.drm_fd, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "divisor 0 get vblank counter failed: %s\n", + strerror(errno)); + *target_msc = 0; + amdgpu_dri2_schedule_event(FALLBACK_SWAP_DELAY, + swap_info); + return TRUE; + } + + *target_msc = vbl.reply.sequence + flip; + *target_msc += amdgpu_get_interpolated_vblanks(crtc); + swap_info->frame = *target_msc; + + return TRUE; + } + + /* + * If we get here, target_msc has already passed or we don't have one, + * and we need to queue an event that will satisfy the divisor/remainder + * equation. + */ + vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; + if (flip == 0) + vbl.request.type |= DRM_VBLANK_NEXTONMISS; + vbl.request.type |= amdgpu_populate_vbl_request_type(crtc); + + vbl.request.sequence = current_msc - (current_msc % divisor) + + remainder; + + /* + * If the calculated deadline vbl.request.sequence is smaller than + * or equal to current_msc, it means we've passed the last 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 <= current_msc) + vbl.request.sequence += divisor; + vbl.request.sequence -= amdgpu_get_interpolated_vblanks(crtc); + + /* Account for 1 frame extra pageflip delay if flip > 0 */ + vbl.request.sequence -= flip; + + vbl.request.signal = (unsigned long)swap_info; + ret = drmWaitVBlank(info->dri2.drm_fd, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "final get vblank counter failed: %s\n", + strerror(errno)); + *target_msc = 0; + amdgpu_dri2_schedule_event(FALLBACK_SWAP_DELAY, swap_info); + return TRUE; + } + + /* Adjust returned value for 1 fame pageflip offset of flip > 0 */ + *target_msc = vbl.reply.sequence + flip; + *target_msc += amdgpu_get_interpolated_vblanks(crtc); + swap_info->frame = *target_msc; + + return TRUE; + +blit_fallback: + box.x1 = 0; + box.y1 = 0; + box.x2 = draw->width; + box.y2 = draw->height; + REGION_INIT(pScreen, ®ion, &box, 0); + + amdgpu_dri2_copy_region(draw, ®ion, front, back); + + DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data); + if (swap_info) { + ListDelDRI2ClientEvents(swap_info->client, &swap_info->link); + free(swap_info); + } + + amdgpu_dri2_unref_buffer(front); + amdgpu_dri2_unref_buffer(back); + + *target_msc = 0; /* offscreen, so zero out target vblank count */ + return TRUE; +} + +#endif /* USE_DRI2_SCHEDULING */ + +Bool amdgpu_dri2_screen_init(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + DRI2InfoRec dri2_info = { 0 }; +#ifdef USE_DRI2_SCHEDULING + const char *driverNames[2]; + Bool scheduling_works = TRUE; +#endif + + if (!info->dri2.available) + return FALSE; + + info->dri2.device_name = drmGetDeviceNameFromFd(info->dri2.drm_fd); + + dri2_info.driverName = SI_DRIVER_NAME; + dri2_info.fd = info->dri2.drm_fd; + dri2_info.deviceName = info->dri2.device_name; + dri2_info.version = DRI2INFOREC_VERSION; + dri2_info.CreateBuffer = amdgpu_dri2_create_buffer; + dri2_info.DestroyBuffer = amdgpu_dri2_destroy_buffer; + dri2_info.CopyRegion = amdgpu_dri2_copy_region; + +#ifdef USE_DRI2_SCHEDULING + if (info->drmmode.mode_res->count_crtcs > 2) { +#ifdef DRM_CAP_VBLANK_HIGH_CRTC + uint64_t cap_value; + + if (drmGetCap + (info->dri2.drm_fd, DRM_CAP_VBLANK_HIGH_CRTC, &cap_value)) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "You need a newer kernel " + "for VBLANKs on CRTC > 1\n"); + scheduling_works = FALSE; + } else if (!cap_value) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "Your kernel does not " + "handle VBLANKs on CRTC > 1\n"); + scheduling_works = FALSE; + } +#else + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "You need to rebuild against a " + "newer libdrm to handle VBLANKs on CRTC > 1\n"); + scheduling_works = FALSE; +#endif + } + + if (scheduling_works) { + dri2_info.version = 4; + dri2_info.ScheduleSwap = amdgpu_dri2_schedule_swap; + dri2_info.GetMSC = amdgpu_dri2_get_msc; + dri2_info.ScheduleWaitMSC = amdgpu_dri2_schedule_wait_msc; + dri2_info.numDrivers = AMDGPU_ARRAY_SIZE(driverNames); + dri2_info.driverNames = driverNames; + driverNames[0] = driverNames[1] = dri2_info.driverName; + + if (DRI2InfoCnt == 0) { +#if HAS_DIXREGISTERPRIVATEKEY + if (!dixRegisterPrivateKey(DRI2ClientEventsPrivateKey, + PRIVATE_CLIENT, + sizeof(DRI2ClientEventsRec))) + { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "DRI2 registering " + "private key to client failed\n"); + return FALSE; + } +#else + if (!dixRequestPrivate(DRI2ClientEventsPrivateKey, + sizeof(DRI2ClientEventsRec))) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "DRI2 requesting " + "private key to client failed\n"); + return FALSE; + } +#endif + + AddCallback(&ClientStateCallback, + amdgpu_dri2_client_state_changed, 0); + } + + DRI2InfoCnt++; + } +#endif + +#if DRI2INFOREC_VERSION >= 9 + dri2_info.version = 9; + dri2_info.CreateBuffer2 = amdgpu_dri2_create_buffer2; + dri2_info.DestroyBuffer2 = amdgpu_dri2_destroy_buffer2; + dri2_info.CopyRegion2 = amdgpu_dri2_copy_region2; +#endif + + info->dri2.enabled = DRI2ScreenInit(pScreen, &dri2_info); + return info->dri2.enabled; +} + +void amdgpu_dri2_close_screen(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + +#ifdef USE_DRI2_SCHEDULING + if (--DRI2InfoCnt == 0) + DeleteCallback(&ClientStateCallback, + amdgpu_dri2_client_state_changed, 0); +#endif + + DRI2CloseScreen(pScreen); + drmFree(info->dri2.device_name); +} + +#endif /* DRI2 */ diff --git a/src/amdgpu_dri2.h b/src/amdgpu_dri2.h new file mode 100644 index 0000000..3ca9dd2 --- /dev/null +++ b/src/amdgpu_dri2.h @@ -0,0 +1,96 @@ +/* + * Copyright 2008 Jerome Glisse + * + * All Rights Reserved. + * + * 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, sublicense, 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 ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS 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. + */ +#ifndef AMDGPU_DRI2_H +#define AMDGPU_DRI2_H + +#include <xorg-server.h> + +struct amdgpu_dri2 { + drmVersionPtr pKernelDRMVersion; + int drm_fd; + Bool available; + Bool enabled; + char *device_name; +}; + +#ifdef DRI2 + +#include "dri2.h" +Bool amdgpu_dri2_screen_init(ScreenPtr pScreen); +void amdgpu_dri2_close_screen(ScreenPtr pScreen); + +int drmmode_get_crtc_id(xf86CrtcPtr crtc); +void amdgpu_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data); +void amdgpu_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data); + +#else + +static inline Bool amdgpu_dri2_screen_init(ScreenPtr pScreen) +{ + return FALSE; +} + +static inline void amdgpu_dri2_close_screen(ScreenPtr pScreen) +{ +} + +static inline void +amdgpu_dri2_dummy_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data, + const char *name) +{ + static Bool warned; + + if (!warned) { + ErrorF("%s called but DRI2 disabled at build time\n", name); + warned = TRUE; + } + + free(event_data); +} + +static inline void +amdgpu_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + amdgpu_dri2_dummy_event_handler(frame, tv_sec, tv_usec, event_data, + __func__); +} + +static inline void +amdgpu_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + amdgpu_dri2_dummy_event_handler(frame, tv_sec, tv_usec, event_data, + __func__); +} + +#endif + +#endif /* AMDGPU_DRI2_H */ diff --git a/src/amdgpu_drv.h b/src/amdgpu_drv.h new file mode 100644 index 0000000..bb9ba14 --- /dev/null +++ b/src/amdgpu_drv.h @@ -0,0 +1,244 @@ +/* + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + * VA Linux Systems Inc., Fremont, California. + * + * All Rights Reserved. + * + * 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, sublicense, 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 ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS 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. + */ + +/* + * Authors: + * Kevin E. Martin <martin@xfree86.org> + * Rickard E. Faith <faith@valinux.com> + * Alan Hourihane <alanh@fairlite.demon.co.uk> + * + */ + +#ifndef _AMDGPU_DRV_H_ +#define _AMDGPU_DRV_H_ + +#include <stdlib.h> /* For abs() */ +#include <unistd.h> /* For usleep() */ +#include <sys/time.h> /* For gettimeofday() */ + +#include "config.h" + +#include "xf86str.h" +#include "compiler.h" + +/* PCI support */ +#include "xf86Pci.h" + +#include "fb.h" + +#include "amdgpu_glamor.h" + +/* Cursor Support */ +#include "xf86Cursor.h" + +/* DDC support */ +#include "xf86DDC.h" + +/* Xv support */ +#include "xf86xv.h" + +#include "amdgpu_probe.h" + +/* DRI support */ +#include "xf86drm.h" +#include "amdgpu_drm.h" + +#ifdef DAMAGE +#include "damage.h" +#include "globals.h" +#endif + +#include "xf86Crtc.h" +#include "X11/Xatom.h" + +#include "amdgpu_dri2.h" +#include "drmmode_display.h" +#include "amdgpu_bo_helper.h" + +/* Render support */ +#ifdef RENDER +#include "picturestr.h" +#endif + +#include "compat-api.h" + +#include "simple_list.h" +#include "amdpciids.h" + +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a)>(b)?(b):(a)) +#endif + +#if HAVE_BYTESWAP_H +#include <byteswap.h> +#elif defined(USE_SYS_ENDIAN_H) +#include <sys/endian.h> +#else +#define bswap_16(value) \ + ((((value) & 0xff) << 8) | ((value) >> 8)) + +#define bswap_32(value) \ + (((uint32_t)bswap_16((uint16_t)((value) & 0xffff)) << 16) | \ + (uint32_t)bswap_16((uint16_t)((value) >> 16))) + +#define bswap_64(value) \ + (((uint64_t)bswap_32((uint32_t)((value) & 0xffffffff)) \ + << 32) | \ + (uint64_t)bswap_32((uint32_t)((value) >> 32))) +#endif + +#if X_BYTE_ORDER == X_BIG_ENDIAN +#define le32_to_cpu(x) bswap_32(x) +#define le16_to_cpu(x) bswap_16(x) +#define cpu_to_le32(x) bswap_32(x) +#define cpu_to_le16(x) bswap_16(x) +#else +#define le32_to_cpu(x) (x) +#define le16_to_cpu(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le16(x) (x) +#endif + +/* Provide substitutes for gcc's __FUNCTION__ on other compilers */ +#if !defined(__GNUC__) && !defined(__FUNCTION__) +#define __FUNCTION__ __func__ /* C99 */ +#endif + +typedef enum { + OPTION_NOACCEL, + OPTION_SW_CURSOR, + OPTION_PAGE_FLIP, +#ifdef RENDER + OPTION_SUBPIXEL_ORDER, +#endif + OPTION_ZAPHOD_HEADS, + OPTION_ACCEL_METHOD +} AMDGPUOpts; + +#define AMDGPU_VSYNC_TIMEOUT 20000 /* Maximum wait for VSYNC (in usecs) */ + +/* Buffer are aligned on 4096 byte boundaries */ +#define AMDGPU_GPU_PAGE_SIZE 4096 +#define AMDGPU_BUFFER_ALIGN (AMDGPU_GPU_PAGE_SIZE - 1) + +#define xFixedToFloat(f) (((float) (f)) / 65536) + +#define AMDGPU_LOGLEVEL_DEBUG 4 + +/* Other macros */ +#define AMDGPU_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +#define AMDGPU_ALIGN(x,bytes) (((x) + ((bytes) - 1)) & ~((bytes) - 1)) +#define AMDGPUPTR(pScrn) ((AMDGPUInfoPtr)(pScrn)->driverPrivate) + +#define CURSOR_WIDTH 64 +#define CURSOR_HEIGHT 64 + +#define CURSOR_WIDTH_CIK 128 +#define CURSOR_HEIGHT_CIK 128 + +#define AMDGPU_BO_FLAGS_GBM 0x1 + +struct amdgpu_buffer { + union { + struct gbm_bo *gbm; + amdgpu_bo_handle amdgpu; + } bo; + void *cpu_ptr; + uint32_t ref_count; + uint32_t flags; +}; + +typedef struct { + EntityInfoPtr pEnt; + pciVideoPtr PciInfo; + int Chipset; + AMDGPUChipFamily ChipFamily; + struct gbm_device *gbm; + + Bool(*CloseScreen) (CLOSE_SCREEN_ARGS_DECL); + + void (*BlockHandler) (BLOCKHANDLER_ARGS_DECL); + + int pix24bpp; /* Depth of pixmap for 24bpp fb */ + Bool dac6bits; /* Use 6 bit DAC? */ + + int pixel_bytes; + + Bool directRenderingEnabled; + struct amdgpu_dri2 dri2; + + /* accel */ + Bool use_glamor; + + /* general */ + OptionInfoPtr Options; + + DisplayModePtr currentMode; + + CreateScreenResourcesProcPtr CreateScreenResources; + + Bool IsSecondary; + Bool IsPrimary; + + Bool shadow_fb; + void *fb_shadow; + struct amdgpu_buffer *front_buffer; + struct amdgpu_buffer *cursor_buffer[32]; + + uint64_t vram_size; + uint64_t gart_size; + drmmode_rec drmmode; + Bool drmmode_inited; + /* r6xx+ tile config */ + Bool have_tiling_info; + int group_bytes; + + /* kms pageflipping */ + Bool allowPageFlip; + + /* cursor size */ + int cursor_w; + int cursor_h; +} AMDGPUInfoRec, *AMDGPUInfoPtr; + + +/* amdgpu_video.c */ +extern void AMDGPUInitVideo(ScreenPtr pScreen); +extern void AMDGPUResetVideo(ScrnInfoPtr pScrn); +extern xf86CrtcPtr amdgpu_pick_best_crtc(ScrnInfoPtr pScrn, + Bool consider_disabled, + int x1, int x2, int y1, int y2); + +extern AMDGPUEntPtr AMDGPUEntPriv(ScrnInfoPtr pScrn); + +drmVBlankSeqType amdgpu_populate_vbl_request_type(xf86CrtcPtr crtc); + +#endif /* _AMDGPU_DRV_H_ */ diff --git a/src/amdgpu_glamor.c b/src/amdgpu_glamor.c new file mode 100644 index 0000000..d0e2c44 --- /dev/null +++ b/src/amdgpu_glamor.c @@ -0,0 +1,349 @@ +/* + * Copyright © 2011 Intel Corporation. + * 2012 Advanced Micro Devices, Inc. + * + * 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 the rights to use, copy, + * modify, merge, publish, distribute, sublicense, 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 + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <xf86.h> +#define GLAMOR_FOR_XORG 1 +#include <glamor.h> + +#include "amdgpu_bo_helper.h" +#include "amdgpu_pixmap.h" + +#include <gbm.h> + +#if HAS_DEVPRIVATEKEYREC +DevPrivateKeyRec amdgpu_pixmap_index; +#else +int amdgpu_pixmap_index; +#endif + +void amdgpu_glamor_exchange_buffers(PixmapPtr src, PixmapPtr dst) +{ + AMDGPUInfoPtr info = AMDGPUPTR(xf86ScreenToScrn(dst->drawable.pScreen)); + + if (!info->use_glamor) + return; + glamor_egl_exchange_buffers(src, dst); +} + +Bool amdgpu_glamor_create_screen_resources(ScreenPtr screen) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + AMDGPUInfoPtr info = AMDGPUPTR(scrn); + union gbm_bo_handle bo_handle; + + if (!info->use_glamor) + return TRUE; + + if (!glamor_glyphs_init(screen)) + return FALSE; + + bo_handle = gbm_bo_get_handle(info->front_buffer->bo.gbm); + if (!glamor_egl_create_textured_screen_ext(screen, + bo_handle.u32, + scrn->displayWidth * + info->pixel_bytes, NULL)) { + return FALSE; + } + + return TRUE; +} + +Bool amdgpu_glamor_pre_init(ScrnInfoPtr scrn) +{ + AMDGPUInfoPtr info = AMDGPUPTR(scrn); + pointer glamor_module; + CARD32 version; + + if (!info->dri2.available) + return FALSE; + + if (scrn->depth < 24) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "glamor requires depth >= 24, disabling.\n"); + return FALSE; + } +#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,15,0,0,0) + if (!xf86LoaderCheckSymbol("glamor_egl_init")) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "glamor requires Load \"glamoregl\" in " + "Section \"Module\", disabling.\n"); + return FALSE; + } +#endif + + /* Load glamor module */ + if ((glamor_module = xf86LoadSubModule(scrn, GLAMOR_EGL_MODULE_NAME))) { + version = xf86GetModuleVersion(glamor_module); + if (version < MODULE_VERSION_NUMERIC(0, 3, 1)) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Incompatible glamor version, required >= 0.3.0.\n"); + return FALSE; + } else { + if (glamor_egl_init(scrn, info->dri2.drm_fd)) { + xf86DrvMsg(scrn->scrnIndex, X_INFO, + "glamor detected, initialising EGL layer.\n"); + } else { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "glamor detected, failed to initialize EGL.\n"); + return FALSE; + } + } + } else { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, "glamor not available\n"); + return FALSE; + } + + info->use_glamor = TRUE; + + return TRUE; +} + +Bool amdgpu_glamor_create_textured_pixmap(PixmapPtr pixmap) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(pixmap->drawable.pScreen); + AMDGPUInfoPtr info = AMDGPUPTR(scrn); + struct amdgpu_pixmap *priv; + union gbm_bo_handle bo_handle; + + if ((info->use_glamor) == 0) + return TRUE; + + priv = amdgpu_get_pixmap_private(pixmap); + if (!priv->stride) { + priv->stride = pixmap->devKind; + } + + bo_handle = gbm_bo_get_handle(priv->bo->bo.gbm); + if (glamor_egl_create_textured_pixmap(pixmap, bo_handle.u32, + priv->stride)) { + return TRUE; + } else { + return FALSE; + } +} + +Bool amdgpu_glamor_pixmap_is_offscreen(PixmapPtr pixmap) +{ + struct amdgpu_pixmap *priv = amdgpu_get_pixmap_private(pixmap); + return priv && priv->bo; +} + +#ifndef CREATE_PIXMAP_USAGE_SHARED +#define CREATE_PIXMAP_USAGE_SHARED AMDGPU_CREATE_PIXMAP_DRI2 +#endif + +#define AMDGPU_CREATE_PIXMAP_SHARED(usage) \ + ((usage) & AMDGPU_CREATE_PIXMAP_DRI2 || (usage) == CREATE_PIXMAP_USAGE_SHARED) + +static PixmapPtr +amdgpu_glamor_create_pixmap(ScreenPtr screen, int w, int h, int depth, + unsigned usage) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct amdgpu_pixmap *priv; + PixmapPtr pixmap, new_pixmap = NULL; + + if (!AMDGPU_CREATE_PIXMAP_SHARED(usage)) { + pixmap = glamor_create_pixmap(screen, w, h, depth, usage); + if (pixmap) + return pixmap; + } + + if (w > 32767 || h > 32767) + return NullPixmap; + + if (depth == 1) + return fbCreatePixmap(screen, w, h, depth, usage); + + if (usage == CREATE_PIXMAP_USAGE_GLYPH_PICTURE && w <= 32 && h <= 32) + return fbCreatePixmap(screen, w, h, depth, usage); + + pixmap = fbCreatePixmap(screen, 0, 0, depth, usage); + if (pixmap == NullPixmap) + return pixmap; + + if (w && h) { + priv = calloc(1, sizeof(struct amdgpu_pixmap)); + if (priv == NULL) + goto fallback_pixmap; + + priv->bo = amdgpu_alloc_pixmap_bo(scrn, w, h, depth, usage, + pixmap->drawable.bitsPerPixel, + &priv->stride); + if (!priv->bo) + goto fallback_priv; + + amdgpu_set_pixmap_private(pixmap, priv); + + screen->ModifyPixmapHeader(pixmap, w, h, 0, 0, priv->stride, + NULL); + + if (!amdgpu_glamor_create_textured_pixmap(pixmap)) + goto fallback_glamor; + } + + return pixmap; + +fallback_glamor: + if (AMDGPU_CREATE_PIXMAP_SHARED(usage)) { + /* XXX need further work to handle the DRI2 failure case. + * Glamor don't know how to handle a BO only pixmap. Put + * a warning indicator here. + */ + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "Failed to create textured DRI2/PRIME pixmap."); + return pixmap; + } + /* Create textured pixmap failed means glamor failed to + * create a texture from current BO for some reasons. We turn + * to create a new glamor pixmap and clean up current one. + * One thing need to be noted, this new pixmap doesn't + * has a priv and bo attached to it. It's glamor's responsbility + * to take care of it. Glamor will mark this new pixmap as a + * texture only pixmap and will never fallback to DDX layer + * afterwards. + */ + new_pixmap = glamor_create_pixmap(screen, w, h, depth, usage); + amdgpu_bo_unref(&priv->bo); +fallback_priv: + free(priv); +fallback_pixmap: + fbDestroyPixmap(pixmap); + if (new_pixmap) + return new_pixmap; + else + return fbCreatePixmap(screen, w, h, depth, usage); +} + +static Bool amdgpu_glamor_destroy_pixmap(PixmapPtr pixmap) +{ + if (pixmap->refcnt == 1) { + glamor_egl_destroy_textured_pixmap(pixmap); + amdgpu_set_pixmap_bo(pixmap, NULL); + } + fbDestroyPixmap(pixmap); + return TRUE; +} + +#ifdef AMDGPU_PIXMAP_SHARING + +static Bool +amdgpu_glamor_share_pixmap_backing(PixmapPtr pixmap, ScreenPtr slave, + void **handle_p) +{ + struct amdgpu_pixmap *priv = amdgpu_get_pixmap_private(pixmap); + + if (!priv) + return FALSE; + + return amdgpu_share_pixmap_backing(priv->bo, handle_p); +} + +static Bool +amdgpu_glamor_set_shared_pixmap_backing(PixmapPtr pixmap, void *handle) +{ + ScreenPtr screen = pixmap->drawable.pScreen; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct amdgpu_pixmap *priv; + + if (!amdgpu_set_shared_pixmap_backing(pixmap, handle)) + return FALSE; + + priv = amdgpu_get_pixmap_private(pixmap); + priv->stride = pixmap->devKind; + + if (!amdgpu_glamor_create_textured_pixmap(pixmap)) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Failed to get PRIME drawable for glamor pixmap.\n"); + return FALSE; + } + + screen->ModifyPixmapHeader(pixmap, + pixmap->drawable.width, + pixmap->drawable.height, + 0, 0, priv->stride, NULL); + + return TRUE; +} + +#endif /* AMDGPU_PIXMAP_SHARING */ + +Bool amdgpu_glamor_init(ScreenPtr screen) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + + if (!glamor_init + (screen, + GLAMOR_INVERTED_Y_AXIS | GLAMOR_USE_EGL_SCREEN | GLAMOR_USE_SCREEN +#ifdef GLAMOR_NO_DRI3 + | GLAMOR_NO_DRI3 +#endif + | GLAMOR_USE_PICTURE_SCREEN)) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Failed to initialize glamor.\n"); + return FALSE; + } + + if (!glamor_egl_init_textured_pixmap(screen)) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Failed to initialize textured pixmap of screen for glamor.\n"); + return FALSE; + } +#if HAS_DIXREGISTERPRIVATEKEY + if (!dixRegisterPrivateKey(&amdgpu_pixmap_index, PRIVATE_PIXMAP, 0)) +#else + if (!dixRequestPrivate(&amdgpu_pixmap_index, 0)) +#endif + return FALSE; + + screen->CreatePixmap = amdgpu_glamor_create_pixmap; + screen->DestroyPixmap = amdgpu_glamor_destroy_pixmap; +#ifdef AMDGPU_PIXMAP_SHARING + screen->SharePixmapBacking = amdgpu_glamor_share_pixmap_backing; + screen->SetSharedPixmapBacking = + amdgpu_glamor_set_shared_pixmap_backing; +#endif + + xf86DrvMsg(scrn->scrnIndex, X_INFO, "Use GLAMOR acceleration.\n"); + return TRUE; +} + +void amdgpu_glamor_flush(ScrnInfoPtr pScrn) +{ + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + + if (info->use_glamor) + glamor_block_handler(pScrn->pScreen); +} + +XF86VideoAdaptorPtr amdgpu_glamor_xv_init(ScreenPtr pScreen, int num_adapt) +{ + return glamor_xv_init(pScreen, num_adapt); +} diff --git a/src/amdgpu_glamor.h b/src/amdgpu_glamor.h new file mode 100644 index 0000000..bc4c628 --- /dev/null +++ b/src/amdgpu_glamor.h @@ -0,0 +1,46 @@ +/* + * Copyright © 2011 Intel Corporation. + * 2012 Advanced Micro Devices, Inc. + * + * 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 the rights to use, copy, + * modify, merge, publish, distribute, sublicense, 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 + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS 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. + */ + +#ifndef AMDGPU_GLAMOR_H +#define AMDGPU_GLAMOR_H + +#include "xf86xv.h" + +Bool amdgpu_glamor_pre_init(ScrnInfoPtr scrn); +Bool amdgpu_glamor_init(ScreenPtr screen); +Bool amdgpu_glamor_create_screen_resources(ScreenPtr screen); +void amdgpu_glamor_free_screen(int scrnIndex, int flags); + +void amdgpu_glamor_flush(ScrnInfoPtr pScrn); + +Bool amdgpu_glamor_create_textured_pixmap(PixmapPtr pixmap); +void amdgpu_glamor_exchange_buffers(PixmapPtr src, PixmapPtr dst); + +Bool amdgpu_glamor_pixmap_is_offscreen(PixmapPtr pixmap); + +XF86VideoAdaptorPtr amdgpu_glamor_xv_init(ScreenPtr pScreen, int num_adapt); + +#endif /* AMDGPU_GLAMOR_H */ diff --git a/src/amdgpu_kms.c b/src/amdgpu_kms.c new file mode 100644 index 0000000..4612000 --- /dev/null +++ b/src/amdgpu_kms.c @@ -0,0 +1,1100 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * 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 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Authors: + * Dave Airlie <airlied@redhat.com> + * + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <sys/ioctl.h> +/* Driver data structures */ +#include "amdgpu_drv.h" +#include "amdgpu_probe.h" +#include "micmap.h" + +#include "amdgpu_version.h" +#include "shadow.h" + +#include "amdpciids.h" + +/* DPMS */ +#ifdef HAVE_XEXTPROTO_71 +#include <X11/extensions/dpmsconst.h> +#else +#define DPMS_SERVER +#include <X11/extensions/dpms.h> +#endif + +#include "amdgpu_chipinfo_gen.h" +#include "amdgpu_bo_helper.h" +#include "amdgpu_pixmap.h" + +#include <gbm.h> + +extern SymTabRec AMDGPUChipsets[]; +static Bool amdgpu_setup_kernel_mem(ScreenPtr pScreen); + +const OptionInfoRec AMDGPUOptions_KMS[] = { + {OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN, {0}, FALSE}, + {OPTION_SW_CURSOR, "SWcursor", OPTV_BOOLEAN, {0}, FALSE}, + {OPTION_PAGE_FLIP, "EnablePageFlip", OPTV_BOOLEAN, {0}, FALSE}, + {OPTION_SUBPIXEL_ORDER, "SubPixelOrder", OPTV_ANYSTR, {0}, FALSE}, + {OPTION_ZAPHOD_HEADS, "ZaphodHeads", OPTV_STRING, {0}, FALSE}, + {OPTION_ACCEL_METHOD, "AccelMethod", OPTV_STRING, {0}, FALSE}, + {-1, NULL, OPTV_NONE, {0}, FALSE} +}; + +const OptionInfoRec *AMDGPUOptionsWeak(void) +{ + return AMDGPUOptions_KMS; +} + +extern _X_EXPORT int gAMDGPUEntityIndex; + +static int getAMDGPUEntityIndex(void) +{ + return gAMDGPUEntityIndex; +} + +AMDGPUEntPtr AMDGPUEntPriv(ScrnInfoPtr pScrn) +{ + DevUnion *pPriv; + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + pPriv = xf86GetEntityPrivate(info->pEnt->index, getAMDGPUEntityIndex()); + return pPriv->ptr; +} + +/* Allocate our private AMDGPUInfoRec */ +static Bool AMDGPUGetRec(ScrnInfoPtr pScrn) +{ + if (pScrn->driverPrivate) + return TRUE; + + pScrn->driverPrivate = xnfcalloc(sizeof(AMDGPUInfoRec), 1); + return TRUE; +} + +/* Free our private AMDGPUInfoRec */ +static void AMDGPUFreeRec(ScrnInfoPtr pScrn) +{ + AMDGPUInfoPtr info; + + if (!pScrn || !pScrn->driverPrivate) + return; + + info = AMDGPUPTR(pScrn); + + if (info->dri2.drm_fd > 0) { + DevUnion *pPriv; + AMDGPUEntPtr pAMDGPUEnt; + pPriv = xf86GetEntityPrivate(pScrn->entityList[0], + getAMDGPUEntityIndex()); + + pAMDGPUEnt = pPriv->ptr; + pAMDGPUEnt->fd_ref--; + if (!pAMDGPUEnt->fd_ref) { + amdgpu_device_deinitialize(pAMDGPUEnt->pDev); + drmClose(pAMDGPUEnt->fd); + pAMDGPUEnt->fd = 0; + } + } + + free(pScrn->driverPrivate); + pScrn->driverPrivate = NULL; +} + +static void *amdgpuShadowWindow(ScreenPtr screen, CARD32 row, CARD32 offset, + int mode, CARD32 * size, void *closure) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(screen); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + int stride; + + stride = (pScrn->displayWidth * pScrn->bitsPerPixel) / 8; + *size = stride; + + return ((uint8_t *) info->front_buffer->cpu_ptr + row * stride + offset); +} + +static Bool AMDGPUCreateScreenResources_KMS(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + PixmapPtr pixmap; + + pScreen->CreateScreenResources = info->CreateScreenResources; + if (!(*pScreen->CreateScreenResources) (pScreen)) + return FALSE; + pScreen->CreateScreenResources = AMDGPUCreateScreenResources_KMS; + + if (!drmmode_set_desired_modes(pScrn, &info->drmmode)) + return FALSE; + + drmmode_uevent_init(pScrn, &info->drmmode); + + if (info->shadow_fb) { + pixmap = pScreen->GetScreenPixmap(pScreen); + + if (!shadowAdd(pScreen, pixmap, shadowUpdatePackedWeak(), + amdgpuShadowWindow, 0, NULL)) + return FALSE; + } + + if (info->dri2.enabled || info->use_glamor) { + if (info->front_buffer) { + PixmapPtr pPix = pScreen->GetScreenPixmap(pScreen); + amdgpu_set_pixmap_bo(pPix, info->front_buffer); + } + } + + if (info->use_glamor) + amdgpu_glamor_create_screen_resources(pScreen); + + return TRUE; +} + +#ifdef AMDGPU_PIXMAP_SHARING +static void redisplay_dirty(ScreenPtr screen, PixmapDirtyUpdatePtr dirty) +{ + RegionRec pixregion; + + PixmapRegionInit(&pixregion, dirty->slave_dst); + DamageRegionAppend(&dirty->slave_dst->drawable, &pixregion); + PixmapSyncDirtyHelper(dirty, &pixregion); + + DamageRegionProcessPending(&dirty->slave_dst->drawable); + RegionUninit(&pixregion); +} + +static void amdgpu_dirty_update(ScreenPtr screen) +{ + RegionPtr region; + PixmapDirtyUpdatePtr ent; + + if (xorg_list_is_empty(&screen->pixmap_dirty_list)) + return; + + xorg_list_for_each_entry(ent, &screen->pixmap_dirty_list, ent) { + region = DamageRegion(ent->damage); + if (RegionNotEmpty(region)) { + redisplay_dirty(screen, ent); + DamageEmpty(ent->damage); + } + } +} +#endif + +static void AMDGPUBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL) +{ + SCREEN_PTR(arg); + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + + pScreen->BlockHandler = info->BlockHandler; + (*pScreen->BlockHandler) (BLOCKHANDLER_ARGS); + pScreen->BlockHandler = AMDGPUBlockHandler_KMS; + + if (info->use_glamor) + amdgpu_glamor_flush(pScrn); + +#ifdef AMDGPU_PIXMAP_SHARING + amdgpu_dirty_update(pScreen); +#endif +} + +static void +amdgpu_flush_callback(CallbackListPtr * list, + pointer user_data, pointer call_data) +{ + ScrnInfoPtr pScrn = user_data; + + if (pScrn->vtSema) { + amdgpu_glamor_flush(pScrn); + } +} + +static Bool AMDGPUIsAccelWorking(ScrnInfoPtr pScrn) +{ + AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); + uint32_t accel_working; + + if (amdgpu_query_info(pAMDGPUEnt->pDev, AMDGPU_INFO_ACCEL_WORKING, + sizeof(accel_working), &accel_working) != 0) + return FALSE; + + return accel_working; +} + +/* This is called by AMDGPUPreInit to set up the default visual */ +static Bool AMDGPUPreInitVisual(ScrnInfoPtr pScrn) +{ + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + + if (!xf86SetDepthBpp(pScrn, 0, 0, 0, Support32bppFb)) + return FALSE; + + switch (pScrn->depth) { + case 8: + case 15: + case 16: + case 24: + break; + + default: + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Given depth (%d) is not supported by %s driver\n", + pScrn->depth, AMDGPU_DRIVER_NAME); + return FALSE; + } + + xf86PrintDepthBpp(pScrn); + + info->pix24bpp = xf86GetBppFromDepth(pScrn, pScrn->depth); + info->pixel_bytes = pScrn->bitsPerPixel / 8; + + if (info->pix24bpp == 24) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Amdgpu does NOT support 24bpp\n"); + return FALSE; + } + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Pixel depth = %d bits stored in %d byte%s (%d bpp pixmaps)\n", + pScrn->depth, + info->pixel_bytes, + info->pixel_bytes > 1 ? "s" : "", info->pix24bpp); + + if (!xf86SetDefaultVisual(pScrn, -1)) + return FALSE; + + if (pScrn->depth > 8 && pScrn->defaultVisual != TrueColor) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Default visual (%s) is not supported at depth %d\n", + xf86GetVisualName(pScrn->defaultVisual), + pScrn->depth); + return FALSE; + } + return TRUE; +} + +/* This is called by AMDGPUPreInit to handle all color weight issues */ +static Bool AMDGPUPreInitWeight(ScrnInfoPtr pScrn) +{ + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + + /* Save flag for 6 bit DAC to use for + setting CRTC registers. Otherwise use + an 8 bit DAC, even if xf86SetWeight sets + pScrn->rgbBits to some value other than + 8. */ + info->dac6bits = FALSE; + + if (pScrn->depth > 8) { + rgb defaultWeight = { 0, 0, 0 }; + + if (!xf86SetWeight(pScrn, defaultWeight, defaultWeight)) + return FALSE; + } else { + pScrn->rgbBits = 8; + } + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Using %d bits per RGB (%d bit DAC)\n", + pScrn->rgbBits, info->dac6bits ? 6 : 8); + + return TRUE; +} + +static Bool AMDGPUPreInitAccel_KMS(ScrnInfoPtr pScrn) +{ + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + const char *accel_method; + + if (!xf86ReturnOptValBool(info->Options, OPTION_NOACCEL, + info->ChipFamily == CHIP_FAMILY_HAWAII) && + AMDGPUIsAccelWorking(pScrn)) { + Bool use_glamor = TRUE; + + accel_method = xf86GetOptValString(info->Options, OPTION_ACCEL_METHOD); + if ((accel_method && !strcmp(accel_method, "none"))) + use_glamor = FALSE; + +#ifdef DRI2 + info->dri2.available = ! !xf86LoadSubModule(pScrn, "dri2"); +#endif + + if (info->dri2.available) + info->gbm = gbm_create_device(info->dri2.drm_fd); + if (info->gbm == NULL) + info->dri2.available = FALSE; + + if (use_glamor && + amdgpu_glamor_pre_init(pScrn)) + return TRUE; + + if (info->dri2.available) + return TRUE; + } + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "GPU accel disabled or not working, using shadowfb for KMS\n"); + info->shadow_fb = TRUE; + if (!xf86LoadSubModule(pScrn, "shadow")) + info->shadow_fb = FALSE; + + return TRUE; +} + +static Bool AMDGPUPreInitChipType_KMS(ScrnInfoPtr pScrn) +{ + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + int i; + + info->Chipset = PCI_DEV_DEVICE_ID(info->PciInfo); + pScrn->chipset = + (char *)xf86TokenToString(AMDGPUChipsets, info->Chipset); + if (!pScrn->chipset) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "ChipID 0x%04x is not recognized\n", info->Chipset); + return FALSE; + } + + if (info->Chipset < 0) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Chipset \"%s\" is not recognized\n", + pScrn->chipset); + return FALSE; + } + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, + "Chipset: \"%s\" (ChipID = 0x%04x)\n", + pScrn->chipset, info->Chipset); + + for (i = 0; i < sizeof(AMDGPUCards) / sizeof(AMDGPUCardInfo); i++) { + if (info->Chipset == AMDGPUCards[i].pci_device_id) { + AMDGPUCardInfo *card = &AMDGPUCards[i]; + info->ChipFamily = card->chip_family; + break; + } + } + + return TRUE; +} + +static void amdgpu_reference_drm_fd(ScrnInfoPtr pScrn) +{ + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); + + info->dri2.drm_fd = pAMDGPUEnt->fd; + pAMDGPUEnt->fd_ref++; + info->drmmode.fd = info->dri2.drm_fd; +} + +static Bool amdgpu_get_tile_config(ScrnInfoPtr pScrn) +{ + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); + struct amdgpu_gpu_info gpu_info; + + memset(&gpu_info, 0, sizeof(gpu_info)); + amdgpu_query_gpu_info(pAMDGPUEnt->pDev, &gpu_info); + + switch ((gpu_info.gb_addr_cfg & 0x70) >> 4) { + case 0: + info->group_bytes = 256; + break; + case 1: + info->group_bytes = 512; + break; + default: + return FALSE; + } + + info->have_tiling_info = TRUE; + return TRUE; +} + +static void AMDGPUSetupCapabilities(ScrnInfoPtr pScrn) +{ +#ifdef AMDGPU_PIXMAP_SHARING + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + uint64_t value; + int ret; + + pScrn->capabilities = 0; + ret = drmGetCap(info->dri2.drm_fd, DRM_CAP_PRIME, &value); + if (ret == 0) { + if (value & DRM_PRIME_CAP_EXPORT) + pScrn->capabilities |= + RR_Capability_SourceOutput | + RR_Capability_SinkOffload; + if (value & DRM_PRIME_CAP_IMPORT) + pScrn->capabilities |= + RR_Capability_SourceOffload | + RR_Capability_SinkOutput; + } +#endif +} + +Bool AMDGPUPreInit_KMS(ScrnInfoPtr pScrn, int flags) +{ + AMDGPUInfoPtr info; + AMDGPUEntPtr pAMDGPUEnt; + DevUnion *pPriv; + Gamma zeros = { 0.0, 0.0, 0.0 }; + int cpp; + uint64_t heap_size = 0; + uint64_t max_allocation = 0; + + if (flags & PROBE_DETECT) + return TRUE; + + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "AMDGPUPreInit_KMS\n"); + if (pScrn->numEntities != 1) + return FALSE; + if (!AMDGPUGetRec(pScrn)) + return FALSE; + + info = AMDGPUPTR(pScrn); + info->IsSecondary = FALSE; + info->IsPrimary = FALSE; + info->pEnt = + xf86GetEntityInfo(pScrn->entityList[pScrn->numEntities - 1]); + if (info->pEnt->location.type != BUS_PCI +#ifdef XSERVER_PLATFORM_BUS + && info->pEnt->location.type != BUS_PLATFORM +#endif + ) + goto fail; + + pPriv = xf86GetEntityPrivate(pScrn->entityList[0], + getAMDGPUEntityIndex()); + pAMDGPUEnt = pPriv->ptr; + + if (xf86IsEntityShared(pScrn->entityList[0])) { + if (xf86IsPrimInitDone(pScrn->entityList[0])) { + info->IsSecondary = TRUE; + pAMDGPUEnt->pSecondaryScrn = pScrn; + } else { + info->IsPrimary = TRUE; + xf86SetPrimInitDone(pScrn->entityList[0]); + pAMDGPUEnt->pPrimaryScrn = pScrn; + pAMDGPUEnt->HasSecondary = FALSE; + } + } + + info->PciInfo = xf86GetPciInfoForEntity(info->pEnt->index); + pScrn->monitor = pScrn->confScreen->monitor; + + if (!AMDGPUPreInitVisual(pScrn)) + goto fail; + + xf86CollectOptions(pScrn, NULL); + if (!(info->Options = malloc(sizeof(AMDGPUOptions_KMS)))) + goto fail; + + memcpy(info->Options, AMDGPUOptions_KMS, sizeof(AMDGPUOptions_KMS)); + xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, info->Options); + + if (!AMDGPUPreInitWeight(pScrn)) + goto fail; + + if (!AMDGPUPreInitChipType_KMS(pScrn)) + goto fail; + + amdgpu_reference_drm_fd(pScrn); + + info->dri2.available = FALSE; + info->dri2.enabled = FALSE; + info->dri2.pKernelDRMVersion = drmGetVersion(info->dri2.drm_fd); + if (info->dri2.pKernelDRMVersion == NULL) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "AMDGPUDRIGetVersion failed to get the DRM version\n"); + goto fail; + } + + if (!AMDGPUPreInitAccel_KMS(pScrn)) + goto fail; + + AMDGPUSetupCapabilities(pScrn); + + /* don't enable tiling if accel is not enabled */ + if (info->use_glamor) { + /* set default group bytes, overridden by kernel info below */ + info->group_bytes = 256; + info->have_tiling_info = FALSE; + amdgpu_get_tile_config(pScrn); + } + + info->allowPageFlip = xf86ReturnOptValBool(info->Options, + OPTION_PAGE_FLIP, + TRUE); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "KMS Pageflipping: %sabled\n", + info->allowPageFlip ? "en" : "dis"); + + if (drmmode_pre_init(pScrn, &info->drmmode, pScrn->bitsPerPixel / 8) == + FALSE) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Kernel modesetting setup failed\n"); + goto fail; + } + + if (info->drmmode.mode_res->count_crtcs == 1) + pAMDGPUEnt->HasCRTC2 = FALSE; + else + pAMDGPUEnt->HasCRTC2 = TRUE; + + info->cursor_w = CURSOR_WIDTH_CIK; + info->cursor_h = CURSOR_HEIGHT_CIK; + + amdgpu_query_heap_size(pAMDGPUEnt->pDev, AMDGPU_GEM_DOMAIN_GTT, + &heap_size, &max_allocation); + info->gart_size = heap_size; + amdgpu_query_heap_size(pAMDGPUEnt->pDev, AMDGPU_GEM_DOMAIN_VRAM, + &heap_size, &max_allocation); + info->vram_size = max_allocation; + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "mem size init: gart size :%llx vram size: s:%llx visible:%llx\n", + (unsigned long long)info->gart_size, + (unsigned long long)heap_size, + (unsigned long long)max_allocation); + + cpp = pScrn->bitsPerPixel / 8; + pScrn->displayWidth = + AMDGPU_ALIGN(pScrn->virtualX, drmmode_get_pitch_align(pScrn, cpp)); + + /* Set display resolution */ + xf86SetDpi(pScrn, 0, 0); + + /* Get ScreenInit function */ + if (!xf86LoadSubModule(pScrn, "fb")) + return FALSE; + + if (!xf86SetGamma(pScrn, zeros)) + return FALSE; + + if (!xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE)) { + if (!xf86LoadSubModule(pScrn, "ramdac")) + return FALSE; + } + + if (pScrn->modes == NULL +#ifdef XSERVER_PLATFORM_BUS + && !pScrn->is_gpu +#endif + ) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No modes.\n"); + goto fail; + } + + return TRUE; +fail: + AMDGPUFreeRec(pScrn); + return FALSE; + +} + +static Bool AMDGPUCursorInit_KMS(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + + return xf86_cursors_init(pScreen, info->cursor_w, info->cursor_h, + (HARDWARE_CURSOR_TRUECOLOR_AT_8BPP | + HARDWARE_CURSOR_AND_SOURCE_WITH_MASK | + HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 | + HARDWARE_CURSOR_UPDATE_UNHIDDEN | + HARDWARE_CURSOR_ARGB)); +} + +void AMDGPUBlank(ScrnInfoPtr pScrn) +{ + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); + xf86OutputPtr output; + xf86CrtcPtr crtc; + int o, c; + + for (c = 0; c < xf86_config->num_crtc; c++) { + crtc = xf86_config->crtc[c]; + for (o = 0; o < xf86_config->num_output; o++) { + output = xf86_config->output[o]; + if (output->crtc != crtc) + continue; + + output->funcs->dpms(output, DPMSModeOff); + } + crtc->funcs->dpms(crtc, DPMSModeOff); + } +} + +void AMDGPUUnblank(ScrnInfoPtr pScrn) +{ + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); + xf86OutputPtr output; + xf86CrtcPtr crtc; + int o, c; + for (c = 0; c < xf86_config->num_crtc; c++) { + crtc = xf86_config->crtc[c]; + if (!crtc->enabled) + continue; + crtc->funcs->dpms(crtc, DPMSModeOn); + for (o = 0; o < xf86_config->num_output; o++) { + output = xf86_config->output[o]; + if (output->crtc != crtc) + continue; + output->funcs->dpms(output, DPMSModeOn); + } + } +} + +static Bool AMDGPUSaveScreen_KMS(ScreenPtr pScreen, int mode) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + Bool unblank; + + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "AMDGPUSaveScreen(%d)\n", mode); + + unblank = xf86IsUnblank(mode); + if (unblank) + SetTimeSinceLastInputEvent(); + + if ((pScrn != NULL) && pScrn->vtSema) { + if (unblank) + AMDGPUUnblank(pScrn); + else + AMDGPUBlank(pScrn); + } + return TRUE; +} + +/* Called at the end of each server generation. Restore the original + * text mode, unmap video memory, and unwrap and call the saved + * CloseScreen function. + */ +static Bool AMDGPUCloseScreen_KMS(CLOSE_SCREEN_ARGS_DECL) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "AMDGPUCloseScreen\n"); + + drmmode_uevent_fini(pScrn, &info->drmmode); + + DeleteCallback(&FlushCallback, amdgpu_flush_callback, pScrn); + + drmDropMaster(info->dri2.drm_fd); + + drmmode_fini(pScrn, &info->drmmode); + if (info->dri2.enabled) { + amdgpu_dri2_close_screen(pScreen); + } + pScrn->vtSema = FALSE; + xf86ClearPrimInitDone(info->pEnt->index); + pScreen->BlockHandler = info->BlockHandler; + pScreen->CloseScreen = info->CloseScreen; + return (*pScreen->CloseScreen) (CLOSE_SCREEN_ARGS); +} + +void AMDGPUFreeScreen_KMS(FREE_SCREEN_ARGS_DECL) +{ + SCRN_INFO_PTR(arg); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "AMDGPUFreeScreen\n"); + + /* when server quits at PreInit, we don't need do this anymore */ + if (!info) + return; + + AMDGPUFreeRec(pScrn); +} + +Bool AMDGPUScreenInit_KMS(SCREEN_INIT_ARGS_DECL) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + int subPixelOrder = SubPixelUnknown; + char *s; + void *front_ptr; + int ret; + + pScrn->fbOffset = 0; + + miClearVisualTypes(); + if (!miSetVisualTypes(pScrn->depth, + miGetDefaultVisualMask(pScrn->depth), + pScrn->rgbBits, pScrn->defaultVisual)) + return FALSE; + miSetPixmapDepths(); + + ret = drmSetMaster(info->dri2.drm_fd); + if (ret) { + ErrorF("Unable to retrieve master\n"); + return FALSE; + } + info->directRenderingEnabled = FALSE; + if (info->shadow_fb == FALSE) + info->directRenderingEnabled = amdgpu_dri2_screen_init(pScreen); + + if (!amdgpu_setup_kernel_mem(pScreen)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "amdgpu_setup_kernel_mem failed\n"); + return FALSE; + } + front_ptr = info->front_buffer->cpu_ptr; + + if (info->shadow_fb) { + info->fb_shadow = calloc(1, + pScrn->displayWidth * pScrn->virtualY * + ((pScrn->bitsPerPixel + 7) >> 3)); + if (info->fb_shadow == NULL) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to allocate shadow framebuffer\n"); + info->shadow_fb = FALSE; + } else { + if (!fbScreenInit(pScreen, info->fb_shadow, + pScrn->virtualX, pScrn->virtualY, + pScrn->xDpi, pScrn->yDpi, + pScrn->displayWidth, + pScrn->bitsPerPixel)) + return FALSE; + } + } + + if (info->shadow_fb == FALSE) { + /* Init fb layer */ + if (!fbScreenInit(pScreen, front_ptr, + pScrn->virtualX, pScrn->virtualY, + pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth, + pScrn->bitsPerPixel)) + return FALSE; + } + + xf86SetBlackWhitePixels(pScreen); + + if (pScrn->bitsPerPixel > 8) { + VisualPtr visual; + + 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; + } + } + } + + /* Must be after RGB order fixed */ + fbPictureInit(pScreen, 0, 0); + +#ifdef RENDER + if ((s = xf86GetOptValString(info->Options, OPTION_SUBPIXEL_ORDER))) { + if (strcmp(s, "RGB") == 0) + subPixelOrder = SubPixelHorizontalRGB; + else if (strcmp(s, "BGR") == 0) + subPixelOrder = SubPixelHorizontalBGR; + else if (strcmp(s, "NONE") == 0) + subPixelOrder = SubPixelNone; + PictureSetSubpixelOrder(pScreen, subPixelOrder); + } +#endif + + pScrn->vtSema = TRUE; + xf86SetBackingStore(pScreen); + + if (info->directRenderingEnabled) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Direct rendering enabled\n"); + } else { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "Direct rendering disabled\n"); + } + + if (info->use_glamor && info->directRenderingEnabled) { + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "Initializing Acceleration\n"); + if (amdgpu_glamor_init(pScreen)) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Acceleration enabled\n"); + } else { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Acceleration initialization failed\n"); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "2D and 3D acceleration disabled\n"); + info->use_glamor = FALSE; + } + } else if (info->directRenderingEnabled) { + if (!amdgpu_pixmap_init(pScreen)) + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "3D acceleration disabled\n"); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "2D acceleration disabled\n"); + } else { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "2D and 3D cceleration disabled\n"); + } + + /* Init DPMS */ + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "Initializing DPMS\n"); + xf86DPMSInit(pScreen, xf86DPMSSet, 0); + + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "Initializing Cursor\n"); + + /* Set Silken Mouse */ + xf86SetSilkenMouse(pScreen); + + /* Cursor setup */ + miDCInitialize(pScreen, xf86GetPointerScreenFuncs()); + + if (!xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE)) { + if (AMDGPUCursorInit_KMS(pScreen)) { + } + } + + /* DGA setup */ +#ifdef XFreeXDGA + /* DGA is dangerous on kms as the base and framebuffer location may change: + * http://lists.freedesktop.org/archives/xorg-devel/2009-September/002113.html + */ + /* xf86DiDGAInit(pScreen, info->LinearAddr + pScrn->fbOffset); */ +#endif + if (info->shadow_fb == FALSE) { + /* Init Xv */ + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "Initializing Xv\n"); + AMDGPUInitVideo(pScreen); + } + + if (info->shadow_fb == TRUE) { + if (!shadowSetup(pScreen)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Shadowfb initialization failed\n"); + return FALSE; + } + } + pScrn->pScreen = pScreen; + + /* Provide SaveScreen & wrap BlockHandler and CloseScreen */ + /* Wrap CloseScreen */ + info->CloseScreen = pScreen->CloseScreen; + pScreen->CloseScreen = AMDGPUCloseScreen_KMS; + pScreen->SaveScreen = AMDGPUSaveScreen_KMS; + info->BlockHandler = pScreen->BlockHandler; + pScreen->BlockHandler = AMDGPUBlockHandler_KMS; + + if (!AddCallback(&FlushCallback, amdgpu_flush_callback, pScrn)) + return FALSE; + + info->CreateScreenResources = pScreen->CreateScreenResources; + pScreen->CreateScreenResources = AMDGPUCreateScreenResources_KMS; + +#ifdef AMDGPU_PIXMAP_SHARING + pScreen->StartPixmapTracking = PixmapStartDirtyTracking; + pScreen->StopPixmapTracking = PixmapStopDirtyTracking; +#endif + + if (!xf86CrtcScreenInit(pScreen)) + return FALSE; + + /* Wrap pointer motion to flip touch screen around */ +// info->PointerMoved = pScrn->PointerMoved; +// pScrn->PointerMoved = AMDGPUPointerMoved; + + if (!drmmode_setup_colormap(pScreen, pScrn)) + return FALSE; + + /* Note unused options */ + if (serverGeneration == 1) + xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options); + + drmmode_init(pScrn, &info->drmmode); + + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "AMDGPUScreenInit finished\n"); + + return TRUE; +} + +Bool AMDGPUEnterVT_KMS(VT_FUNC_ARGS_DECL) +{ + SCRN_INFO_PTR(arg); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + int ret; + + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "AMDGPUEnterVT_KMS\n"); + + ret = drmSetMaster(info->dri2.drm_fd); + if (ret) + ErrorF("Unable to retrieve master\n"); + + pScrn->vtSema = TRUE; + + if (!drmmode_set_desired_modes(pScrn, &info->drmmode)) + return FALSE; + + return TRUE; +} + +void AMDGPULeaveVT_KMS(VT_FUNC_ARGS_DECL) +{ + SCRN_INFO_PTR(arg); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "AMDGPULeaveVT_KMS\n"); + + drmDropMaster(info->dri2.drm_fd); + + xf86RotateFreeShadow(pScrn); + + xf86_hide_cursors(pScrn); + + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "Ok, leaving now...\n"); +} + +Bool AMDGPUSwitchMode_KMS(SWITCH_MODE_ARGS_DECL) +{ + SCRN_INFO_PTR(arg); + Bool ret; + ret = xf86SetSingleMode(pScrn, mode, RR_Rotate_0); + return ret; + +} + +void AMDGPUAdjustFrame_KMS(ADJUST_FRAME_ARGS_DECL) +{ + SCRN_INFO_PTR(arg); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + drmmode_adjust_frame(pScrn, &info->drmmode, x, y); + return; +} + +static Bool amdgpu_setup_kernel_mem(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); + int cpp = info->pixel_bytes; + int cursor_size; + int c; + + cursor_size = info->cursor_w * info->cursor_h * 4; + cursor_size = AMDGPU_ALIGN(cursor_size, AMDGPU_GPU_PAGE_SIZE); + for (c = 0; c < xf86_config->num_crtc; c++) { + /* cursor objects */ + if (info->cursor_buffer[c] == NULL) { + if (info->gbm) { + info->cursor_buffer[c] = (struct amdgpu_buffer *)calloc(1, sizeof(struct amdgpu_buffer)); + if (!info->cursor_buffer[c]) { + return FALSE; + } + info->cursor_buffer[c]->ref_count = 1; + + info->cursor_buffer[c]->bo.gbm = gbm_bo_create(info->gbm, + info->cursor_w, + info->cursor_h, + GBM_FORMAT_ARGB8888, + GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE); + if (!info->cursor_buffer[c]->bo.gbm) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to allocate cursor buffer memory\n"); + free(info->cursor_buffer[c]); + return FALSE; + } + } else { + AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); + info->cursor_buffer[c] = amdgpu_bo_open(pAMDGPUEnt->pDev, + cursor_size, + 0, + AMDGPU_GEM_DOMAIN_VRAM); + if (!(info->cursor_buffer[c])) { + ErrorF("Failed to allocate cursor buffer memory\n"); + return FALSE; + } + + if (amdgpu_bo_cpu_map(info->cursor_buffer[c]->bo.amdgpu, + &info->cursor_buffer[c]->cpu_ptr)) { + ErrorF("Failed to map cursor buffer memory\n"); + } + } + + drmmode_set_cursor(pScrn, &info->drmmode, c, + info->cursor_buffer[c]); + } + } + + if (info->front_buffer == NULL) { + int pitch; + int hint = info->use_glamor ? 0 : AMDGPU_CREATE_PIXMAP_LINEAR; + + info->front_buffer = + amdgpu_alloc_pixmap_bo(pScrn, pScrn->virtualX, + pScrn->virtualY, pScrn->depth, + hint, pScrn->bitsPerPixel, + &pitch); + if (!(info->front_buffer)) { + ErrorF("Failed to allocate front buffer memory\n"); + return FALSE; + } + + if (amdgpu_bo_map(pScrn, info->front_buffer)) { + ErrorF("Failed to map front buffer memory\n"); + return FALSE; + } + + pScrn->displayWidth = pitch / cpp; + } + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Front buffer pitch: %d bytes\n", + pScrn->displayWidth * cpp); + return TRUE; +} + +/* Used to disallow modes that are not supported by the hardware */ +ModeStatus AMDGPUValidMode(SCRN_ARG_TYPE arg, DisplayModePtr mode, + Bool verbose, int flag) +{ + /* There are problems with double scan mode at high clocks + * They're likely related PLL and display buffer settings. + * Disable these modes for now. + */ + if (mode->Flags & V_DBLSCAN) { + if ((mode->CrtcHDisplay >= 1024) || (mode->CrtcVDisplay >= 768)) + return MODE_CLOCK_RANGE; + } + return MODE_OK; +} diff --git a/src/amdgpu_misc.c b/src/amdgpu_misc.c new file mode 100644 index 0000000..560a877 --- /dev/null +++ b/src/amdgpu_misc.c @@ -0,0 +1,70 @@ +/* + * Copyright 2000 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org + * + * 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, and + * that the name of Marc Aurele La France not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Marc Aurele La France makes no representations + * about the suitability of this software for any purpose. It is provided + * "as-is" without express or implied warranty. + * + * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "amdgpu_probe.h" +#include "amdgpu_version.h" + +#include "xf86.h" + +/* Module loader interface for subsidiary driver module */ + +static XF86ModuleVersionInfo AMDGPUVersionRec = { + AMDGPU_DRIVER_NAME, + MODULEVENDORSTRING, + MODINFOSTRING1, + MODINFOSTRING2, + XORG_VERSION_CURRENT, + AMDGPU_VERSION_MAJOR, AMDGPU_VERSION_MINOR, AMDGPU_VERSION_PATCH, + ABI_CLASS_VIDEODRV, + ABI_VIDEODRV_VERSION, + MOD_CLASS_VIDEODRV, + {0, 0, 0, 0} +}; + +/* + * AMDGPUSetup -- + * + * This function is called every time the module is loaded. + */ +static pointer +AMDGPUSetup(pointer Module, pointer Options, int *ErrorMajor, int *ErrorMinor) +{ + static Bool Inited = FALSE; + + if (!Inited) { + Inited = TRUE; + xf86AddDriver(&AMDGPU, Module, HaveDriverFuncs); + } + + return (pointer) TRUE; +} + +/* The following record must be called amdgpuModuleData */ +_X_EXPORT XF86ModuleData amdgpuModuleData = { + &AMDGPUVersionRec, + AMDGPUSetup, + NULL +}; diff --git a/src/amdgpu_pci_chipset_gen.h b/src/amdgpu_pci_chipset_gen.h new file mode 100644 index 0000000..257a121 --- /dev/null +++ b/src/amdgpu_pci_chipset_gen.h @@ -0,0 +1,77 @@ +/* This file is autogenerated please do not edit */ +static PciChipsets AMDGPUPciChipsets[] = { + { PCI_CHIP_BONAIRE_6640, PCI_CHIP_BONAIRE_6640, RES_SHARED_VGA }, + { PCI_CHIP_BONAIRE_6641, PCI_CHIP_BONAIRE_6641, RES_SHARED_VGA }, + { PCI_CHIP_BONAIRE_6649, PCI_CHIP_BONAIRE_6649, RES_SHARED_VGA }, + { PCI_CHIP_BONAIRE_6650, PCI_CHIP_BONAIRE_6650, RES_SHARED_VGA }, + { PCI_CHIP_BONAIRE_6651, PCI_CHIP_BONAIRE_6651, RES_SHARED_VGA }, + { PCI_CHIP_BONAIRE_6658, PCI_CHIP_BONAIRE_6658, RES_SHARED_VGA }, + { PCI_CHIP_BONAIRE_665C, PCI_CHIP_BONAIRE_665C, RES_SHARED_VGA }, + { PCI_CHIP_BONAIRE_665D, PCI_CHIP_BONAIRE_665D, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_9830, PCI_CHIP_KABINI_9830, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_9831, PCI_CHIP_KABINI_9831, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_9832, PCI_CHIP_KABINI_9832, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_9833, PCI_CHIP_KABINI_9833, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_9834, PCI_CHIP_KABINI_9834, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_9835, PCI_CHIP_KABINI_9835, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_9836, PCI_CHIP_KABINI_9836, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_9837, PCI_CHIP_KABINI_9837, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_9838, PCI_CHIP_KABINI_9838, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_9839, PCI_CHIP_KABINI_9839, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_983A, PCI_CHIP_KABINI_983A, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_983B, PCI_CHIP_KABINI_983B, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_983C, PCI_CHIP_KABINI_983C, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_983D, PCI_CHIP_KABINI_983D, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_983E, PCI_CHIP_KABINI_983E, RES_SHARED_VGA }, + { PCI_CHIP_KABINI_983F, PCI_CHIP_KABINI_983F, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_1304, PCI_CHIP_KAVERI_1304, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_1305, PCI_CHIP_KAVERI_1305, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_1306, PCI_CHIP_KAVERI_1306, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_1307, PCI_CHIP_KAVERI_1307, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_1309, PCI_CHIP_KAVERI_1309, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_130A, PCI_CHIP_KAVERI_130A, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_130B, PCI_CHIP_KAVERI_130B, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_130C, PCI_CHIP_KAVERI_130C, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_130D, PCI_CHIP_KAVERI_130D, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_130E, PCI_CHIP_KAVERI_130E, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_130F, PCI_CHIP_KAVERI_130F, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_1310, PCI_CHIP_KAVERI_1310, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_1311, PCI_CHIP_KAVERI_1311, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_1312, PCI_CHIP_KAVERI_1312, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_1313, PCI_CHIP_KAVERI_1313, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_1315, PCI_CHIP_KAVERI_1315, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_1316, PCI_CHIP_KAVERI_1316, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_1317, PCI_CHIP_KAVERI_1317, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_131B, PCI_CHIP_KAVERI_131B, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_131C, PCI_CHIP_KAVERI_131C, RES_SHARED_VGA }, + { PCI_CHIP_KAVERI_131D, PCI_CHIP_KAVERI_131D, RES_SHARED_VGA }, + { PCI_CHIP_HAWAII_67A0, PCI_CHIP_HAWAII_67A0, RES_SHARED_VGA }, + { PCI_CHIP_HAWAII_67A1, PCI_CHIP_HAWAII_67A1, RES_SHARED_VGA }, + { PCI_CHIP_HAWAII_67A2, PCI_CHIP_HAWAII_67A2, RES_SHARED_VGA }, + { PCI_CHIP_HAWAII_67A8, PCI_CHIP_HAWAII_67A8, RES_SHARED_VGA }, + { PCI_CHIP_HAWAII_67A9, PCI_CHIP_HAWAII_67A9, RES_SHARED_VGA }, + { PCI_CHIP_HAWAII_67AA, PCI_CHIP_HAWAII_67AA, RES_SHARED_VGA }, + { PCI_CHIP_HAWAII_67B0, PCI_CHIP_HAWAII_67B0, RES_SHARED_VGA }, + { PCI_CHIP_HAWAII_67B1, PCI_CHIP_HAWAII_67B1, RES_SHARED_VGA }, + { PCI_CHIP_HAWAII_67B8, PCI_CHIP_HAWAII_67B8, RES_SHARED_VGA }, + { PCI_CHIP_HAWAII_67B9, PCI_CHIP_HAWAII_67B9, RES_SHARED_VGA }, + { PCI_CHIP_HAWAII_67BA, PCI_CHIP_HAWAII_67BA, RES_SHARED_VGA }, + { PCI_CHIP_HAWAII_67BE, PCI_CHIP_HAWAII_67BE, RES_SHARED_VGA }, + { PCI_CHIP_TOPAZ_6900, PCI_CHIP_TOPAZ_6900, RES_SHARED_VGA }, + { PCI_CHIP_TOPAZ_6901, PCI_CHIP_TOPAZ_6901, RES_SHARED_VGA }, + { PCI_CHIP_TOPAZ_6902, PCI_CHIP_TOPAZ_6902, RES_SHARED_VGA }, + { PCI_CHIP_TOPAZ_6903, PCI_CHIP_TOPAZ_6903, RES_SHARED_VGA }, + { PCI_CHIP_TOPAZ_6907, PCI_CHIP_TOPAZ_6907, RES_SHARED_VGA }, + { PCI_CHIP_TONGA_6920, PCI_CHIP_TONGA_6920, RES_SHARED_VGA }, + { PCI_CHIP_TONGA_6921, PCI_CHIP_TONGA_6921, RES_SHARED_VGA }, + { PCI_CHIP_TONGA_6938, PCI_CHIP_TONGA_6938, RES_SHARED_VGA }, + { PCI_CHIP_TONGA_6939, PCI_CHIP_TONGA_6939, RES_SHARED_VGA }, + { PCI_CHIP_TONGA_692B, PCI_CHIP_TONGA_692B, RES_SHARED_VGA }, + { PCI_CHIP_TONGA_692F, PCI_CHIP_TONGA_692F, RES_SHARED_VGA }, + { PCI_CHIP_CARRIZO_9870, PCI_CHIP_CARRIZO_9870, RES_SHARED_VGA }, + { PCI_CHIP_CARRIZO_9874, PCI_CHIP_CARRIZO_9874, RES_SHARED_VGA }, + { PCI_CHIP_CARRIZO_9875, PCI_CHIP_CARRIZO_9875, RES_SHARED_VGA }, + { PCI_CHIP_CARRIZO_9876, PCI_CHIP_CARRIZO_9876, RES_SHARED_VGA }, + { PCI_CHIP_CARRIZO_9877, PCI_CHIP_CARRIZO_9877, RES_SHARED_VGA }, + { -1, -1, RES_UNDEFINED } +}; diff --git a/src/amdgpu_pci_device_match_gen.h b/src/amdgpu_pci_device_match_gen.h new file mode 100644 index 0000000..1e9255f --- /dev/null +++ b/src/amdgpu_pci_device_match_gen.h @@ -0,0 +1,77 @@ +/* This file is autogenerated please do not edit */ +static const struct pci_id_match amdgpu_device_match[] = { + ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_6640, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_6641, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_6649, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_6650, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_6651, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_6658, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_665C, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_BONAIRE_665D, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9830, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9831, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9832, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9833, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9834, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9835, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9836, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9837, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9838, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_9839, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_983A, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_983B, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_983C, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_983D, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_983E, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KABINI_983F, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1304, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1305, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1306, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1307, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1309, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_130A, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_130B, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_130C, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_130D, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_130E, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_130F, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1310, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1311, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1312, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1313, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1315, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1316, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_1317, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_131B, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_131C, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_KAVERI_131D, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67A0, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67A1, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67A2, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67A8, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67A9, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67AA, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67B0, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67B1, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67B8, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67B9, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67BA, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_HAWAII_67BE, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_TOPAZ_6900, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_TOPAZ_6901, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_TOPAZ_6902, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_TOPAZ_6903, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_TOPAZ_6907, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_TONGA_6920, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_TONGA_6921, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_TONGA_6938, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_TONGA_6939, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_TONGA_692B, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_TONGA_692F, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_CARRIZO_9870, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_CARRIZO_9874, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_CARRIZO_9875, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_CARRIZO_9876, 0 ), + ATI_DEVICE_MATCH( PCI_CHIP_CARRIZO_9877, 0 ), + { 0, 0, 0 } +}; diff --git a/src/amdgpu_pixmap.c b/src/amdgpu_pixmap.c new file mode 100644 index 0000000..657ad33 --- /dev/null +++ b/src/amdgpu_pixmap.c @@ -0,0 +1,115 @@ +/* Copyright © 2014 Advanced Micro Devices, Inc. + * + * 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 the rights to use, copy, + * modify, merge, publish, distribute, sublicense, 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 + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <xf86.h> +#include "amdgpu_pixmap.h" +#include "amdgpu_bo_helper.h" + +static PixmapPtr +amdgpu_pixmap_create(ScreenPtr screen, int w, int h, int depth, unsigned usage) +{ + ScrnInfoPtr scrn; + struct amdgpu_pixmap *priv; + PixmapPtr pixmap; + AMDGPUInfoPtr info; + + /* only DRI2 pixmap is suppported */ + if (!(usage & AMDGPU_CREATE_PIXMAP_DRI2)) + return fbCreatePixmap(screen, w, h, depth, usage); + + if (w > 32767 || h > 32767) + return NullPixmap; + + if (depth == 1) + return fbCreatePixmap(screen, w, h, depth, usage); + + pixmap = fbCreatePixmap(screen, 0, 0, depth, usage); + if (pixmap == NullPixmap) + return pixmap; + + if (w && h) { + priv = calloc(1, sizeof(struct amdgpu_pixmap)); + if (priv == NULL) + goto fallback_pixmap; + + scrn = xf86ScreenToScrn(screen); + info = AMDGPUPTR(scrn); + if (!info->use_glamor) + usage |= AMDGPU_CREATE_PIXMAP_LINEAR; + priv->bo = amdgpu_alloc_pixmap_bo(scrn, w, h, depth, usage, + pixmap->drawable.bitsPerPixel, + &priv->stride); + if (!priv->bo) + goto fallback_priv; + + amdgpu_set_pixmap_private(pixmap, priv); + + if (amdgpu_bo_map(scrn, priv->bo)) { + ErrorF("Failed to mmap the bo\n"); + goto fallback_bo; + } + + screen->ModifyPixmapHeader(pixmap, w, h, + 0, 0, priv->stride, + priv->bo->cpu_ptr); + } + + return pixmap; + +fallback_bo: + amdgpu_bo_unref(&priv->bo); +fallback_priv: + free(priv); +fallback_pixmap: + fbDestroyPixmap(pixmap); + return fbCreatePixmap(screen, w, h, depth, usage); +} + +static Bool amdgpu_pixmap_destroy(PixmapPtr pixmap) +{ + if (pixmap->refcnt == 1) { + amdgpu_set_pixmap_bo(pixmap, NULL); + } + fbDestroyPixmap(pixmap); + return TRUE; +} + +/* This should only be called when glamor is disabled */ +Bool amdgpu_pixmap_init(ScreenPtr screen) +{ +#if HAS_DIXREGISTERPRIVATEKEY + if (!dixRegisterPrivateKey(&amdgpu_pixmap_index, PRIVATE_PIXMAP, 0)) +#else + if (!dixRequestPrivate(&amdgpu_pixmap_index, 0)) +#endif + return FALSE; + + screen->CreatePixmap = amdgpu_pixmap_create; + screen->DestroyPixmap = amdgpu_pixmap_destroy; + return TRUE; +} diff --git a/src/amdgpu_pixmap.h b/src/amdgpu_pixmap.h new file mode 100644 index 0000000..e37466c --- /dev/null +++ b/src/amdgpu_pixmap.h @@ -0,0 +1,110 @@ +/* + * Copyright © 2014 Advanced Micro Devices, Inc. + * + * 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 the rights to use, copy, + * modify, merge, publish, distribute, sublicense, 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 + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS 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. + */ + +#ifndef AMDGPU_PIXMAP_H +#define AMDGPU_PIXMAP_H + +#include "amdgpu_drv.h" + +struct amdgpu_pixmap { + struct amdgpu_buffer *bo; + int stride; +}; + +#if HAS_DEVPRIVATEKEYREC +extern DevPrivateKeyRec amdgpu_pixmap_index; +#else +extern int amdgpu_pixmap_index; +#endif + +static inline struct amdgpu_pixmap *amdgpu_get_pixmap_private(PixmapPtr pixmap) +{ +#if HAS_DEVPRIVATEKEYREC + return dixGetPrivate(&pixmap->devPrivates, &amdgpu_pixmap_index); +#else + return dixLookupPrivate(&pixmap->devPrivates, &amdgpu_pixmap_index); +#endif +} + +static inline void amdgpu_set_pixmap_private(PixmapPtr pixmap, + struct amdgpu_pixmap *priv) +{ + dixSetPrivate(&pixmap->devPrivates, &amdgpu_pixmap_index, priv); +} + +#if XF86_CRTC_VERSION >= 5 +#define AMDGPU_PIXMAP_SHARING 1 +#endif + +static inline void amdgpu_set_pixmap_bo(PixmapPtr pPix, struct amdgpu_buffer *bo) +{ + struct amdgpu_pixmap *priv; + + priv = amdgpu_get_pixmap_private(pPix); + if (priv == NULL && bo == NULL) + return; + + if (priv) { + if (priv->bo == bo) + return; + + if (priv->bo) { + amdgpu_bo_unref(&priv->bo); + } + + if (!bo) { + free(priv); + priv = NULL; + } + } + + if (bo) { + if (!priv) { + priv = calloc(1, sizeof(struct amdgpu_pixmap)); + if (!priv) + goto out; + } + amdgpu_bo_ref(bo); + priv->bo = bo; + } +out: + amdgpu_set_pixmap_private(pPix, priv); +} + +static inline struct amdgpu_buffer *amdgpu_get_pixmap_bo(PixmapPtr pPix) +{ + struct amdgpu_pixmap *priv; + priv = amdgpu_get_pixmap_private(pPix); + return priv ? priv->bo : NULL; +} + +enum { + AMDGPU_CREATE_PIXMAP_DRI2 = 0x08000000, + AMDGPU_CREATE_PIXMAP_LINEAR = 0x04000000 +}; + +extern Bool amdgpu_pixmap_init(ScreenPtr screen); + +#endif /* AMDGPU_PIXMAP_H */ diff --git a/src/amdgpu_probe.c b/src/amdgpu_probe.c new file mode 100644 index 0000000..9edaf4f --- /dev/null +++ b/src/amdgpu_probe.c @@ -0,0 +1,374 @@ +/* + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + * VA Linux Systems Inc., Fremont, California. + * + * All Rights Reserved. + * + * 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, sublicense, 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 ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +/* + * Authors: + * Kevin E. Martin <martin@xfree86.org> + * Rickard E. Faith <faith@valinux.com> + * KMS support - Dave Airlie <airlied@redhat.com> + */ + +#include "amdgpu_probe.h" +#include "amdgpu_version.h" +#include "amdpciids.h" + +#include "xf86.h" + +#include "xf86drmMode.h" +#include "dri.h" + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include <xf86_OSproc.h> +#endif + +#ifdef XSERVER_PLATFORM_BUS +#include <xf86platformBus.h> +#endif + +#include "amdgpu_chipset_gen.h" + +#include "amdgpu_pci_chipset_gen.h" + +#include "amdgpu_pci_device_match_gen.h" + +_X_EXPORT int gAMDGPUEntityIndex = -1; + +/* Return the options for supported chipset 'n'; NULL otherwise */ +static const OptionInfoRec *AMDGPUAvailableOptions(int chipid, int busid) +{ + return AMDGPUOptionsWeak(); +} + +/* Return the string name for supported chipset 'n'; NULL otherwise. */ +static void AMDGPUIdentify(int flags) +{ + xf86PrintChipsets(AMDGPU_NAME, + "Driver for AMD Radeon chipsets", AMDGPUChipsets); +} + +static Bool amdgpu_kernel_mode_enabled(ScrnInfoPtr pScrn, + struct pci_device *pci_dev) +{ + char *busIdString; + int ret; + + if (!xf86LoaderCheckSymbol("DRICreatePCIBusID")) { + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 0, + "[KMS] No DRICreatePCIBusID symbol, no kernel modesetting.\n"); + return FALSE; + } + + busIdString = DRICreatePCIBusID(pci_dev); + ret = drmCheckModesettingSupported(busIdString); +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + if (ret) { + if (xf86LoadKernelModule("amdgpukms")) + ret = drmCheckModesettingSupported(busIdString); + } +#endif + free(busIdString); + if (ret) { + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 0, + "[KMS] drm report modesetting isn't supported.\n"); + return FALSE; + } + + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 0, + "[KMS] Kernel modesetting enabled.\n"); + return TRUE; +} + +static int amdgpu_kernel_open_fd(ScrnInfoPtr pScrn, struct pci_device *dev) +{ + char *busid; + drmSetVersion sv; + int err; + int fd; + +#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,9,99,901,0) + XNFasprintf(&busid, "pci:%04x:%02x:%02x.%d", + dev->domain, dev->bus, dev->dev, dev->func); +#else + busid = XNFprintf("pci:%04x:%02x:%02x.%d", + dev->domain, dev->bus, dev->dev, dev->func); +#endif + + fd = drmOpen(NULL, busid); + free(busid); + if (fd == -1) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "[drm] Failed to open DRM device for %s: %s\n", + busid, strerror(errno)); + return fd; + } + + /* 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. + * (see DRIOpenDRMMaster() in DRI1) + */ + sv.drm_di_major = 1; + sv.drm_di_minor = 1; + sv.drm_dd_major = -1; + sv.drm_dd_minor = -1; + err = drmSetInterfaceVersion(fd, &sv); + if (err != 0) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "[drm] failed to set drm interface version.\n"); + drmClose(fd); + return -1; + } + + return fd; +} + +static Bool amdgpu_get_scrninfo(int entity_num, void *pci_dev) +{ + ScrnInfoPtr pScrn = NULL; + EntityInfoPtr pEnt; + DevUnion *pPriv; + AMDGPUEntPtr pAMDGPUEnt; + + pScrn = xf86ConfigPciEntity(pScrn, 0, entity_num, AMDGPUPciChipsets, + NULL, NULL, NULL, NULL, NULL); + + if (!pScrn) + return FALSE; + + if (pci_dev) { + if (!amdgpu_kernel_mode_enabled(pScrn, pci_dev)) { + return FALSE; + } + } + + pScrn->driverVersion = AMDGPU_VERSION_CURRENT; + pScrn->driverName = AMDGPU_DRIVER_NAME; + pScrn->name = AMDGPU_NAME; + pScrn->Probe = NULL; + + pScrn->PreInit = AMDGPUPreInit_KMS; + pScrn->ScreenInit = AMDGPUScreenInit_KMS; + pScrn->SwitchMode = AMDGPUSwitchMode_KMS; + pScrn->AdjustFrame = AMDGPUAdjustFrame_KMS; + pScrn->EnterVT = AMDGPUEnterVT_KMS; + pScrn->LeaveVT = AMDGPULeaveVT_KMS; + pScrn->FreeScreen = AMDGPUFreeScreen_KMS; + pScrn->ValidMode = AMDGPUValidMode; + + pEnt = xf86GetEntityInfo(entity_num); + + /* Create a AMDGPUEntity for all chips, even with old single head + * Radeon, need to use pAMDGPUEnt for new monitor detection routines. + */ + xf86SetEntitySharable(entity_num); + + if (gAMDGPUEntityIndex == -1) + gAMDGPUEntityIndex = xf86AllocateEntityPrivateIndex(); + + pPriv = xf86GetEntityPrivate(pEnt->index, gAMDGPUEntityIndex); + + if (!pPriv->ptr) { + uint32_t major_version; + uint32_t minor_version; + + pPriv->ptr = xnfcalloc(sizeof(AMDGPUEntRec), 1); + pAMDGPUEnt = pPriv->ptr; + pAMDGPUEnt->HasSecondary = FALSE; + + pAMDGPUEnt->fd = amdgpu_kernel_open_fd(pScrn, pci_dev); + if (pAMDGPUEnt->fd < 0) + goto error_fd; + + pAMDGPUEnt->fd_ref = 1; + + if (amdgpu_device_initialize(pAMDGPUEnt->fd, + &major_version, + &minor_version, + &pAMDGPUEnt->pDev)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "amdgpu_device_initialize failed\n"); + goto error_amdgpu; + } + } else { + pAMDGPUEnt = pPriv->ptr; + pAMDGPUEnt->HasSecondary = TRUE; + } + + xf86SetEntityInstanceForScreen(pScrn, pEnt->index, + xf86GetNumEntityInstances(pEnt-> + index) + - 1); + free(pEnt); + + return TRUE; + +error_amdgpu: + drmClose(pAMDGPUEnt->fd); + pAMDGPUEnt->fd = 0; +error_fd: + free(pPriv->ptr); + return FALSE; +} + +static Bool +amdgpu_pci_probe(DriverPtr pDriver, + int entity_num, struct pci_device *device, intptr_t match_data) +{ + return amdgpu_get_scrninfo(entity_num, (void *)device); +} + +static Bool AMDGPUDriverFunc(ScrnInfoPtr scrn, xorgDriverFuncOp op, void *data) +{ + xorgHWFlags *flag; + + switch (op) { + case GET_REQUIRED_HW_INTERFACES: + flag = (CARD32 *) data; + (*flag) = 0; + return TRUE; + default: + return FALSE; + } +} + +#ifdef XSERVER_PLATFORM_BUS +static Bool +amdgpu_platform_probe(DriverPtr pDriver, + int entity_num, int flags, + struct xf86_platform_device *dev, intptr_t match_data) +{ + ScrnInfoPtr pScrn; + int scr_flags = 0; + EntityInfoPtr pEnt; + DevUnion *pPriv; + AMDGPUEntPtr pAMDGPUEnt; + + if (!dev->pdev) + return FALSE; + + if (flags & PLATFORM_PROBE_GPU_SCREEN) + scr_flags = XF86_ALLOCATE_GPU_SCREEN; + + pScrn = xf86AllocateScreen(pDriver, scr_flags); + if (xf86IsEntitySharable(entity_num)) + xf86SetEntityShared(entity_num); + xf86AddEntityToScreen(pScrn, entity_num); + + if (!amdgpu_kernel_mode_enabled(pScrn, dev->pdev)) + return FALSE; + + pScrn->driverVersion = AMDGPU_VERSION_CURRENT; + pScrn->driverName = AMDGPU_DRIVER_NAME; + pScrn->name = AMDGPU_NAME; + pScrn->Probe = NULL; + pScrn->PreInit = AMDGPUPreInit_KMS; + pScrn->ScreenInit = AMDGPUScreenInit_KMS; + pScrn->SwitchMode = AMDGPUSwitchMode_KMS; + pScrn->AdjustFrame = AMDGPUAdjustFrame_KMS; + pScrn->EnterVT = AMDGPUEnterVT_KMS; + pScrn->LeaveVT = AMDGPULeaveVT_KMS; + pScrn->FreeScreen = AMDGPUFreeScreen_KMS; + pScrn->ValidMode = AMDGPUValidMode; + + pEnt = xf86GetEntityInfo(entity_num); + + /* Create a AMDGPUEntity for all chips, even with old single head + * Radeon, need to use pAMDGPUEnt for new monitor detection routines. + */ + xf86SetEntitySharable(entity_num); + + if (gAMDGPUEntityIndex == -1) + gAMDGPUEntityIndex = xf86AllocateEntityPrivateIndex(); + + pPriv = xf86GetEntityPrivate(pEnt->index, gAMDGPUEntityIndex); + + if (!pPriv->ptr) { + uint32_t major_version; + uint32_t minor_version; + + pPriv->ptr = xnfcalloc(sizeof(AMDGPUEntRec), 1); + pAMDGPUEnt = pPriv->ptr; + pAMDGPUEnt->HasSecondary = FALSE; + pAMDGPUEnt->fd = amdgpu_kernel_open_fd(pScrn, dev->pdev); + if (pAMDGPUEnt->fd < 0) + goto error_fd; + + pAMDGPUEnt->fd_ref = 1; + + if (amdgpu_device_initialize(pAMDGPUEnt->fd, + &major_version, + &minor_version, + &pAMDGPUEnt->pDev)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "amdgpu_device_initialize failed\n"); + goto error_amdgpu; + } + } else { + pAMDGPUEnt = pPriv->ptr; + pAMDGPUEnt->HasSecondary = TRUE; + } + + xf86SetEntityInstanceForScreen(pScrn, pEnt->index, + xf86GetNumEntityInstances(pEnt-> + index) + - 1); + free(pEnt); + + return TRUE; + +error_amdgpu: + drmClose(pAMDGPUEnt->fd); + pAMDGPUEnt->fd = 0; +error_fd: + free(pPriv->ptr); + return FALSE; +} +#endif + +_X_EXPORT DriverRec AMDGPU = { + AMDGPU_VERSION_CURRENT, + AMDGPU_DRIVER_NAME, + AMDGPUIdentify, + NULL, + AMDGPUAvailableOptions, + NULL, + 0, + AMDGPUDriverFunc, + amdgpu_device_match, + amdgpu_pci_probe, +#ifdef XSERVER_PLATFORM_BUS + amdgpu_platform_probe +#endif +}; diff --git a/src/amdgpu_probe.h b/src/amdgpu_probe.h new file mode 100644 index 0000000..1f760ac --- /dev/null +++ b/src/amdgpu_probe.h @@ -0,0 +1,97 @@ +/* + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + * VA Linux Systems Inc., Fremont, California. + * + * All Rights Reserved. + * + * 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, sublicense, 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 ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS 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. + */ + +/* + * Authors: + * Kevin E. Martin <martin@xfree86.org> + * + * Modified by Marc Aurele La France <tsi@xfree86.org> for ATI driver merge. + */ + +#ifndef _AMDGPU_PROBE_H_ +#define _AMDGPU_PROBE_H_ 1 + +#include <stdint.h> +#include "xf86str.h" +#include "xf86DDC.h" +#include "randrstr.h" + +#include "xf86Crtc.h" + +#include <amdgpu.h> + +#include "compat-api.h" + +extern DriverRec AMDGPU; + +typedef enum { + CHIP_FAMILY_UNKNOW, + CHIP_FAMILY_LEGACY, + CHIP_FAMILY_AMDGPU, + CHIP_FAMILY_BONAIRE, + CHIP_FAMILY_KAVERI, + CHIP_FAMILY_KABINI, + CHIP_FAMILY_HAWAII, + CHIP_FAMILY_TOPAZ, + CHIP_FAMILY_TONGA, + CHIP_FAMILY_CARRIZO, + CHIP_FAMILY_LAST +} AMDGPUChipFamily; + +typedef struct { + uint32_t pci_device_id; + AMDGPUChipFamily chip_family; +} AMDGPUCardInfo; + +typedef struct { + Bool HasSecondary; + Bool HasCRTC2; /* All cards except original Radeon */ + + ScrnInfoPtr pSecondaryScrn; + ScrnInfoPtr pPrimaryScrn; + amdgpu_device_handle pDev; + + int fd; /* for sharing across zaphod heads */ + int fd_ref; + unsigned long fd_wakeup_registered; /* server generation for which fd has been registered for wakeup handling */ + int fd_wakeup_ref; +} AMDGPUEntRec, *AMDGPUEntPtr; + +extern const OptionInfoRec *AMDGPUOptionsWeak(void); + +extern Bool AMDGPUPreInit_KMS(ScrnInfoPtr, int); +extern Bool AMDGPUScreenInit_KMS(SCREEN_INIT_ARGS_DECL); +extern Bool AMDGPUSwitchMode_KMS(SWITCH_MODE_ARGS_DECL); +extern void AMDGPUAdjustFrame_KMS(ADJUST_FRAME_ARGS_DECL); +extern Bool AMDGPUEnterVT_KMS(VT_FUNC_ARGS_DECL); +extern void AMDGPULeaveVT_KMS(VT_FUNC_ARGS_DECL); +extern void AMDGPUFreeScreen_KMS(FREE_SCREEN_ARGS_DECL); + +extern ModeStatus AMDGPUValidMode(SCRN_ARG_TYPE arg, DisplayModePtr mode, + Bool verbose, int flag); +#endif /* _AMDGPU_PROBE_H_ */ diff --git a/src/amdgpu_version.h b/src/amdgpu_version.h new file mode 100644 index 0000000..8b98ecd --- /dev/null +++ b/src/amdgpu_version.h @@ -0,0 +1,61 @@ +/* + * Copyright 2000 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org + * + * 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, and + * that the name of Marc Aurele La France not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Marc Aurele La France makes no representations + * about the suitability of this software for any purpose. It is provided + * "as-is" without express or implied warranty. + * + * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _AMDGPU_VERSION_H_ +#define _AMDGPU_VERSION_H_ 1 + +#undef AMDGPU_NAME +#undef AMDGPU_DRIVER_NAME +#undef R200_DRIVER_NAME +#undef AMDGPU_VERSION_MAJOR +#undef AMDGPU_VERSION_MINOR +#undef AMDGPU_VERSION_PATCH +#undef AMDGPU_VERSION_CURRENT +#undef AMDGPU_VERSION_EVALUATE +#undef AMDGPU_VERSION_STRINGIFY +#undef AMDGPU_VERSION_NAME + +#define AMDGPU_NAME "AMDGPU" +#define AMDGPU_DRIVER_NAME "amdgpu" +#define SI_DRIVER_NAME "radeonsi" + +#define AMDGPU_VERSION_MAJOR PACKAGE_VERSION_MAJOR +#define AMDGPU_VERSION_MINOR PACKAGE_VERSION_MINOR +#define AMDGPU_VERSION_PATCH PACKAGE_VERSION_PATCHLEVEL + +#ifndef AMDGPU_VERSION_EXTRA +#define AMDGPU_VERSION_EXTRA "" +#endif + +#define AMDGPU_VERSION_CURRENT \ + ((AMDGPU_VERSION_MAJOR << 20) | \ + (AMDGPU_VERSION_MINOR << 10) | \ + (AMDGPU_VERSION_PATCH)) + +#define AMDGPU_VERSION_EVALUATE(__x) #__x +#define AMDGPU_VERSION_STRINGIFY(_x) AMDGPU_VERSION_EVALUATE(_x) +#define AMDGPU_VERSION_NAME \ + AMDGPU_VERSION_STRINGIFY(AMDGPU_VERSION_MAJOR) "." \ + AMDGPU_VERSION_STRINGIFY(AMDGPU_VERSION_MINOR) "." \ + AMDGPU_VERSION_STRINGIFY(AMDGPU_VERSION_PATCH) AMDGPU_VERSION_EXTRA + +#endif /* _AMDGPU_VERSION_H_ */ diff --git a/src/amdgpu_video.c b/src/amdgpu_video.c new file mode 100644 index 0000000..32f5709 --- /dev/null +++ b/src/amdgpu_video.c @@ -0,0 +1,177 @@ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <math.h> + +#include "amdgpu_drv.h" +#include "amdgpu_probe.h" +#include "amdgpu_video.h" +#include "amdgpu_pixmap.h" + +#include "xf86.h" +#include "dixstruct.h" + +/* DPMS */ +#ifdef HAVE_XEXTPROTO_71 +#include <X11/extensions/dpmsconst.h> +#else +#define DPMS_SERVER +#include <X11/extensions/dpms.h> +#endif + +#include <X11/extensions/Xv.h> +#include "fourcc.h" + +#define OFF_DELAY 250 /* milliseconds */ +#define FREE_DELAY 15000 + +#define OFF_TIMER 0x01 +#define FREE_TIMER 0x02 +#define CLIENT_VIDEO_ON 0x04 + +static void amdgpu_box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b) +{ + dest->x1 = a->x1 > b->x1 ? a->x1 : b->x1; + dest->x2 = a->x2 < b->x2 ? a->x2 : b->x2; + dest->y1 = a->y1 > b->y1 ? a->y1 : b->y1; + dest->y2 = a->y2 < b->y2 ? a->y2 : b->y2; + + if (dest->x1 >= dest->x2 || dest->y1 >= dest->y2) + dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0; +} + +static void amdgpu_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box) +{ + if (crtc->enabled) { + crtc_box->x1 = crtc->x; + crtc_box->x2 = + crtc->x + xf86ModeWidth(&crtc->mode, crtc->rotation); + crtc_box->y1 = crtc->y; + crtc_box->y2 = + crtc->y + xf86ModeHeight(&crtc->mode, crtc->rotation); + } else + crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0; +} + +static int amdgpu_box_area(BoxPtr box) +{ + return (int)(box->x2 - box->x1) * (int)(box->y2 - box->y1); +} + +Bool amdgpu_crtc_is_enabled(xf86CrtcPtr crtc) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + return drmmode_crtc->dpms_mode == DPMSModeOn; +} + +uint32_t amdgpu_get_interpolated_vblanks(xf86CrtcPtr crtc) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + return drmmode_crtc->interpolated_vblanks; +} + +xf86CrtcPtr +amdgpu_pick_best_crtc(ScrnInfoPtr pScrn, Bool consider_disabled, + int x1, int x2, int y1, int y2) +{ + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); + int coverage, best_coverage, c; + BoxRec box, crtc_box, cover_box; + RROutputPtr primary_output = NULL; + xf86CrtcPtr best_crtc = NULL, primary_crtc = NULL; + + if (!pScrn->vtSema) + return NULL; + + box.x1 = x1; + box.x2 = x2; + box.y1 = y1; + box.y2 = y2; + best_coverage = 0; + + /* Prefer the CRTC of the primary output */ +#ifdef HAS_DIXREGISTERPRIVATEKEY + if (dixPrivateKeyRegistered(rrPrivKey)) +#endif + { + primary_output = RRFirstOutput(pScrn->pScreen); + } + if (primary_output && primary_output->crtc) + primary_crtc = primary_output->crtc->devPrivate; + + /* first consider only enabled CRTCs */ + for (c = 0; c < xf86_config->num_crtc; c++) { + xf86CrtcPtr crtc = xf86_config->crtc[c]; + + if (!amdgpu_crtc_is_enabled(crtc)) + continue; + + amdgpu_crtc_box(crtc, &crtc_box); + amdgpu_box_intersect(&cover_box, &crtc_box, &box); + coverage = amdgpu_box_area(&cover_box); + if (coverage > best_coverage || + (coverage == best_coverage && crtc == primary_crtc)) { + best_crtc = crtc; + best_coverage = coverage; + } + } + if (best_crtc || !consider_disabled) + return best_crtc; + + /* if we found nothing, repeat the search including disabled CRTCs */ + for (c = 0; c < xf86_config->num_crtc; c++) { + xf86CrtcPtr crtc = xf86_config->crtc[c]; + + amdgpu_crtc_box(crtc, &crtc_box); + amdgpu_box_intersect(&cover_box, &crtc_box, &box); + coverage = amdgpu_box_area(&cover_box); + if (coverage > best_coverage || + (coverage == best_coverage && crtc == primary_crtc)) { + best_crtc = crtc; + best_coverage = coverage; + } + } + return best_crtc; +} + +void AMDGPUInitVideo(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL; + XF86VideoAdaptorPtr texturedAdaptor = NULL; + int num_adaptors; + + num_adaptors = xf86XVListGenericAdaptors(pScrn, &adaptors); + newAdaptors = + malloc((num_adaptors + 2) * sizeof(XF86VideoAdaptorPtr *)); + if (newAdaptors == NULL) + return; + + memcpy(newAdaptors, adaptors, + num_adaptors * sizeof(XF86VideoAdaptorPtr)); + adaptors = newAdaptors; + + if (info->use_glamor) { + texturedAdaptor = amdgpu_glamor_xv_init(pScreen, 16); + if (texturedAdaptor != NULL) { + adaptors[num_adaptors++] = texturedAdaptor; + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Set up textured video (glamor)\n"); + } else + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to set up textured video (glamor)\n"); + } + + if (num_adaptors) + xf86XVScreenInit(pScreen, adaptors, num_adaptors); + + if (newAdaptors) + free(newAdaptors); + +} diff --git a/src/amdgpu_video.h b/src/amdgpu_video.h new file mode 100644 index 0000000..2915e3a --- /dev/null +++ b/src/amdgpu_video.h @@ -0,0 +1,12 @@ +#ifndef __AMDGPU_VIDEO_H__ +#define __AMDGPU_VIDEO_H__ + +#include "xf86i2c.h" +#include "i2c_def.h" + +#include "xf86Crtc.h" + +Bool amdgpu_crtc_is_enabled(xf86CrtcPtr crtc); +uint32_t amdgpu_get_interpolated_vblanks(xf86CrtcPtr crtc); + +#endif /* __AMDGPU_VIDEO_H__ */ diff --git a/src/amdpciids.h b/src/amdpciids.h new file mode 100644 index 0000000..33ec6da --- /dev/null +++ b/src/amdpciids.h @@ -0,0 +1,39 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * + * 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 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * +*/ + + +#ifndef _AMDPCIIDS_H +#define _AMDPCIIDS_H + +#define PCI_VENDOR_ATI 0x1002 + +#include "ati_pciids_gen.h" + +typedef struct pci_device *pciVideoPtr; + +#define PCI_DEV_DEVICE_ID(_pcidev) ((_pcidev)->device_id) + +#define ATI_DEVICE_MATCH(d, i) \ + { PCI_VENDOR_ATI, (d), PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0, (i) } + +#endif /* AMDPCIIDS_H */ diff --git a/src/ati_pciids_gen.h b/src/ati_pciids_gen.h new file mode 100644 index 0000000..8c58736 --- /dev/null +++ b/src/ati_pciids_gen.h @@ -0,0 +1,73 @@ +#define PCI_CHIP_BONAIRE_6640 0x6640 +#define PCI_CHIP_BONAIRE_6641 0x6641 +#define PCI_CHIP_BONAIRE_6649 0x6649 +#define PCI_CHIP_BONAIRE_6650 0x6650 +#define PCI_CHIP_BONAIRE_6651 0x6651 +#define PCI_CHIP_BONAIRE_6658 0x6658 +#define PCI_CHIP_BONAIRE_665C 0x665C +#define PCI_CHIP_BONAIRE_665D 0x665D +#define PCI_CHIP_KABINI_9830 0x9830 +#define PCI_CHIP_KABINI_9831 0x9831 +#define PCI_CHIP_KABINI_9832 0x9832 +#define PCI_CHIP_KABINI_9833 0x9833 +#define PCI_CHIP_KABINI_9834 0x9834 +#define PCI_CHIP_KABINI_9835 0x9835 +#define PCI_CHIP_KABINI_9836 0x9836 +#define PCI_CHIP_KABINI_9837 0x9837 +#define PCI_CHIP_KABINI_9838 0x9838 +#define PCI_CHIP_KABINI_9839 0x9839 +#define PCI_CHIP_KABINI_983A 0x983A +#define PCI_CHIP_KABINI_983B 0x983B +#define PCI_CHIP_KABINI_983C 0x983C +#define PCI_CHIP_KABINI_983D 0x983D +#define PCI_CHIP_KABINI_983E 0x983E +#define PCI_CHIP_KABINI_983F 0x983F +#define PCI_CHIP_KAVERI_1304 0x1304 +#define PCI_CHIP_KAVERI_1305 0x1305 +#define PCI_CHIP_KAVERI_1306 0x1306 +#define PCI_CHIP_KAVERI_1307 0x1307 +#define PCI_CHIP_KAVERI_1309 0x1309 +#define PCI_CHIP_KAVERI_130A 0x130A +#define PCI_CHIP_KAVERI_130B 0x130B +#define PCI_CHIP_KAVERI_130C 0x130C +#define PCI_CHIP_KAVERI_130D 0x130D +#define PCI_CHIP_KAVERI_130E 0x130E +#define PCI_CHIP_KAVERI_130F 0x130F +#define PCI_CHIP_KAVERI_1310 0x1310 +#define PCI_CHIP_KAVERI_1311 0x1311 +#define PCI_CHIP_KAVERI_1312 0x1312 +#define PCI_CHIP_KAVERI_1313 0x1313 +#define PCI_CHIP_KAVERI_1315 0x1315 +#define PCI_CHIP_KAVERI_1316 0x1316 +#define PCI_CHIP_KAVERI_1317 0x1317 +#define PCI_CHIP_KAVERI_131B 0x131B +#define PCI_CHIP_KAVERI_131C 0x131C +#define PCI_CHIP_KAVERI_131D 0x131D +#define PCI_CHIP_HAWAII_67A0 0x67A0 +#define PCI_CHIP_HAWAII_67A1 0x67A1 +#define PCI_CHIP_HAWAII_67A2 0x67A2 +#define PCI_CHIP_HAWAII_67A8 0x67A8 +#define PCI_CHIP_HAWAII_67A9 0x67A9 +#define PCI_CHIP_HAWAII_67AA 0x67AA +#define PCI_CHIP_HAWAII_67B0 0x67B0 +#define PCI_CHIP_HAWAII_67B1 0x67B1 +#define PCI_CHIP_HAWAII_67B8 0x67B8 +#define PCI_CHIP_HAWAII_67B9 0x67B9 +#define PCI_CHIP_HAWAII_67BA 0x67BA +#define PCI_CHIP_HAWAII_67BE 0x67BE +#define PCI_CHIP_TOPAZ_6900 0x6900 +#define PCI_CHIP_TOPAZ_6901 0x6901 +#define PCI_CHIP_TOPAZ_6902 0x6902 +#define PCI_CHIP_TOPAZ_6903 0x6903 +#define PCI_CHIP_TOPAZ_6907 0x6907 +#define PCI_CHIP_TONGA_6920 0x6920 +#define PCI_CHIP_TONGA_6921 0x6921 +#define PCI_CHIP_TONGA_6938 0x6938 +#define PCI_CHIP_TONGA_6939 0x6939 +#define PCI_CHIP_TONGA_692B 0x692B +#define PCI_CHIP_TONGA_692F 0x692F +#define PCI_CHIP_CARRIZO_9870 0x9870 +#define PCI_CHIP_CARRIZO_9874 0x9874 +#define PCI_CHIP_CARRIZO_9875 0x9875 +#define PCI_CHIP_CARRIZO_9876 0x9876 +#define PCI_CHIP_CARRIZO_9877 0x9877 diff --git a/src/compat-api.h b/src/compat-api.h new file mode 100644 index 0000000..b299f83 --- /dev/null +++ b/src/compat-api.h @@ -0,0 +1,94 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * 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 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Author: Dave Airlie <airlied@redhat.com> + */ + +/* this file provides API compat between server post 1.13 and pre it, + it should be reused inside as many drivers as possible */ +#ifndef COMPAT_API_H +#define COMPAT_API_H + +#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 + +#ifndef XF86_HAS_SCRN_CONV +#define xf86ScreenToScrn(s) xf86Screens[(s)->myNum] +#define xf86ScrnToScreen(s) screenInfo.screens[(s)->scrnIndex] +#endif + +#ifndef XF86_SCRN_INTERFACE + +#define SCRN_ARG_TYPE int +#define SCRN_INFO_PTR(arg1) ScrnInfoPtr pScrn = xf86Screens[(arg1)] + +#define SCREEN_ARG_TYPE int +#define SCREEN_PTR(arg1) ScreenPtr pScreen = screenInfo.screens[(arg1)] + +#define SCREEN_INIT_ARGS_DECL int i, ScreenPtr pScreen, int argc, char **argv + +#define BLOCKHANDLER_ARGS_DECL int arg, pointer blockData, pointer pTimeout, pointer pReadmask +#define BLOCKHANDLER_ARGS arg, blockData, pTimeout, pReadmask + +#define CLOSE_SCREEN_ARGS_DECL int scrnIndex, ScreenPtr pScreen +#define CLOSE_SCREEN_ARGS scrnIndex, pScreen + +#define ADJUST_FRAME_ARGS_DECL int arg, int x, int y, int flags + +#define SWITCH_MODE_ARGS_DECL int arg, DisplayModePtr mode, int flags + +#define FREE_SCREEN_ARGS_DECL int arg, int flags + +#define VT_FUNC_ARGS_DECL int arg, int flags +#define VT_FUNC_ARGS(flags) pScrn->scrnIndex, (flags) + +#define XF86_ENABLEDISABLEFB_ARG(x) ((x)->scrnIndex) +#else +#define SCRN_ARG_TYPE ScrnInfoPtr +#define SCRN_INFO_PTR(arg1) ScrnInfoPtr pScrn = (arg1) + +#define SCREEN_ARG_TYPE ScreenPtr +#define SCREEN_PTR(arg1) ScreenPtr pScreen = (arg1) + +#define SCREEN_INIT_ARGS_DECL ScreenPtr pScreen, int argc, char **argv + +#define BLOCKHANDLER_ARGS_DECL ScreenPtr arg, pointer pTimeout, pointer pReadmask +#define BLOCKHANDLER_ARGS arg, pTimeout, pReadmask + +#define CLOSE_SCREEN_ARGS_DECL ScreenPtr pScreen +#define CLOSE_SCREEN_ARGS pScreen + +#define ADJUST_FRAME_ARGS_DECL ScrnInfoPtr arg, int x, int y +#define SWITCH_MODE_ARGS_DECL ScrnInfoPtr arg, DisplayModePtr mode + +#define FREE_SCREEN_ARGS_DECL ScrnInfoPtr arg + +#define VT_FUNC_ARGS_DECL ScrnInfoPtr arg +#define VT_FUNC_ARGS(flags) pScrn + +#define XF86_ENABLEDISABLEFB_ARG(x) (x) + +#endif + +#endif diff --git a/src/drmmode_display.c b/src/drmmode_display.c new file mode 100644 index 0000000..59a3e5a --- /dev/null +++ b/src/drmmode_display.c @@ -0,0 +1,1855 @@ +/* + * Copyright © 2007 Red Hat, Inc. + * + * 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 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Authors: + * Dave Airlie <airlied@redhat.com> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <sys/ioctl.h> +#include <time.h> +#include "micmap.h" +#include "xf86cmap.h" +#include "sarea.h" + +#include "drmmode_display.h" +#include "amdgpu_bo_helper.h" +#include "amdgpu_pixmap.h" + +/* DPMS */ +#ifdef HAVE_XEXTPROTO_71 +#include <X11/extensions/dpmsconst.h> +#else +#define DPMS_SERVER +#include <X11/extensions/dpms.h> +#endif + +#include <gbm.h> + +#define DEFAULT_NOMINAL_FRAME_RATE 60 + +static Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height); + +static Bool +AMDGPUZaphodStringMatches(ScrnInfoPtr pScrn, const char *s, char *output_name) +{ + int i = 0; + char s1[20]; + + do { + switch (*s) { + case ',': + s1[i] = '\0'; + i = 0; + if (strcmp(s1, output_name) == 0) + return TRUE; + break; + case ' ': + case '\t': + case '\n': + case '\r': + break; + default: + s1[i] = *s; + i++; + break; + } + } while (*s++); + + s1[i] = '\0'; + if (strcmp(s1, output_name) == 0) + return TRUE; + + return FALSE; +} + +static PixmapPtr drmmode_create_bo_pixmap(ScrnInfoPtr pScrn, + int width, int height, + int depth, int bpp, + int pitch, + struct amdgpu_buffer *bo) +{ + ScreenPtr pScreen = pScrn->pScreen; + PixmapPtr pixmap; + + pixmap = (*pScreen->CreatePixmap) (pScreen, 0, 0, depth, 0); + if (!pixmap) + return NULL; + + if (!(*pScreen->ModifyPixmapHeader) (pixmap, width, height, + depth, bpp, pitch, NULL)) { + return NULL; + } + + amdgpu_set_pixmap_bo(pixmap, bo); + + if (!amdgpu_glamor_create_textured_pixmap(pixmap)) { + pScreen->DestroyPixmap(pixmap); + return NULL; + } + + return pixmap; +} + +static void drmmode_destroy_bo_pixmap(PixmapPtr pixmap) +{ + ScreenPtr pScreen = pixmap->drawable.pScreen; + + (*pScreen->DestroyPixmap) (pixmap); +} + +static void +drmmode_ConvertFromKMode(ScrnInfoPtr scrn, + drmModeModeInfo * kmode, DisplayModePtr mode) +{ + memset(mode, 0, sizeof(DisplayModeRec)); + 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; //& FLAG_BITS; + 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, scrn->adjustFlags); +} + +static void +drmmode_ConvertToKMode(ScrnInfoPtr scrn, + drmModeModeInfo * 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; //& FLAG_BITS; + if (mode->name) + strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN); + kmode->name[DRM_DISPLAY_MODE_LEN - 1] = 0; + +} + +/* + * Retrieves present time in microseconds that is compatible + * with units used by vblank timestamps. Depending on the kernel + * version and DRM kernel module configuration, the vblank + * timestamp can either be in real time or monotonic time + */ +int drmmode_get_current_ust(int drm_fd, CARD64 * ust) +{ + uint64_t cap_value; + int ret; + struct timespec now; + + ret = drmGetCap(drm_fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap_value); + if (ret || !cap_value) + /* old kernel or drm_timestamp_monotonic turned off */ + ret = clock_gettime(CLOCK_REALTIME, &now); + else + ret = clock_gettime(CLOCK_MONOTONIC, &now); + if (ret) + return ret; + *ust = ((CARD64) now.tv_sec * 1000000) + ((CARD64) now.tv_nsec / 1000); + return 0; +} + +static void drmmode_crtc_dpms(xf86CrtcPtr crtc, int mode) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + ScrnInfoPtr scrn = crtc->scrn; + AMDGPUInfoPtr info = AMDGPUPTR(scrn); + CARD64 ust; + int ret; + + if (drmmode_crtc->dpms_mode == DPMSModeOn && mode != DPMSModeOn) { + drmVBlank vbl; + + /* + * On->Off transition: record the last vblank time, + * sequence number and frame period. + */ + vbl.request.type = DRM_VBLANK_RELATIVE; + vbl.request.type |= amdgpu_populate_vbl_request_type(crtc); + vbl.request.sequence = 0; + ret = drmWaitVBlank(info->dri2.drm_fd, &vbl); + if (ret) + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "%s cannot get last vblank counter\n", + __func__); + else { + CARD64 seq = (CARD64) vbl.reply.sequence; + CARD64 nominal_frame_rate, pix_in_frame; + + ust = ((CARD64) vbl.reply.tval_sec * 1000000) + + vbl.reply.tval_usec; + drmmode_crtc->dpms_last_ust = ust; + drmmode_crtc->dpms_last_seq = seq; + nominal_frame_rate = crtc->mode.Clock; + nominal_frame_rate *= 1000; + pix_in_frame = crtc->mode.HTotal * crtc->mode.VTotal; + if (nominal_frame_rate == 0 || pix_in_frame == 0) + nominal_frame_rate = DEFAULT_NOMINAL_FRAME_RATE; + else + nominal_frame_rate /= pix_in_frame; + drmmode_crtc->dpms_last_fps = nominal_frame_rate; + } + } else if (drmmode_crtc->dpms_mode != DPMSModeOn && mode == DPMSModeOn) { + /* + * Off->On transition: calculate and accumulate the + * number of interpolated vblanks while we were in Off state + */ + ret = drmmode_get_current_ust(info->dri2.drm_fd, &ust); + if (ret) + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "%s cannot get current time\n", __func__); + else if (drmmode_crtc->dpms_last_ust) { + CARD64 time_elapsed, delta_seq; + time_elapsed = ust - drmmode_crtc->dpms_last_ust; + delta_seq = time_elapsed * drmmode_crtc->dpms_last_fps; + delta_seq /= 1000000; + drmmode_crtc->interpolated_vblanks += delta_seq; + + } + } + drmmode_crtc->dpms_mode = mode; +} + +/* TODO: currently this function only clear the front buffer to zero */ +/* Moving forward, we might to look into making the copy with glamor instead */ +void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode) +{ + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + uint32_t size = pScrn->displayWidth * info->pixel_bytes * pScrn->virtualY; + + /* memset the bo */ + if (info->gbm) { + void *cpu_ptr = malloc(size); + + if (cpu_ptr) { + memset(cpu_ptr, 0x00, size); + gbm_bo_write(info->front_buffer->bo.gbm, cpu_ptr, size); + free(cpu_ptr); + } + } else { + memset(info->front_buffer->cpu_ptr, 0x00, size); + } +} + +static Bool +drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, + Rotation rotation, int x, int y) +{ + ScrnInfoPtr pScrn = crtc->scrn; + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + int saved_x, saved_y; + Rotation saved_rotation; + DisplayModeRec saved_mode; + uint32_t *output_ids; + int output_count = 0; + Bool ret = TRUE; + int i; + int fb_id; + drmModeModeInfo kmode; + union gbm_bo_handle bo_handle; + + if (drmmode->fb_id == 0) { + if (info->gbm) { + bo_handle = gbm_bo_get_handle(info->front_buffer->bo.gbm); + } else { + if (amdgpu_bo_export(info->front_buffer->bo.amdgpu, + amdgpu_bo_handle_type_kms, + &bo_handle.u32)) { + ErrorF("failed to get BO handle for FB\n"); + return FALSE; + } + } + + ret = drmModeAddFB(drmmode->fd, + pScrn->virtualX, + pScrn->virtualY, + pScrn->depth, pScrn->bitsPerPixel, + pScrn->displayWidth * info->pixel_bytes, + bo_handle.u32, &drmmode->fb_id); + if (ret < 0) { + ErrorF("failed to add fb\n"); + return FALSE; + } + } + + saved_mode = crtc->mode; + saved_x = crtc->x; + saved_y = crtc->y; + saved_rotation = crtc->rotation; + + if (mode) { + crtc->mode = *mode; + crtc->x = x; + crtc->y = y; + crtc->rotation = rotation; + crtc->transformPresent = FALSE; + } + + output_ids = calloc(sizeof(uint32_t), xf86_config->num_output); + if (!output_ids) { + ret = FALSE; + goto done; + } + + if (mode) { + for (i = 0; i < xf86_config->num_output; i++) { + xf86OutputPtr output = xf86_config->output[i]; + drmmode_output_private_ptr drmmode_output; + + if (output->crtc != crtc) + continue; + + drmmode_output = output->driver_private; + output_ids[output_count] = + drmmode_output->mode_output->connector_id; + output_count++; + } + + if (!xf86CrtcRotate(crtc)) { + goto done; + } + crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green, + crtc->gamma_blue, crtc->gamma_size); + + drmmode_ConvertToKMode(crtc->scrn, &kmode, mode); + + fb_id = drmmode->fb_id; +#ifdef AMDGPU_PIXMAP_SHARING + if (crtc->randr_crtc && crtc->randr_crtc->scanout_pixmap) { + x = drmmode_crtc->scanout_pixmap_x; + y = 0; + } else +#endif + if (drmmode_crtc->rotate_fb_id) { + fb_id = drmmode_crtc->rotate_fb_id; + x = y = 0; + } + ret = + drmModeSetCrtc(drmmode->fd, + drmmode_crtc->mode_crtc->crtc_id, fb_id, x, + y, output_ids, output_count, &kmode); + if (ret) + xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, + "failed to set mode: %s", strerror(-ret)); + else + ret = TRUE; + + if (crtc->scrn->pScreen) + xf86CrtcSetScreenSubpixelOrder(crtc->scrn->pScreen); + /* go through all the outputs and force DPMS them back on? */ + for (i = 0; i < xf86_config->num_output; i++) { + xf86OutputPtr output = xf86_config->output[i]; + + if (output->crtc != crtc) + continue; + + output->funcs->dpms(output, DPMSModeOn); + } + } + + if (pScrn->pScreen && + !xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE)) + xf86_reload_cursors(pScrn->pScreen); + +done: + if (!ret) { + crtc->x = saved_x; + crtc->y = saved_y; + crtc->rotation = saved_rotation; + crtc->mode = saved_mode; + } +#if defined(XF86_CRTC_VERSION) && XF86_CRTC_VERSION >= 3 + else + crtc->active = TRUE; +#endif + + return ret; +} + +static void drmmode_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg) +{ + +} + +static void drmmode_set_cursor_position(xf86CrtcPtr crtc, int x, int y) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + + drmModeMoveCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, x, y); +} + +static void drmmode_load_cursor_argb(xf86CrtcPtr crtc, CARD32 * image) +{ + ScrnInfoPtr pScrn = crtc->scrn; + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + int i; + uint32_t cursor_size = info->cursor_w * info->cursor_h; + + if (info->gbm) { + uint32_t ptr[cursor_size]; + + for (i = 0; i < cursor_size; i++) + ptr[i] = cpu_to_le32(image[i]); + + gbm_bo_write(drmmode_crtc->cursor_buffer->bo.gbm, ptr, cursor_size * 4); + } else { + /* cursor should be mapped already */ + uint32_t *ptr = (uint32_t *) (drmmode_crtc->cursor_buffer->cpu_ptr); + + for (i = 0; i < cursor_size; i++) + ptr[i] = cpu_to_le32(image[i]); + } +} + +static void drmmode_hide_cursor(xf86CrtcPtr crtc) +{ + ScrnInfoPtr pScrn = crtc->scrn; + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + + drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, 0, + info->cursor_w, info->cursor_h); + +} + +static void drmmode_show_cursor(xf86CrtcPtr crtc) +{ + ScrnInfoPtr pScrn = crtc->scrn; + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + union gbm_bo_handle bo_handle; + + if (info->gbm) { + bo_handle = gbm_bo_get_handle(drmmode_crtc->cursor_buffer->bo.gbm); + } else { + if (amdgpu_bo_export(drmmode_crtc->cursor_buffer->bo.amdgpu, + amdgpu_bo_handle_type_kms, + &bo_handle.u32)) { + ErrorF("failed to get BO handle for cursor\n"); + return; + } + } + + drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, bo_handle.u32, + info->cursor_w, info->cursor_h); +} + +static void *drmmode_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, + int height) +{ + ScrnInfoPtr pScrn = crtc->scrn; + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + struct amdgpu_buffer *rotate_buffer = NULL; + int ret; + int pitch; + union gbm_bo_handle bo_handle; + + /* rotation requires acceleration */ + if (info->shadow_fb) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Rotation requires acceleration!\n"); + return NULL; + } + + rotate_buffer = amdgpu_alloc_pixmap_bo(pScrn, width, height, + pScrn->depth, 0, + pScrn->bitsPerPixel, &pitch); + if (!rotate_buffer) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to allocate rotation buffer memory\n"); + return NULL; + } + + bo_handle = gbm_bo_get_handle(rotate_buffer->bo.gbm); + ret = drmModeAddFB(drmmode->fd, width, height, crtc->scrn->depth, + crtc->scrn->bitsPerPixel, pitch, + bo_handle.u32, &drmmode_crtc->rotate_fb_id); + if (ret) { + ErrorF("failed to add rotate fb\n"); + } + + drmmode_crtc->rotate_buffer = rotate_buffer; + return rotate_buffer; +} + +static PixmapPtr +drmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height) +{ + ScrnInfoPtr pScrn = crtc->scrn; + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + unsigned long rotate_pitch; + PixmapPtr rotate_pixmap; + + if (!data) + data = drmmode_crtc_shadow_allocate(crtc, width, height); + + rotate_pitch = gbm_bo_get_stride(drmmode_crtc->rotate_buffer->bo.gbm); + + rotate_pixmap = drmmode_create_bo_pixmap(pScrn, + width, height, + pScrn->depth, + pScrn->bitsPerPixel, + rotate_pitch, + drmmode_crtc->rotate_buffer); + if (rotate_pixmap == NULL) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Couldn't allocate shadow pixmap for rotated CRTC\n"); + } + return rotate_pixmap; + +} + +static void +drmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, + void *data) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + + if (rotate_pixmap) + drmmode_destroy_bo_pixmap(rotate_pixmap); + + if (data) { + drmModeRmFB(drmmode->fd, drmmode_crtc->rotate_fb_id); + drmmode_crtc->rotate_fb_id = 0; + amdgpu_bo_unref(&drmmode_crtc->rotate_buffer); + drmmode_crtc->rotate_buffer = NULL; + } + +} + +static void +drmmode_crtc_gamma_set(xf86CrtcPtr crtc, uint16_t * red, uint16_t * green, + uint16_t * blue, int size) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + + drmModeCrtcSetGamma(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, + size, red, green, blue); +} + +#ifdef AMDGPU_PIXMAP_SHARING +static Bool drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix) +{ + ScreenPtr screen = xf86ScrnToScreen(crtc->scrn); + PixmapPtr screenpix = screen->GetScreenPixmap(screen); + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + int c, total_width = 0, max_height = 0, this_x = 0; + + if (!ppix) { + if (crtc->randr_crtc->scanout_pixmap) + PixmapStopDirtyTracking(crtc-> + randr_crtc->scanout_pixmap, + screenpix); + drmmode_crtc->scanout_pixmap_x = 0; + return TRUE; + } + + /* iterate over all the attached crtcs - + work out bounding box */ + for (c = 0; c < xf86_config->num_crtc; c++) { + xf86CrtcPtr iter = xf86_config->crtc[c]; + if (!iter->enabled && iter != crtc) + continue; + if (iter == crtc) { + this_x = total_width; + total_width += ppix->drawable.width; + if (max_height < ppix->drawable.height) + max_height = ppix->drawable.height; + } else { + total_width += iter->mode.HDisplay; + if (max_height < iter->mode.VDisplay) + max_height = iter->mode.VDisplay; + } +#ifndef HAS_DIRTYTRACKING2 + if (iter != crtc) { + ErrorF + ("Cannot do multiple crtcs without X server dirty tracking 2 interface\n"); + return FALSE; + } +#endif + } + + if (total_width != screenpix->drawable.width || + max_height != screenpix->drawable.height) { + Bool ret; + ret = + drmmode_xf86crtc_resize(crtc->scrn, total_width, + max_height); + if (ret == FALSE) + return FALSE; + + screenpix = screen->GetScreenPixmap(screen); + screen->width = screenpix->drawable.width = total_width; + screen->height = screenpix->drawable.height = max_height; + } + drmmode_crtc->scanout_pixmap_x = this_x; +#ifdef HAS_DIRTYTRACKING2 + PixmapStartDirtyTracking2(ppix, screenpix, 0, 0, this_x, 0); +#else + PixmapStartDirtyTracking(ppix, screenpix, 0, 0); +#endif + return TRUE; +} +#endif + +static const xf86CrtcFuncsRec drmmode_crtc_funcs = { + .dpms = drmmode_crtc_dpms, + .set_mode_major = drmmode_set_mode_major, + .set_cursor_colors = drmmode_set_cursor_colors, + .set_cursor_position = drmmode_set_cursor_position, + .show_cursor = drmmode_show_cursor, + .hide_cursor = drmmode_hide_cursor, + .load_cursor_argb = drmmode_load_cursor_argb, + + .gamma_set = drmmode_crtc_gamma_set, + .shadow_create = drmmode_crtc_shadow_create, + .shadow_allocate = drmmode_crtc_shadow_allocate, + .shadow_destroy = drmmode_crtc_shadow_destroy, + .destroy = NULL, /* XXX */ +#ifdef AMDGPU_PIXMAP_SHARING + .set_scanout_pixmap = drmmode_set_scanout_pixmap, +#endif +}; + +int drmmode_get_crtc_id(xf86CrtcPtr crtc) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + return drmmode_crtc->hw_id; +} + +void drmmode_crtc_hw_id(xf86CrtcPtr crtc) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + ScrnInfoPtr pScrn = crtc->scrn; + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + struct drm_amdgpu_info ginfo; + int r; + + memset(&ginfo, 0, sizeof(ginfo)); + ginfo.return_pointer = (uintptr_t)&drmmode_crtc->hw_id; + ginfo.return_size = sizeof(drmmode_crtc->hw_id); + ginfo.query = AMDGPU_INFO_CRTC_FROM_ID; + ginfo.mode_crtc.id = drmmode_crtc->mode_crtc->crtc_id; + r = drmCommandWriteRead(info->dri2.drm_fd, DRM_AMDGPU_INFO, &ginfo, + sizeof(ginfo)); + if (r) + drmmode_crtc->hw_id = -1; +} + +static void drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num) +{ + xf86CrtcPtr crtc; + drmmode_crtc_private_ptr drmmode_crtc; + + crtc = xf86CrtcCreate(pScrn, &drmmode_crtc_funcs); + if (crtc == NULL) + return; + + drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1); + drmmode_crtc->mode_crtc = + drmModeGetCrtc(drmmode->fd, drmmode->mode_res->crtcs[num]); + drmmode_crtc->drmmode = drmmode; + crtc->driver_private = drmmode_crtc; + drmmode_crtc_hw_id(crtc); + + return; +} + +static xf86OutputStatus drmmode_output_detect(xf86OutputPtr output) +{ + /* go to the hw and retrieve a new output struct */ + drmmode_output_private_ptr drmmode_output = output->driver_private; + drmmode_ptr drmmode = drmmode_output->drmmode; + xf86OutputStatus status; + drmModeFreeConnector(drmmode_output->mode_output); + + drmmode_output->mode_output = + drmModeGetConnector(drmmode->fd, drmmode_output->output_id); + if (!drmmode_output->mode_output) + return XF86OutputStatusDisconnected; + + switch (drmmode_output->mode_output->connection) { + case DRM_MODE_CONNECTED: + status = XF86OutputStatusConnected; + break; + case DRM_MODE_DISCONNECTED: + status = XF86OutputStatusDisconnected; + break; + default: + case DRM_MODE_UNKNOWNCONNECTION: + status = XF86OutputStatusUnknown; + break; + } + return status; +} + +static Bool +drmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes) +{ + return MODE_OK; +} + +static DisplayModePtr drmmode_output_get_modes(xf86OutputPtr output) +{ + drmmode_output_private_ptr drmmode_output = output->driver_private; + drmModeConnectorPtr koutput = drmmode_output->mode_output; + drmmode_ptr drmmode = drmmode_output->drmmode; + int i; + DisplayModePtr Modes = NULL, Mode; + drmModePropertyPtr props; + xf86MonPtr mon = NULL; + + if (!koutput) + return NULL; + + /* look for an EDID property */ + for (i = 0; i < koutput->count_props; i++) { + props = drmModeGetProperty(drmmode->fd, koutput->props[i]); + if (props && (props->flags & DRM_MODE_PROP_BLOB)) { + if (!strcmp(props->name, "EDID")) { + if (drmmode_output->edid_blob) + drmModeFreePropertyBlob + (drmmode_output->edid_blob); + drmmode_output->edid_blob = + drmModeGetPropertyBlob(drmmode->fd, + koutput->prop_values + [i]); + } + } + if (props) + drmModeFreeProperty(props); + } + + if (drmmode_output->edid_blob) { + mon = xf86InterpretEDID(output->scrn->scrnIndex, + drmmode_output->edid_blob->data); + if (mon && drmmode_output->edid_blob->length > 128) + mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA; + } + xf86OutputSetEDID(output, mon); + + /* modes should already be available */ + for (i = 0; i < koutput->count_modes; i++) { + Mode = xnfalloc(sizeof(DisplayModeRec)); + + drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i], + Mode); + Modes = xf86ModesAdd(Modes, Mode); + + } + return Modes; +} + +static void drmmode_output_destroy(xf86OutputPtr output) +{ + drmmode_output_private_ptr drmmode_output = output->driver_private; + int i; + + if (drmmode_output->edid_blob) + drmModeFreePropertyBlob(drmmode_output->edid_blob); + for (i = 0; i < drmmode_output->num_props; i++) { + drmModeFreeProperty(drmmode_output->props[i].mode_prop); + free(drmmode_output->props[i].atoms); + } + for (i = 0; i < drmmode_output->mode_output->count_encoders; i++) { + drmModeFreeEncoder(drmmode_output->mode_encoders[i]); + free(drmmode_output->mode_encoders); + } + free(drmmode_output->props); + drmModeFreeConnector(drmmode_output->mode_output); + free(drmmode_output); + output->driver_private = NULL; +} + +static void drmmode_output_dpms(xf86OutputPtr output, int mode) +{ + drmmode_output_private_ptr drmmode_output = output->driver_private; + drmModeConnectorPtr koutput = drmmode_output->mode_output; + drmmode_ptr drmmode = drmmode_output->drmmode; + + drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id, + drmmode_output->dpms_enum_id, mode); + return; +} + +static Bool drmmode_property_ignore(drmModePropertyPtr prop) +{ + if (!prop) + return TRUE; + /* ignore blob prop */ + if (prop->flags & DRM_MODE_PROP_BLOB) + return TRUE; + /* ignore standard property */ + if (!strcmp(prop->name, "EDID") || !strcmp(prop->name, "DPMS")) + return TRUE; + + return FALSE; +} + +static void drmmode_output_create_resources(xf86OutputPtr output) +{ + drmmode_output_private_ptr drmmode_output = output->driver_private; + drmModeConnectorPtr mode_output = drmmode_output->mode_output; + drmmode_ptr drmmode = drmmode_output->drmmode; + drmModePropertyPtr drmmode_prop; + int i, j, err; + + drmmode_output->props = + calloc(mode_output->count_props, sizeof(drmmode_prop_rec)); + if (!drmmode_output->props) + return; + + drmmode_output->num_props = 0; + for (i = 0, j = 0; i < mode_output->count_props; i++) { + drmmode_prop = + drmModeGetProperty(drmmode->fd, mode_output->props[i]); + if (drmmode_property_ignore(drmmode_prop)) { + drmModeFreeProperty(drmmode_prop); + continue; + } + drmmode_output->props[j].mode_prop = drmmode_prop; + drmmode_output->props[j].value = mode_output->prop_values[i]; + drmmode_output->num_props++; + j++; + } + + for (i = 0; i < drmmode_output->num_props; i++) { + drmmode_prop_ptr p = &drmmode_output->props[i]; + drmmode_prop = p->mode_prop; + + if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) { + INT32 range[2]; + INT32 value = p->value; + + p->num_atoms = 1; + p->atoms = calloc(p->num_atoms, sizeof(Atom)); + if (!p->atoms) + continue; + p->atoms[0] = + MakeAtom(drmmode_prop->name, + strlen(drmmode_prop->name), TRUE); + range[0] = drmmode_prop->values[0]; + range[1] = drmmode_prop->values[1]; + err = + RRConfigureOutputProperty(output->randr_output, + p->atoms[0], FALSE, TRUE, + drmmode_prop->flags & + DRM_MODE_PROP_IMMUTABLE ? + TRUE : FALSE, 2, range); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRConfigureOutputProperty error, %d\n", + err); + } + err = + RRChangeOutputProperty(output->randr_output, + p->atoms[0], XA_INTEGER, 32, + PropModeReplace, 1, &value, + FALSE, TRUE); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRChangeOutputProperty error, %d\n", + err); + } + } else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) { + p->num_atoms = drmmode_prop->count_enums + 1; + p->atoms = calloc(p->num_atoms, sizeof(Atom)); + if (!p->atoms) + continue; + p->atoms[0] = + MakeAtom(drmmode_prop->name, + strlen(drmmode_prop->name), TRUE); + for (j = 1; j <= drmmode_prop->count_enums; j++) { + struct drm_mode_property_enum *e = + &drmmode_prop->enums[j - 1]; + p->atoms[j] = + MakeAtom(e->name, strlen(e->name), TRUE); + } + err = + RRConfigureOutputProperty(output->randr_output, + p->atoms[0], FALSE, FALSE, + drmmode_prop->flags & + DRM_MODE_PROP_IMMUTABLE ? + TRUE : FALSE, + p->num_atoms - 1, + (INT32 *) & p->atoms[1]); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRConfigureOutputProperty error, %d\n", + err); + } + for (j = 0; j < drmmode_prop->count_enums; j++) + if (drmmode_prop->enums[j].value == p->value) + break; + /* there's always a matching value */ + err = + RRChangeOutputProperty(output->randr_output, + p->atoms[0], XA_ATOM, 32, + PropModeReplace, 1, + &p->atoms[j + 1], FALSE, + TRUE); + if (err != 0) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "RRChangeOutputProperty error, %d\n", + err); + } + } + } +} + +static Bool +drmmode_output_set_property(xf86OutputPtr output, Atom property, + RRPropertyValuePtr value) +{ + drmmode_output_private_ptr drmmode_output = output->driver_private; + drmmode_ptr drmmode = drmmode_output->drmmode; + int i; + + for (i = 0; i < drmmode_output->num_props; i++) { + drmmode_prop_ptr p = &drmmode_output->props[i]; + + if (p->atoms[0] != property) + continue; + + if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) { + uint32_t val; + + if (value->type != XA_INTEGER || value->format != 32 || + value->size != 1) + return FALSE; + val = *(uint32_t *) value->data; + + drmModeConnectorSetProperty(drmmode->fd, + drmmode_output->output_id, + p->mode_prop->prop_id, + (uint64_t) val); + return TRUE; + } else if (p->mode_prop->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, 4); + name = NameForAtom(atom); + + /* search for matching name string, then set its value down */ + for (j = 0; j < p->mode_prop->count_enums; j++) { + if (!strcmp(p->mode_prop->enums[j].name, name)) { + drmModeConnectorSetProperty(drmmode->fd, + drmmode_output->output_id, + p->mode_prop->prop_id, + p->mode_prop->enums + [j].value); + return TRUE; + } + } + } + } + + return TRUE; +} + +static Bool drmmode_output_get_property(xf86OutputPtr output, Atom property) +{ + return TRUE; +} + +static const xf86OutputFuncsRec drmmode_output_funcs = { + .dpms = drmmode_output_dpms, + .create_resources = drmmode_output_create_resources, + .set_property = drmmode_output_set_property, + .get_property = drmmode_output_get_property, +#if 0 + + .save = drmmode_crt_save, + .restore = drmmode_crt_restore, + .mode_fixup = drmmode_crt_mode_fixup, + .prepare = drmmode_output_prepare, + .mode_set = drmmode_crt_mode_set, + .commit = drmmode_output_commit, +#endif + .detect = drmmode_output_detect, + .mode_valid = drmmode_output_mode_valid, + + .get_modes = drmmode_output_get_modes, + .destroy = drmmode_output_destroy +}; + +static int subpixel_conv_table[7] = { 0, SubPixelUnknown, + SubPixelHorizontalRGB, + SubPixelHorizontalBGR, + SubPixelVerticalRGB, + SubPixelVerticalBGR, + SubPixelNone +}; + +const char *output_names[] = { "None", + "VGA", + "DVI-I", + "DVI-D", + "DVI-A", + "Composite", + "S-video", + "LVDS", + "CTV", + "DIN", + "DisplayPort", + "HDMI-A", + "HDMI-B", + "TV", + "eDP", + "Virtual", + "DSI", +}; + +#define NUM_OUTPUT_NAMES (sizeof(output_names) / sizeof(output_names[0])) + +static void +drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num) +{ + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + xf86OutputPtr output; + drmModeConnectorPtr koutput; + drmModeEncoderPtr *kencoders = NULL; + drmmode_output_private_ptr drmmode_output; + drmModePropertyPtr props; + char name[32]; + int i; + const char *s; + + koutput = + drmModeGetConnector(drmmode->fd, + drmmode->mode_res->connectors[num]); + if (!koutput) + return; + + kencoders = calloc(sizeof(drmModeEncoderPtr), koutput->count_encoders); + if (!kencoders) { + goto out_free_encoders; + } + + for (i = 0; i < koutput->count_encoders; i++) { + kencoders[i] = + drmModeGetEncoder(drmmode->fd, koutput->encoders[i]); + if (!kencoders[i]) { + goto out_free_encoders; + } + } + + if (koutput->connector_type >= NUM_OUTPUT_NAMES) + snprintf(name, 32, "Unknown%d-%d", koutput->connector_type, + koutput->connector_type_id - 1); +#ifdef AMDGPU_PIXMAP_SHARING + else if (pScrn->is_gpu) + snprintf(name, 32, "%s-%d-%d", + output_names[koutput->connector_type], + pScrn->scrnIndex - GPU_SCREEN_OFFSET + 1, + koutput->connector_type_id - 1); +#endif + else + snprintf(name, 32, "%s-%d", + output_names[koutput->connector_type], + koutput->connector_type_id - 1); + + if (xf86IsEntityShared(pScrn->entityList[0])) { + if ((s = + xf86GetOptValString(info->Options, OPTION_ZAPHOD_HEADS))) { + if (!AMDGPUZaphodStringMatches(pScrn, s, name)) + goto out_free_encoders; + } else { + if (info->IsPrimary && (num != 0)) + goto out_free_encoders; + else if (info->IsSecondary && (num != 1)) + goto out_free_encoders; + } + } + + output = xf86OutputCreate(pScrn, &drmmode_output_funcs, name); + if (!output) { + goto out_free_encoders; + } + + drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1); + if (!drmmode_output) { + xf86OutputDestroy(output); + goto out_free_encoders; + } + + drmmode_output->output_id = drmmode->mode_res->connectors[num]; + drmmode_output->mode_output = koutput; + drmmode_output->mode_encoders = kencoders; + drmmode_output->drmmode = drmmode; + output->mm_width = koutput->mmWidth; + output->mm_height = koutput->mmHeight; + + output->subpixel_order = subpixel_conv_table[koutput->subpixel]; + output->interlaceAllowed = TRUE; + output->doubleScanAllowed = TRUE; + output->driver_private = drmmode_output; + + output->possible_crtcs = 0xffffffff; + for (i = 0; i < koutput->count_encoders; i++) { + output->possible_crtcs &= kencoders[i]->possible_crtcs; + } + /* work out the possible clones later */ + output->possible_clones = 0; + + for (i = 0; i < koutput->count_props; i++) { + props = drmModeGetProperty(drmmode->fd, koutput->props[i]); + if (props && (props->flags & DRM_MODE_PROP_ENUM)) { + if (!strcmp(props->name, "DPMS")) { + drmmode_output->dpms_enum_id = + koutput->props[i]; + drmModeFreeProperty(props); + break; + } + drmModeFreeProperty(props); + } + } + + return; +out_free_encoders: + if (kencoders) { + for (i = 0; i < koutput->count_encoders; i++) + drmModeFreeEncoder(kencoders[i]); + free(kencoders); + } + drmModeFreeConnector(koutput); + +} + +uint32_t find_clones(ScrnInfoPtr scrn, xf86OutputPtr output) +{ + drmmode_output_private_ptr drmmode_output = + output->driver_private, clone_drmout; + int i; + xf86OutputPtr clone_output; + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + int index_mask = 0; + + if (drmmode_output->enc_clone_mask == 0) + return index_mask; + + for (i = 0; i < xf86_config->num_output; i++) { + clone_output = xf86_config->output[i]; + clone_drmout = clone_output->driver_private; + if (output == clone_output) + continue; + + if (clone_drmout->enc_mask == 0) + continue; + if (drmmode_output->enc_clone_mask == clone_drmout->enc_mask) + index_mask |= (1 << i); + } + return index_mask; +} + +static void drmmode_clones_init(ScrnInfoPtr scrn, drmmode_ptr drmmode) +{ + int i, j; + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + + for (i = 0; i < xf86_config->num_output; i++) { + xf86OutputPtr output = xf86_config->output[i]; + drmmode_output_private_ptr drmmode_output; + + drmmode_output = output->driver_private; + drmmode_output->enc_clone_mask = 0xff; + /* and all the possible encoder clones for this output together */ + for (j = 0; j < drmmode_output->mode_output->count_encoders; + j++) { + int k; + for (k = 0; k < drmmode->mode_res->count_encoders; k++) { + if (drmmode->mode_res->encoders[k] == + drmmode_output-> + mode_encoders[j]->encoder_id) + drmmode_output->enc_mask |= (1 << k); + } + + drmmode_output->enc_clone_mask &= + drmmode_output->mode_encoders[j]->possible_clones; + } + } + + for (i = 0; i < xf86_config->num_output; i++) { + xf86OutputPtr output = xf86_config->output[i]; + output->possible_clones = find_clones(scrn, output); + } +} + +/* returns pitch alignment in pixels */ +int drmmode_get_pitch_align(ScrnInfoPtr scrn, int bpe) +{ + AMDGPUInfoPtr info = AMDGPUPTR(scrn); + + if (info->have_tiling_info) + /* linear aligned requirements */ + return MAX(64, info->group_bytes / bpe); + else + /* default to 512 elements if we don't know the real + * group size otherwise the kernel may reject the CS + * if the group sizes don't match as the pitch won't + * be aligned properly. + */ + return 512; +} + +static Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height) +{ + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + drmmode_crtc_private_ptr + drmmode_crtc = xf86_config->crtc[0]->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + AMDGPUInfoPtr info = AMDGPUPTR(scrn); + struct amdgpu_buffer *old_front = NULL; + Bool ret; + ScreenPtr screen = xf86ScrnToScreen(scrn); + uint32_t old_fb_id; + int i, pitch, old_width, old_height, old_pitch; + int cpp = info->pixel_bytes; + PixmapPtr ppix = screen->GetScreenPixmap(screen); + void *fb_shadow; + int hint = info->use_glamor ? 0 : AMDGPU_CREATE_PIXMAP_LINEAR; + + if (scrn->virtualX == width && scrn->virtualY == height) + return TRUE; + + xf86DrvMsg(scrn->scrnIndex, X_INFO, + "Allocate new frame buffer %dx%d\n", width, height); + + old_width = scrn->virtualX; + old_height = scrn->virtualY; + old_pitch = scrn->displayWidth; + old_fb_id = drmmode->fb_id; + old_front = info->front_buffer; + + scrn->virtualX = width; + scrn->virtualY = height; + + info->front_buffer = + amdgpu_alloc_pixmap_bo(scrn, scrn->virtualX, scrn->virtualY, + scrn->depth, hint, scrn->bitsPerPixel, + &pitch); + if (!info->front_buffer) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Failed to allocate front buffer memory\n"); + goto fail; + } + + if (amdgpu_bo_map(scrn, info->front_buffer)) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Failed to map front buffer memory\n"); + goto fail; + } + + xf86DrvMsg(scrn->scrnIndex, X_INFO, " => pitch %d bytes\n", pitch); + scrn->displayWidth = pitch / cpp; + + if (info->front_buffer->flags & AMDGPU_BO_FLAGS_GBM) { + union gbm_bo_handle bo_handle; + + bo_handle = gbm_bo_get_handle(info->front_buffer->bo.gbm); + ret = drmModeAddFB(drmmode->fd, width, height, scrn->depth, + scrn->bitsPerPixel, pitch, + bo_handle.u32, &drmmode->fb_id); + if (ret) { + goto fail; + } + + amdgpu_set_pixmap_bo(ppix, info->front_buffer); + screen->ModifyPixmapHeader(ppix, + width, height, -1, -1, pitch, info->front_buffer->cpu_ptr); + } else { + uint32_t bo_handle; + + if (amdgpu_bo_export(info->front_buffer->bo.amdgpu, + amdgpu_bo_handle_type_kms, + &bo_handle)) { + goto fail; + } + ret = drmModeAddFB(drmmode->fd, width, height, scrn->depth, + scrn->bitsPerPixel, pitch, + bo_handle, &drmmode->fb_id); + if (ret) { + goto fail; + } + + fb_shadow = calloc(1, pitch * scrn->virtualY); + if (fb_shadow == NULL) + goto fail; + free(info->fb_shadow); + info->fb_shadow = fb_shadow; + screen->ModifyPixmapHeader(ppix, + width, height, -1, -1, pitch, + info->fb_shadow); + } +#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,9,99,1,0) + scrn->pixmapPrivate.ptr = ppix->devPrivate.ptr; +#endif + + for (i = 0; i < xf86_config->num_crtc; i++) { + xf86CrtcPtr crtc = xf86_config->crtc[i]; + + if (!crtc->enabled) + continue; + + drmmode_set_mode_major(crtc, &crtc->mode, + crtc->rotation, crtc->x, crtc->y); + } + + if (info->use_glamor) + amdgpu_glamor_create_screen_resources(scrn->pScreen); + + if (old_fb_id) + drmModeRmFB(drmmode->fd, old_fb_id); + if (old_front) { + amdgpu_bo_unref(&old_front); + } + + return TRUE; + +fail: + if (info->front_buffer) { + amdgpu_bo_unref(&info->front_buffer); + } + info->front_buffer = old_front; + scrn->virtualX = old_width; + scrn->virtualY = old_height; + scrn->displayWidth = old_pitch; + drmmode->fb_id = old_fb_id; + + return FALSE; +} + +static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = { + drmmode_xf86crtc_resize +}; + +static void +drmmode_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + amdgpu_dri2_frame_event_handler(frame, tv_sec, tv_usec, event_data); +} + +static void +drmmode_flip_handler(int fd, unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + drmmode_flipevtcarrier_ptr flipcarrier = event_data; + drmmode_flipdata_ptr flipdata = flipcarrier->flipdata; + drmmode_ptr drmmode = flipdata->drmmode; + + /* Is this the event whose info shall be delivered to higher level? */ + if (flipcarrier->dispatch_me) { + /* Yes: Cache msc, ust for later delivery. */ + flipdata->fe_frame = frame; + flipdata->fe_tv_sec = tv_sec; + flipdata->fe_tv_usec = tv_usec; + } + free(flipcarrier); + + /* Last crtc completed flip? */ + flipdata->flip_count--; + if (flipdata->flip_count > 0) + return; + + /* Release framebuffer */ + drmModeRmFB(drmmode->fd, flipdata->old_fb_id); + + if (flipdata->event_data == NULL) + return; + + /* Deliver cached msc, ust from reference crtc to flip event handler */ + amdgpu_dri2_flip_event_handler(flipdata->fe_frame, flipdata->fe_tv_sec, + flipdata->fe_tv_usec, + flipdata->event_data); + + free(flipdata); +} + +static void drm_wakeup_handler(pointer data, int err, pointer p) +{ + drmmode_ptr drmmode = data; + fd_set *read_mask = p; + + if (err >= 0 && FD_ISSET(drmmode->fd, read_mask)) { + drmHandleEvent(drmmode->fd, &drmmode->event_context); + } +} + +Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp) +{ + int i; + + xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs); + + drmmode->scrn = pScrn; + drmmode->cpp = cpp; + drmmode->mode_res = drmModeGetResources(drmmode->fd); + if (!drmmode->mode_res) + return FALSE; + + xf86CrtcSetSizeRange(pScrn, 320, 200, drmmode->mode_res->max_width, + drmmode->mode_res->max_height); + for (i = 0; i < drmmode->mode_res->count_crtcs; i++) + if (!xf86IsEntityShared(pScrn->entityList[0]) + || pScrn->confScreen->device->screen == i) + drmmode_crtc_init(pScrn, drmmode, i); + + for (i = 0; i < drmmode->mode_res->count_connectors; i++) + drmmode_output_init(pScrn, drmmode, i); + + /* workout clones */ + drmmode_clones_init(pScrn, drmmode); + +#ifdef AMDGPU_PIXMAP_SHARING + xf86ProviderSetup(pScrn, NULL, "amdgpu"); +#endif + + xf86InitialConfiguration(pScrn, TRUE); + + drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION; + drmmode->event_context.vblank_handler = drmmode_vblank_handler; + drmmode->event_context.page_flip_handler = drmmode_flip_handler; + + return TRUE; +} + +void drmmode_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode) +{ + AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + + info->drmmode_inited = TRUE; + if (pAMDGPUEnt->fd_wakeup_registered != serverGeneration) { + AddGeneralSocket(drmmode->fd); + RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr) NoopDDA, + drm_wakeup_handler, drmmode); + pAMDGPUEnt->fd_wakeup_registered = serverGeneration; + pAMDGPUEnt->fd_wakeup_ref = 1; + } else + pAMDGPUEnt->fd_wakeup_ref++; +} + +void drmmode_fini(ScrnInfoPtr pScrn, drmmode_ptr drmmode) +{ + AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn); + AMDGPUInfoPtr info = AMDGPUPTR(pScrn); + + if (!info->drmmode_inited) + return; + + if (pAMDGPUEnt->fd_wakeup_registered == serverGeneration && + !--pAMDGPUEnt->fd_wakeup_ref) { + RemoveGeneralSocket(drmmode->fd); + RemoveBlockAndWakeupHandlers((BlockHandlerProcPtr) NoopDDA, + drm_wakeup_handler, drmmode); + } +} + +void drmmode_set_cursor(ScrnInfoPtr scrn, drmmode_ptr drmmode, int id, + struct amdgpu_buffer *bo) +{ + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + xf86CrtcPtr crtc = xf86_config->crtc[id]; + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + + drmmode_crtc->cursor_buffer = bo; +} + +void drmmode_adjust_frame(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int x, int y) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn); + xf86OutputPtr output = config->output[config->compat_output]; + xf86CrtcPtr crtc = output->crtc; + + if (crtc && crtc->enabled) { + drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation, x, y); + } +} + +Bool drmmode_set_desired_modes(ScrnInfoPtr pScrn, drmmode_ptr drmmode) +{ + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn); + int c; + + drmmode_copy_fb(pScrn, drmmode); + + for (c = 0; c < config->num_crtc; c++) { + xf86CrtcPtr crtc = config->crtc[c]; + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + xf86OutputPtr output = NULL; + int o; + + /* Skip disabled CRTCs */ + if (!crtc->enabled) { + drmModeSetCrtc(drmmode->fd, + drmmode_crtc->mode_crtc->crtc_id, 0, 0, + 0, NULL, 0, NULL); + continue; + } + + if (config->output[config->compat_output]->crtc == crtc) + output = config->output[config->compat_output]; + else { + for (o = 0; o < config->num_output; o++) + if (config->output[o]->crtc == crtc) { + output = config->output[o]; + break; + } + } + /* paranoia */ + if (!output) + continue; + + /* Mark that we'll need to re-set the mode for sure */ + memset(&crtc->mode, 0, sizeof(crtc->mode)); + if (!crtc->desiredMode.CrtcHDisplay) { + DisplayModePtr mode = xf86OutputFindClosestMode(output, + pScrn-> + currentMode); + + if (!mode) + return FALSE; + crtc->desiredMode = *mode; + crtc->desiredRotation = RR_Rotate_0; + crtc->desiredX = 0; + crtc->desiredY = 0; + } + + if (!crtc->funcs->set_mode_major(crtc, &crtc->desiredMode, + crtc->desiredRotation, + crtc->desiredX, + crtc->desiredY)) + return FALSE; + } + return TRUE; +} + +static void drmmode_load_palette(ScrnInfoPtr pScrn, int numColors, + 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 index, j, i; + int c; + + for (c = 0; c < xf86_config->num_crtc; c++) { + xf86CrtcPtr crtc = xf86_config->crtc[c]; + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + + for (i = 0; i < 256; i++) { + lut_r[i] = drmmode_crtc->lut_r[i] << 6; + lut_g[i] = drmmode_crtc->lut_g[i] << 6; + lut_b[i] = drmmode_crtc->lut_b[i] << 6; + } + + switch (pScrn->depth) { + case 15: + for (i = 0; i < numColors; i++) { + index = indices[i]; + for (j = 0; j < 8; j++) { + lut_r[index * 8 + j] = + colors[index].red << 6; + lut_g[index * 8 + j] = + colors[index].green << 6; + lut_b[index * 8 + j] = + colors[index].blue << 6; + } + } + break; + case 16: + for (i = 0; i < numColors; i++) { + index = indices[i]; + + if (i <= 31) { + for (j = 0; j < 8; j++) { + lut_r[index * 8 + j] = + colors[index].red << 6; + lut_b[index * 8 + j] = + colors[index].blue << 6; + } + } + + for (j = 0; j < 4; j++) { + lut_g[index * 4 + j] = + colors[index].green << 6; + } + } + break; + default: + for (i = 0; i < numColors; i++) { + index = indices[i]; + lut_r[index] = colors[index].red << 6; + lut_g[index] = colors[index].green << 6; + lut_b[index] = colors[index].blue << 6; + } + break; + } + + /* Make the change through RandR */ + if (crtc->randr_crtc) + RRCrtcGammaSet(crtc->randr_crtc, lut_r, lut_g, lut_b); + else + crtc->funcs->gamma_set(crtc, lut_r, lut_g, lut_b, 256); + } +} + +Bool drmmode_setup_colormap(ScreenPtr pScreen, ScrnInfoPtr pScrn) +{ + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn); + + if (xf86_config->num_crtc) { + xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, AMDGPU_LOGLEVEL_DEBUG, + "Initializing kms color map\n"); + if (!miCreateDefColormap(pScreen)) + return FALSE; + /* all amdgpus support 10 bit CLUTs */ + if (!xf86HandleColormaps(pScreen, 256, 10, + drmmode_load_palette, NULL, + CMAP_PALETTED_TRUECOLOR +#if 0 /* This option messes up text mode! (eich@suse.de) */ + | CMAP_LOAD_EVEN_IF_OFFSCREEN +#endif + | CMAP_RELOAD_ON_MODE_SWITCH)) + return FALSE; + } + return TRUE; +} + +#ifdef HAVE_LIBUDEV +static void drmmode_handle_uevents(int fd, void *closure) +{ + drmmode_ptr drmmode = closure; + ScrnInfoPtr scrn = drmmode->scrn; + struct udev_device *dev; + dev = udev_monitor_receive_device(drmmode->uevent_monitor); + if (!dev) + return; + + RRGetInfo(xf86ScrnToScreen(scrn), TRUE); + udev_device_unref(dev); +} +#endif + +void drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode) +{ +#ifdef HAVE_LIBUDEV + struct udev *u; + struct udev_monitor *mon; + + u = udev_new(); + if (!u) + return; + mon = udev_monitor_new_from_netlink(u, "udev"); + if (!mon) { + udev_unref(u); + return; + } + + if (udev_monitor_filter_add_match_subsystem_devtype(mon, + "drm", + "drm_minor") < 0 || + udev_monitor_enable_receiving(mon) < 0) { + udev_monitor_unref(mon); + udev_unref(u); + return; + } + + drmmode->uevent_handler = + xf86AddGeneralHandler(udev_monitor_get_fd(mon), + drmmode_handle_uevents, drmmode); + + drmmode->uevent_monitor = mon; +#endif +} + +void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode) +{ +#ifdef HAVE_LIBUDEV + if (drmmode->uevent_handler) { + struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor); + xf86RemoveGeneralHandler(drmmode->uevent_handler); + + udev_monitor_unref(drmmode->uevent_monitor); + udev_unref(u); + } +#endif +} + +Bool amdgpu_do_pageflip(ScrnInfoPtr scrn, struct amdgpu_buffer *new_front, + void *data, int ref_crtc_hw_id) +{ + AMDGPUInfoPtr info = AMDGPUPTR(scrn); + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); + drmmode_crtc_private_ptr drmmode_crtc = config->crtc[0]->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + unsigned int pitch; + int i, old_fb_id; + int height, emitted = 0; + drmmode_flipdata_ptr flipdata; + drmmode_flipevtcarrier_ptr flipcarrier; + union gbm_bo_handle bo_handle; + uint32_t handle; + + if (info->front_buffer->flags & AMDGPU_BO_FLAGS_GBM) { + pitch = gbm_bo_get_stride(info->front_buffer->bo.gbm); + height = gbm_bo_get_height(info->front_buffer->bo.gbm); + bo_handle = gbm_bo_get_handle(new_front->bo.gbm); + handle = bo_handle.u32; + } else { + pitch = scrn->displayWidth; + height = scrn->virtualY; + if (amdgpu_bo_export(new_front->bo.amdgpu, + amdgpu_bo_handle_type_kms, + &handle)) + goto error_out; + } + + /* + * Create a new handle for the back buffer + */ + old_fb_id = drmmode->fb_id; + + if (drmModeAddFB(drmmode->fd, scrn->virtualX, height, + scrn->depth, scrn->bitsPerPixel, pitch, + handle, &drmmode->fb_id)) { + goto error_out; + } + flipdata = calloc(1, sizeof(drmmode_flipdata_rec)); + if (!flipdata) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "flip queue: data alloc failed.\n"); + goto error_undo; + } + /* + * Queue flips on all enabled CRTCs + * Note that if/when we get per-CRTC buffers, we'll have to update this. + * Right now it assumes a single shared fb across all CRTCs, with the + * kernel fixing up the offset of each CRTC as necessary. + * + * Also, flips queued on disabled or incorrectly configured displays + * may never complete; this is a configuration error. + */ + + flipdata->event_data = data; + flipdata->drmmode = drmmode; + + for (i = 0; i < config->num_crtc; i++) { + if (!config->crtc[i]->enabled) + continue; + + flipdata->flip_count++; + drmmode_crtc = config->crtc[i]->driver_private; + + flipcarrier = calloc(1, sizeof(drmmode_flipevtcarrier_rec)); + if (!flipcarrier) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "flip queue: carrier alloc failed.\n"); + if (emitted == 0) + free(flipdata); + goto error_undo; + } + + /* Only the reference crtc will finally deliver its page flip + * completion event. All other crtc's events will be discarded. + */ + flipcarrier->dispatch_me = + (drmmode_crtc->hw_id == ref_crtc_hw_id); + flipcarrier->flipdata = flipdata; + + if (drmModePageFlip + (drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, + drmmode->fb_id, DRM_MODE_PAGE_FLIP_EVENT, flipcarrier)) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "flip queue failed: %s\n", strerror(errno)); + free(flipcarrier); + if (emitted == 0) + free(flipdata); + goto error_undo; + } + emitted++; + } + + flipdata->old_fb_id = old_fb_id; + return TRUE; + +error_undo: + drmModeRmFB(drmmode->fd, drmmode->fb_id); + drmmode->fb_id = old_fb_id; + +error_out: + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n", + strerror(errno)); + return FALSE; +} diff --git a/src/drmmode_display.h b/src/drmmode_display.h new file mode 100644 index 0000000..c2ec683 --- /dev/null +++ b/src/drmmode_display.h @@ -0,0 +1,125 @@ +/* + * Copyright © 2007 Red Hat, Inc. + * + * 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 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Authors: + * Dave Airlie <airlied@redhat.com> + * + */ +#ifndef DRMMODE_DISPLAY_H +#define DRMMODE_DISPLAY_H + +#include "xf86drmMode.h" +#ifdef HAVE_LIBUDEV +#include "libudev.h" +#endif + +#include "amdgpu_probe.h" +#include "amdgpu.h" + +#ifndef DRM_CAP_TIMESTAMP_MONOTONIC +#define DRM_CAP_TIMESTAMP_MONOTONIC 0x6 +#endif + +typedef struct { + int fd; + unsigned fb_id; + drmModeResPtr mode_res; + drmModeFBPtr mode_fb; + int cpp; + ScrnInfoPtr scrn; +#ifdef HAVE_LIBUDEV + struct udev_monitor *uevent_monitor; + InputHandlerProc uevent_handler; +#endif + drmEventContext event_context; +} drmmode_rec, *drmmode_ptr; + +typedef struct { + drmmode_ptr drmmode; + unsigned old_fb_id; + int flip_count; + void *event_data; + unsigned int fe_frame; + unsigned int fe_tv_sec; + unsigned int fe_tv_usec; +} drmmode_flipdata_rec, *drmmode_flipdata_ptr; + +typedef struct { + drmmode_flipdata_ptr flipdata; + Bool dispatch_me; +} drmmode_flipevtcarrier_rec, *drmmode_flipevtcarrier_ptr; + +typedef struct { + drmmode_ptr drmmode; + drmModeCrtcPtr mode_crtc; + int hw_id; + struct amdgpu_buffer *cursor_buffer; + struct amdgpu_buffer *rotate_buffer; + unsigned rotate_fb_id; + int dpms_mode; + CARD64 dpms_last_ust; + uint32_t dpms_last_seq; + int dpms_last_fps; + uint32_t interpolated_vblanks; + uint16_t lut_r[256], lut_g[256], lut_b[256]; + int scanout_pixmap_x; +} drmmode_crtc_private_rec, *drmmode_crtc_private_ptr; + +typedef struct { + drmModePropertyPtr mode_prop; + uint64_t value; + int num_atoms; /* if range prop, num_atoms == 1; if enum prop, num_atoms == num_enums + 1 */ + Atom *atoms; +} drmmode_prop_rec, *drmmode_prop_ptr; + +typedef struct { + drmmode_ptr drmmode; + int output_id; + drmModeConnectorPtr mode_output; + drmModeEncoderPtr *mode_encoders; + drmModePropertyBlobPtr edid_blob; + int dpms_enum_id; + int num_props; + drmmode_prop_ptr props; + int enc_mask; + int enc_clone_mask; +} drmmode_output_private_rec, *drmmode_output_private_ptr; + +extern Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp); +extern void drmmode_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode); +extern void drmmode_fini(ScrnInfoPtr pScrn, drmmode_ptr drmmode); +extern void drmmode_set_cursor(ScrnInfoPtr scrn, drmmode_ptr drmmode, int id, + struct amdgpu_buffer *bo); +void drmmode_adjust_frame(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int x, int y); +extern Bool drmmode_set_desired_modes(ScrnInfoPtr pScrn, drmmode_ptr drmmode); +extern void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode); +extern Bool drmmode_setup_colormap(ScreenPtr pScreen, ScrnInfoPtr pScrn); + +extern void drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode); +extern void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode); + +extern int drmmode_get_pitch_align(ScrnInfoPtr scrn, int bpe); +Bool amdgpu_do_pageflip(ScrnInfoPtr scrn, struct amdgpu_buffer *new_front, + void *data, int ref_crtc_hw_id); +int drmmode_get_current_ust(int drm_fd, CARD64 * ust); + +#endif diff --git a/src/pcidb/ati_pciids.csv b/src/pcidb/ati_pciids.csv new file mode 100644 index 0000000..bac9b8f --- /dev/null +++ b/src/pcidb/ati_pciids.csv @@ -0,0 +1,74 @@ +"#pciid","define","family","name" +"0x6640","BONAIRE_6640","BONAIRE","BONAIRE" +"0x6641","BONAIRE_6641","BONAIRE","BONAIRE" +"0x6649","BONAIRE_6649","BONAIRE","BONAIRE" +"0x6650","BONAIRE_6650","BONAIRE","BONAIRE" +"0x6651","BONAIRE_6651","BONAIRE","BONAIRE" +"0x6658","BONAIRE_6658","BONAIRE","BONAIRE" +"0x665C","BONAIRE_665C","BONAIRE","BONAIRE" +"0x665D","BONAIRE_665D","BONAIRE","BONAIRE" +"0x9830","KABINI_9830","KABINI","KABINI" +"0x9831","KABINI_9831","KABINI","KABINI" +"0x9832","KABINI_9832","KABINI","KABINI" +"0x9833","KABINI_9833","KABINI","KABINI" +"0x9834","KABINI_9834","KABINI","KABINI" +"0x9835","KABINI_9835","KABINI","KABINI" +"0x9836","KABINI_9836","KABINI","KABINI" +"0x9837","KABINI_9837","KABINI","KABINI" +"0x9838","KABINI_9838","KABINI","KABINI" +"0x9839","KABINI_9839","KABINI","KABINI" +"0x983A","KABINI_983A","KABINI","KABINI" +"0x983B","KABINI_983B","KABINI","KABINI" +"0x983C","KABINI_983C","KABINI","KABINI" +"0x983D","KABINI_983D","KABINI","KABINI" +"0x983E","KABINI_983E","KABINI","KABINI" +"0x983F","KABINI_983F","KABINI","KABINI" +"0x1304","KAVERI_1304","KAVERI","KAVERI" +"0x1305","KAVERI_1305","KAVERI","KAVERI" +"0x1306","KAVERI_1306","KAVERI","KAVERI" +"0x1307","KAVERI_1307","KAVERI","KAVERI" +"0x1309","KAVERI_1309","KAVERI","KAVERI" +"0x130A","KAVERI_130A","KAVERI","KAVERI" +"0x130B","KAVERI_130B","KAVERI","KAVERI" +"0x130C","KAVERI_130C","KAVERI","KAVERI" +"0x130D","KAVERI_130D","KAVERI","KAVERI" +"0x130E","KAVERI_130E","KAVERI","KAVERI" +"0x130F","KAVERI_130F","KAVERI","KAVERI" +"0x1310","KAVERI_1310","KAVERI","KAVERI" +"0x1311","KAVERI_1311","KAVERI","KAVERI" +"0x1312","KAVERI_1312","KAVERI","KAVERI" +"0x1313","KAVERI_1313","KAVERI","KAVERI" +"0x1315","KAVERI_1315","KAVERI","KAVERI" +"0x1316","KAVERI_1316","KAVERI","KAVERI" +"0x1317","KAVERI_1317","KAVERI","KAVERI" +"0x131B","KAVERI_131B","KAVERI","KAVERI" +"0x131C","KAVERI_131C","KAVERI","KAVERI" +"0x131D","KAVERI_131D","KAVERI","KAVERI" +"0x67A0","HAWAII_67A0","HAWAII","HAWAII" +"0x67A1","HAWAII_67A1","HAWAII","HAWAII" +"0x67A2","HAWAII_67A2","HAWAII","HAWAII" +"0x67A8","HAWAII_67A8","HAWAII","HAWAII" +"0x67A9","HAWAII_67A9","HAWAII","HAWAII" +"0x67AA","HAWAII_67AA","HAWAII","HAWAII" +"0x67B0","HAWAII_67B0","HAWAII","HAWAII" +"0x67B1","HAWAII_67B1","HAWAII","HAWAII" +"0x67B8","HAWAII_67B8","HAWAII","HAWAII" +"0x67B9","HAWAII_67B9","HAWAII","HAWAII" +"0x67BA","HAWAII_67BA","HAWAII","HAWAII" +"0x67BE","HAWAII_67BE","HAWAII","HAWAII" +"0x6900","TOPAZ_6900","TOPAZ","TOPAZ" +"0x6901","TOPAZ_6901","TOPAZ","TOPAZ" +"0x6902","TOPAZ_6902","TOPAZ","TOPAZ" +"0x6903","TOPAZ_6903","TOPAZ","TOPAZ" +"0x6907","TOPAZ_6907","TOPAZ","TOPAZ" +"0x6920","TONGA_6920","TONGA","TONGA" +"0x6921","TONGA_6921","TONGA","TONGA" +"0x6938","TONGA_6938","TONGA","TONGA" +"0x6939","TONGA_6939","TONGA","TONGA" +"0x692B","TONGA_692B","TONGA","TONGA" +"0x692F","TONGA_692F","TONGA","TONGA" +"0x9870","CARRIZO_9870","CARRIZO","CARRIZO" +"0x9874","CARRIZO_9874","CARRIZO","CARRIZO" +"0x9875","CARRIZO_9875","CARRIZO","CARRIZO" +"0x9876","CARRIZO_9876","CARRIZO","CARRIZO" +"0x9877","CARRIZO_9877","CARRIZO","CARRIZO" diff --git a/src/pcidb/parse_pci_ids.pl b/src/pcidb/parse_pci_ids.pl new file mode 100755 index 0000000..06a9e1e --- /dev/null +++ b/src/pcidb/parse_pci_ids.pl @@ -0,0 +1,70 @@ +#!/usr/bin/perl +# +# Copyright 2007 Red Hat Inc. +# This crappy script written by Dave Airlie to avoid hassle of adding +# ids in every place. +# +use strict; +use warnings; +use Text::CSV_XS; + +my $file = $ARGV[0]; + +my $atioutfile = 'ati_pciids_gen.h'; +my $amdgpupcichipsetfile = 'amdgpu_pci_chipset_gen.h'; +my $amdgpupcidevicematchfile = 'amdgpu_pci_device_match_gen.h'; +my $amdgpuchipsetfile = 'amdgpu_chipset_gen.h'; +my $amdgpuchipinfofile = 'amdgpu_chipinfo_gen.h'; + +my $csv = Text::CSV_XS->new(); + +open (CSV, "<", $file) or die $!; + +open (ATIOUT, ">", $atioutfile) or die; +open (PCICHIPSET, ">", $amdgpupcichipsetfile) or die; +open (PCIDEVICEMATCH, ">", $amdgpupcidevicematchfile) or die; +open (AMDGPUCHIPSET, ">", $amdgpuchipsetfile) or die; +open (AMDGPUCHIPINFO, ">", $amdgpuchipinfofile) or die; + +print AMDGPUCHIPSET "/* This file is autogenerated please do not edit */\n"; +print AMDGPUCHIPSET "SymTabRec AMDGPUChipsets[] = {\n"; +print PCICHIPSET "/* This file is autogenerated please do not edit */\n"; +print PCICHIPSET "static PciChipsets AMDGPUPciChipsets[] = {\n"; +print PCIDEVICEMATCH "/* This file is autogenerated please do not edit */\n"; +print PCIDEVICEMATCH "static const struct pci_id_match amdgpu_device_match[] = {\n"; +print AMDGPUCHIPINFO "/* This file is autogenerated please do not edit */\n"; +print AMDGPUCHIPINFO "static AMDGPUCardInfo AMDGPUCards[] = {\n"; +while (<CSV>) { + if ($csv->parse($_)) { + my @columns = $csv->fields(); + + if ((substr($columns[0], 0, 1) ne "#")) { + + print ATIOUT "#define PCI_CHIP_$columns[1] $columns[0]\n"; + + if (($columns[2] ne "R128") && ($columns[2] ne "MACH64") && ($columns[2] ne "MACH32")) { + print PCICHIPSET " { PCI_CHIP_$columns[1], PCI_CHIP_$columns[1], RES_SHARED_VGA },\n"; + + print PCIDEVICEMATCH " ATI_DEVICE_MATCH( PCI_CHIP_$columns[1], 0 ),\n"; + + print AMDGPUCHIPSET " { PCI_CHIP_$columns[1], \"$columns[3]\" },\n"; + + print AMDGPUCHIPINFO " { $columns[0], CHIP_FAMILY_$columns[2] },\n"; + } + } + } else { + my $err = $csv->error_input; + print "Failed to parse line: $err"; + } +} + +print AMDGPUCHIPINFO "};\n"; +print AMDGPUCHIPSET " { -1, NULL }\n};\n"; +print PCICHIPSET " { -1, -1, RES_UNDEFINED }\n};\n"; +print PCIDEVICEMATCH " { 0, 0, 0 }\n};\n"; +close CSV; +close ATIOUT; +close PCICHIPSET; +close PCIDEVICEMATCH; +close AMDGPUCHIPSET; +close AMDGPUCHIPINFO; diff --git a/src/simple_list.h b/src/simple_list.h new file mode 100644 index 0000000..94a8af4 --- /dev/null +++ b/src/simple_list.h @@ -0,0 +1,201 @@ +/** + * \file simple_list.h + * Simple macros for type-safe, intrusive lists. + * + * Intended to work with a list sentinal which is created as an empty + * list. Insert & delete are O(1). + * + * \author + * (C) 1997, Keith Whitwell + */ + +/* + * Mesa 3-D graphics library + * Version: 3.5 + * + * Copyright (C) 1999-2001 Brian Paul All Rights Reserved. + * + * 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 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * 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 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 + * BRIAN PAUL 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. + */ + +#ifndef _SIMPLE_LIST_H +#define _SIMPLE_LIST_H + +struct simple_node { + struct simple_node *next; + struct simple_node *prev; +}; + +/** + * Remove an element from list. + * + * \param elem element to remove. + */ +#define remove_from_list(elem) \ +do { \ + (elem)->next->prev = (elem)->prev; \ + (elem)->prev->next = (elem)->next; \ +} while (0) + +/** + * Insert an element to the list head. + * + * \param list list. + * \param elem element to insert. + */ +#define insert_at_head(list, elem) \ +do { \ + (elem)->prev = list; \ + (elem)->next = (list)->next; \ + (list)->next->prev = elem; \ + (list)->next = elem; \ +} while(0) + +/** + * Insert an element to the list tail. + * + * \param list list. + * \param elem element to insert. + */ +#define insert_at_tail(list, elem) \ +do { \ + (elem)->next = list; \ + (elem)->prev = (list)->prev; \ + (list)->prev->next = elem; \ + (list)->prev = elem; \ +} while(0) + +/** + * Move an element to the list head. + * + * \param list list. + * \param elem element to move. + */ +#define move_to_head(list, elem) \ +do { \ + remove_from_list(elem); \ + insert_at_head(list, elem); \ +} while (0) + +/** + * Move an element to the list tail. + * + * \param list list. + * \param elem element to move. + */ +#define move_to_tail(list, elem) \ +do { \ + remove_from_list(elem); \ + insert_at_tail(list, elem); \ +} while (0) + +/** + * Make a empty list empty. + * + * \param sentinal list (sentinal element). + */ +#define make_empty_list(sentinal) \ +do { \ + (sentinal)->next = sentinal; \ + (sentinal)->prev = sentinal; \ +} while (0) + +/** + * Get list first element. + * + * \param list list. + * + * \return pointer to first element. + */ +#define first_elem(list) ((list)->next) + +/** + * Get list last element. + * + * \param list list. + * + * \return pointer to last element. + */ +#define last_elem(list) ((list)->prev) + +/** + * Get next element. + * + * \param elem element. + * + * \return pointer to next element. + */ +#define next_elem(elem) ((elem)->next) + +/** + * Get previous element. + * + * \param elem element. + * + * \return pointer to previous element. + */ +#define prev_elem(elem) ((elem)->prev) + +/** + * Test whether element is at end of the list. + * + * \param list list. + * \param elem element. + * + * \return non-zero if element is at end of list, or zero otherwise. + */ +#define at_end(list, elem) ((elem) == (list)) + +/** + * Test if a list is empty. + * + * \param list list. + * + * \return non-zero if list empty, or zero otherwise. + */ +#define is_empty_list(list) ((list)->next == (list)) + +/** + * Walk through the elements of a list. + * + * \param ptr pointer to the current element. + * \param list list. + * + * \note It should be followed by a { } block or a single statement, as in a \c + * for loop. + */ +#define foreach(ptr, list) \ + for( ptr=(list)->next ; ptr!=list ; ptr=(ptr)->next ) + +/** + * Walk through the elements of a list. + * + * Same as #foreach but lets you unlink the current value during a list + * traversal. Useful for freeing a list, element by element. + * + * \param ptr pointer to the current element. + * \param t temporary pointer. + * \param list list. + * + * \note It should be followed by a { } block or a single statement, as in a \c + * for loop. + */ +#define foreach_s(ptr, t, list) \ + for(ptr=(list)->next,t=(ptr)->next; list != ptr; ptr=t, t=(t)->next) + +#endif |