From c9f6fc5585acfad91f3c4f918fd62b8c37209a80 Mon Sep 17 00:00:00 2001 From: Yi Sun Date: Thu, 5 Mar 2015 15:56:04 +0800 Subject: Init the repo for upstream. Signed-off-by: Yi Sun --- Makefile.am | 25 + autogen.sh | 19 + configure.ac | 231 +++ lib/.gitignore | 1 + lib/Makefile.am | 20 + lib/Makefile.sources | 56 + lib/check-ndebug.h | 3 + lib/drmtest.c | 378 +++++ lib/drmtest.h | 105 ++ lib/igt_aux.c | 699 +++++++++ lib/igt_aux.h | 104 ++ lib/igt_core.c | 1689 ++++++++++++++++++++ lib/igt_core.h | 652 ++++++++ lib/igt_debugfs.c | 615 ++++++++ lib/igt_debugfs.h | 145 ++ lib/igt_fb.c | 807 ++++++++++ lib/igt_fb.h | 105 ++ lib/igt_gt.c | 341 ++++ lib/igt_gt.h | 77 + lib/igt_kms.c | 1853 ++++++++++++++++++++++ lib/igt_kms.h | 280 ++++ lib/intel_batchbuffer.c | 532 +++++++ lib/intel_batchbuffer.h | 268 ++++ lib/intel_chipset.c | 206 +++ lib/intel_chipset.h | 444 ++++++ lib/intel_io.h | 85 + lib/intel_reg.h | 3600 +++++++++++++++++++++++++++++++++++++++++++ lib/ioctl_wrappers.c | 1144 ++++++++++++++ lib/ioctl_wrappers.h | 138 ++ lib/media_fill.h | 42 + lib/rendercopy.h | 55 + m4/ax_pkg_swig.m4 | 135 ++ m4/ax_python_devel.m4 | 324 ++++ m4/ax_swig_python.m4 | 64 + tests/.gitignore | 154 ++ tests/1080p-left.png | Bin 0 -> 41858 bytes tests/1080p-right.png | Bin 0 -> 42502 bytes tests/Makefile.am | 23 + tests/Makefile.sources | 16 + tests/pass.png | Bin 0 -> 569 bytes tests/template.c | 80 + tests/testdisplay.c | 477 ++++++ tests/testdisplay.h | 35 + tests/testdisplay_hotplug.c | 138 ++ 44 files changed, 16165 insertions(+) create mode 100644 Makefile.am create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 lib/.gitignore create mode 100644 lib/Makefile.am create mode 100644 lib/Makefile.sources create mode 100644 lib/check-ndebug.h create mode 100644 lib/drmtest.c create mode 100644 lib/drmtest.h create mode 100644 lib/igt_aux.c create mode 100644 lib/igt_aux.h create mode 100644 lib/igt_core.c create mode 100644 lib/igt_core.h create mode 100644 lib/igt_debugfs.c create mode 100644 lib/igt_debugfs.h create mode 100644 lib/igt_fb.c create mode 100644 lib/igt_fb.h create mode 100644 lib/igt_gt.c create mode 100644 lib/igt_gt.h create mode 100644 lib/igt_kms.c create mode 100644 lib/igt_kms.h create mode 100644 lib/intel_batchbuffer.c create mode 100644 lib/intel_batchbuffer.h create mode 100644 lib/intel_chipset.c create mode 100644 lib/intel_chipset.h create mode 100644 lib/intel_io.h create mode 100644 lib/intel_reg.h create mode 100644 lib/ioctl_wrappers.c create mode 100644 lib/ioctl_wrappers.h create mode 100644 lib/media_fill.h create mode 100644 lib/rendercopy.h create mode 100644 m4/ax_pkg_swig.m4 create mode 100644 m4/ax_python_devel.m4 create mode 100644 m4/ax_swig_python.m4 create mode 100644 tests/.gitignore create mode 100644 tests/1080p-left.png create mode 100644 tests/1080p-right.png create mode 100644 tests/Makefile.am create mode 100644 tests/Makefile.sources create mode 100644 tests/pass.png create mode 100644 tests/template.c create mode 100644 tests/testdisplay.c create mode 100644 tests/testdisplay.h create mode 100644 tests/testdisplay_hotplug.c diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..d36dd0a --- /dev/null +++ b/Makefile.am @@ -0,0 +1,25 @@ +# Copyright © 2005 Adam Jackson. +# Copyright © 2009,2013 Intel Corporation +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# on the rights to use, copy, modify, merge, publish, distribute, sub +# license, and/or sell copies of the Software, and to permit persons to whom +# the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL +# ADAM JACKSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 + +#SUBDIRS = lib man tools scripts benchmarks demos overlay +SUBDIRS = lib tests diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..629a257 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,19 @@ +#! /bin/sh + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir + +if ! type gtkdocize > /dev/null 2>&1; then + echo "EXTRA_DIST =" > gtk-doc.make + echo "CLEANFILES =" >> gtk-doc.make +else + gtkdocize || exit $? +fi + +autoreconf -v --install || exit 1 +cd $ORIGDIR || exit $? + +$srcdir/configure "$@" diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..3e77f7d --- /dev/null +++ b/configure.ac @@ -0,0 +1,231 @@ +# Copyright 2005 Adam Jackson. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# on the rights to use, copy, modify, merge, publish, distribute, sub +# license, and/or sell copies of the Software, and to permit persons to whom +# the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL +# ADAM JACKSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Process this file with autoconf to produce a configure script + +AC_PREREQ([2.60]) +AC_INIT([intel-gpu-tools], + [1.9], + [https://bugs.freedesktop.org/enter_bug.cgi?product=DRI&component=DRM/Intel], + [intel-gpu-tools]) + +AC_CONFIG_SRCDIR([Makefile.am]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_USE_SYSTEM_EXTENSIONS +AC_SYS_LARGEFILE +AC_GNU_SOURCE +AC_CANONICAL_HOST + +AM_INIT_AUTOMAKE([1.11 foreign dist-bzip2]) +AM_PATH_PYTHON([3],, [:]) + +AC_PROG_CC +AM_PROG_LEX +AC_PROG_YACC + +# check for gtk-doc +m4_ifdef([GTK_DOC_CHECK], [ +GTK_DOC_CHECK([1.14],[--flavour no-tmpl]) +],[ +AM_CONDITIONAL([ENABLE_GTK_DOC], false) +enable_gtk_doc=no +]) + + +# Checks for functions, headers, structures, etc. +AC_HEADER_STDC +AC_CHECK_HEADERS([termios.h linux/kd.h sys/kd.h libgen.h]) +AC_CHECK_MEMBERS([struct sysinfo.totalram],[],[],[AC_INCLUDES_DEFAULT + #include + ]) +AC_CHECK_TYPES([sighandler_t],[],[],[AC_INCLUDES_DEFAULT +#include ]) +AC_CHECK_FUNCS([swapctl]) +AC_CHECK_FUNCS([asprintf]) + +# Initialize libtool +AC_DISABLE_STATIC +AC_PROG_LIBTOOL + +# Require X.Org macros 1.16 or later for XORG_TESTSET_CFLAG +m4_ifndef([XORG_MACROS_VERSION], + [m4_fatal([must install xorg-macros 1.16 or later before running autoconf/autogen])]) +XORG_MACROS_VERSION(1.16) +XORG_DEFAULT_OPTIONS + +# warning flags for the assembler. We can't quite use CWARNFLAGS for it yet as +# it generates waaaay too many warnings. +ASSEMBLER_WARN_CFLAGS="" +if test "x$GCC" = "xyes"; then + ASSEMBLER_WARN_CFLAGS="-Wall -Wstrict-prototypes \ + -Wmissing-prototypes -Wmissing-declarations \ + -Wnested-externs -fno-strict-aliasing" +fi +AC_SUBST(ASSEMBLER_WARN_CFLAGS) + +PKG_CHECK_MODULES(DRM, [libdrm_intel >= 2.4.52 libdrm]) +PKG_CHECK_MODULES(PCIACCESS, [pciaccess >= 0.10]) +PKG_CHECK_MODULES(OVERLAY_XVLIB, [xv x11 xext dri2proto >= 2.6], enable_overlay_xvlib=yes, enable_overlay_xvlib=no) +PKG_CHECK_MODULES(OVERLAY_XLIB, [cairo-xlib dri2proto >= 2.6], enable_overlay_xlib=yes, enable_overlay_xlib=no) + +AM_CONDITIONAL(BUILD_OVERLAY_XVLIB, [test "x$enable_overlay_xvlib" = xyes]) +AM_CONDITIONAL(BUILD_OVERLAY_XLIB, [test "x$enable_overlay_xlib" = xyes]) +AM_CONDITIONAL(BUILD_OVERLAY, [test "x$enable_overlay_xlib" = xyes -o "x$enable_overlay_xvlib"]) +if test x$enable_overlay_xvlib = xyes; then + AC_DEFINE(HAVE_OVERLAY_XVLIB, 1, [Enable XV backend]) +fi +if test x$enable_overlay_xlib = xyes; then + AC_DEFINE(HAVE_OVERLAY_XLIB, 1, [Enable X backend]) +fi +PKG_CHECK_MODULES(XRANDR, xrandr >= 1.3, AC_DEFINE(HAVE_XRANDR, 1, [Have libXrandr]), [have_xrandr=no]) + +# for testdisplay +PKG_CHECK_MODULES(CAIRO, [cairo >= 1.12.0]) +PKG_CHECK_MODULES(LIBUDEV, [libudev], [udev=yes], [udev=no]) +if test x"$udev" = xyes; then + AC_DEFINE(HAVE_UDEV,1,[Enable udev-based monitor hotplug detection]) +fi +PKG_CHECK_MODULES(GLIB, glib-2.0) + +# can we build the assembler? +AS_IF([test x"$LEX" != "x:" -a x"$YACC" != xyacc], + [enable_assembler=yes], + [enable_assembler=no]) +AM_CONDITIONAL(BUILD_ASSEMBLER, [test "x$enable_assembler" = xyes]) + +# ----------------------------------------------------------------------------- +# Configuration options +# ----------------------------------------------------------------------------- +# for dma-buf tests +AC_ARG_ENABLE(nouveau, AS_HELP_STRING([--disable-nouveau], + [Enable use of nouveau API for prime tests (default: auto)]), + [NOUVEAU=$enableval], [NOUVEAU=auto]) +if test "x$NOUVEAU" = xauto; then + PKG_CHECK_EXISTS([libdrm_nouveau >= 2.4.33], [NOUVEAU=yes], [NOUVEAU=no]) +fi +if test "x$NOUVEAU" = xyes; then + PKG_CHECK_MODULES(DRM_NOUVEAU, [libdrm_nouveau >= 2.4.33]) + AC_DEFINE(HAVE_NOUVEAU, 1, [Have nouveau support]) +fi +AM_CONDITIONAL(HAVE_NOUVEAU, [test "x$NOUVEAU" = xyes]) + +#Configure options for the python quick-dumper +AC_ARG_ENABLE(dumper, + AS_HELP_STRING([--disable-dumper], + [Disable the python based register dumper (default: enabled)]), + [DUMPER=$enableval], [DUMPER=auto]) +if test "x$DUMPER" = xauto; then + # AX_PYTHON_DEVEL/AX_SWIG_PYTHON are not super friendly and don't + # easily allow us to introspect the result of their checks. So if we + # find SWING on the system, that's enough to try compiling the dumper. + AX_PKG_SWIG(2.0.0, [DUMPER=yes], [DUMPER=no]) +fi +if test "x$DUMPER" = xyes; then + AC_DEFINE(HAVE_DUMPER, 1, [Have dumper support]) + # SWIG configuration + AX_PKG_SWIG(2.0.0, [], [ AC_MSG_ERROR([SWIG 2.0.0 or higher is required to build..]) ]) + AX_PYTHON_DEVEL([>= '3.0']) + AX_SWIG_PYTHON +fi +AM_CONDITIONAL(HAVE_DUMPER, [test "x$DUMPER" = xyes]) + +# Define a configure option for the shader debugger +AC_ARG_ENABLE(shader-debugger, AS_HELP_STRING([--enable-shader-debugger], + [Enable shader debugging support [autodetected]]), + [BUILD_SHADER_DEBUGGER="$enableval"], [BUILD_SHADER_DEBUGGER=auto]) + +# Shader debugger depends on python3, intel-genasm and objcopy +if test "x$BUILD_SHADER_DEBUGGER" != xno; then + # Check that the assembler is built + if test "x$enable_assembler" = xno; then + BUILD_SHADER_DEBUGGER=no + if test "x$BUILD_SHADER_DEBUGGER" = xyes; then + AC_MSG_ERROR([Shader debugger requested, but assembler not enabled.]) + fi + fi + + # Check Python 3 is installed + if test "$PYTHON" = ":" ; then + if test "x$BUILD_SHADER_DEBUGGER" = xyes; then + AC_MSG_ERROR([Shader debugger requested, python version 3 not found.]) + else + BUILD_SHADER_DEBUGGER=no + fi + fi + + # Check for the objcopy GNU binary utiliy command + AC_PATH_PROGS([OBJCOPY], objcopy) + if test -z "$OBJCOPY" ; then + if test "x$BUILD_SHADER_DEBUGGER" = xyes; then + AC_MSG_ERROR([Shader debugger requested, but objcopy command not found.]) + else + BUILD_SHADER_DEBUGGER=no + fi + fi +fi + +AM_CONDITIONAL(BUILD_SHADER_DEBUGGER, [test "x$BUILD_SHADER_DEBUGGER" != xno]) +AS_IF([test "x$BUILD_SHADER_DEBUGGER" != xno], + [enable_debugger=yes], [enable_debugger=no]) + +# ----------------------------------------------------------------------------- + +# To build multithread code, gcc uses -pthread, Solaris Studio cc uses -mt +XORG_TESTSET_CFLAG([THREAD_CFLAGS], [-pthread], [-mt]) +AC_SUBST([THREAD_CFLAGS]) + +AC_ARG_ENABLE(tests, + AS_HELP_STRING([--disable-tests], + [Disable tests build (default: enabled)]), + [BUILD_TESTS=$enableval], [BUILD_TESTS="yes"]) +if test "x$BUILD_TESTS" = xyes; then + AC_DEFINE(BUILD_TESTS, 1, [Build tests]) +fi +AM_CONDITIONAL(BUILD_TESTS, [test "x$BUILD_TESTS" = xyes]) +AC_DEFINE_UNQUOTED(TARGET_CPU_PLATFORM, ["$host_cpu"], [Target platform]) + +AC_CONFIG_FILES([ + Makefile + lib/Makefile + tests/Makefile + ]) +AC_OUTPUT + +# Print a summary of the compilation +echo "" +echo "Intel GPU tools" + +echo "" +echo " • Tests:" +echo " Build tests : ${BUILD_TESTS}" +echo " Compile prime tests: ${NOUVEAU}" +echo "" +echo " • Tools:" +echo " Assembler : ${enable_assembler}" +echo " Debugger : ${enable_debugger}" +echo " Python dumper : ${DUMPER}" +echo " Overlay : X: ${enable_overlay_xlib}, Xv: ${enable_overlay_xvlib}" +echo "" +echo " • API-Documentation : ${enable_gtk_doc}" +echo "" + +# vim: set ft=config ts=8 sw=8 tw=0 noet : diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 0000000..6702033 --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1 @@ +version.h diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..fcef049 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,20 @@ +IGT_LIB_PATH := $(builddir) +GPU_TOOLS_PATH := $(top_srcdir) + +SUBDIRS = . + +include Makefile.sources + +noinst_LTLIBRARIES = libintel_tools.la +noinst_HEADERS = check-ndebug.h + +AM_CPPFLAGS = -I$(top_srcdir) +AM_CFLAGS = $(DRM_CFLAGS) $(CWARNFLAGS) \ + -DIGT_DATADIR=\""$(abs_top_srcdir)/tests"\" \ + -DIGT_LOG_DOMAIN=\""$(subst _,-,$*)"\" \ + -pthread + + +LDADD = $(CAIRO_LIBS) +AM_CFLAGS += $(CAIRO_CFLAGS) + diff --git a/lib/Makefile.sources b/lib/Makefile.sources new file mode 100644 index 0000000..518f020 --- /dev/null +++ b/lib/Makefile.sources @@ -0,0 +1,56 @@ +libintel_tools_la_SOURCES = \ + drmtest.c \ + drmtest.h \ + igt_debugfs.c \ + igt_debugfs.h \ + igt_aux.c \ + igt_aux.h \ + igt_gt.c \ + igt_gt.h \ + intel_batchbuffer.c \ + intel_batchbuffer.h \ + intel_chipset.h \ + intel_chipset.c \ + intel_io.h \ + intel_reg.h \ + ioctl_wrappers.c \ + ioctl_wrappers.h \ + igt_kms.c \ + igt_kms.h \ + igt_fb.c \ + igt_fb.h \ + igt_core.c \ + igt_core.h \ + rendercopy.h \ + media_fill.h \ + intel_chipset.h \ + intel_chipset.c \ + $(NULL) + +.PHONY: version.h.tmp + +$(IGT_LIB_PATH)/version.h.tmp: + @touch $@ + @if test -d $(GPU_TOOLS_PATH)/.git; then \ + if which git > /dev/null 2>&1; then \ + cd $(GPU_TOOLS_PATH); \ + git log -n 1 --oneline | \ + sed 's/^\([^ ]*\) .*/#define IGT_GIT_SHA1 "g\1"/' ; \ + else \ + echo '#define IGT_GIT_SHA1 "NO-GIT"' ; \ + fi \ + else \ + echo '#define IGT_GIT_SHA1 "NOT-GIT"' ; \ + fi >> $@ + + +$(IGT_LIB_PATH)/version.h: $(IGT_LIB_PATH)/version.h.tmp + @if ! cmp -s $(IGT_LIB_PATH)/version.h.tmp $(IGT_LIB_PATH)/version.h; then \ + mv $(IGT_LIB_PATH)/version.h.tmp $(IGT_LIB_PATH)/version.h ; \ + else \ + rm $(IGT_LIB_PATH)/version.h.tmp ; \ + fi + +BUILT_SOURCES = $(IGT_LIB_PATH)/version.h +CLEANFILES = $(IGT_LIB_PATH)/version.h $(IGT_LIB_PATH)/version.h.tmp + diff --git a/lib/check-ndebug.h b/lib/check-ndebug.h new file mode 100644 index 0000000..68a6ec4 --- /dev/null +++ b/lib/check-ndebug.h @@ -0,0 +1,3 @@ +#ifdef NDEBUG +#error "Testsuite needs its asserts!" +#endif diff --git a/lib/drmtest.c b/lib/drmtest.c new file mode 100644 index 0000000..ee5c123 --- /dev/null +++ b/lib/drmtest.c @@ -0,0 +1,378 @@ +/* + * Copyright © 2007, 2011, 2013 Intel Corporation + * + * 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: + * Eric Anholt + * Daniel Vetter + * + */ + +#ifndef ANDROID +#define _GNU_SOURCE +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drmtest.h" +#include "i915_drm.h" +#include "intel_chipset.h" +#include "intel_io.h" +#include "igt_gt.h" +#include "igt_debugfs.h" +#include "version.h" +#include "config.h" +#include "intel_reg.h" +#include "ioctl_wrappers.h" + +/** + * SECTION:drmtest + * @short_description: Base library for drm tests and tools + * @title: drmtest + * @include: drmtest.h + * + * This library contains the basic support for writing tests, with the most + * important part being the helper function to open drm device nodes. + * + * But there's also a bit of other assorted stuff here. + * + * Note that this library's header pulls in the [i-g-t core](intel-gpu-tools-i-g-t-core.html) + * and [batchbuffer](intel-gpu-tools-intel-batchbuffer.html) libraries as dependencies. + */ + +uint16_t __drm_device_id; + +static int is_i915_device(int fd) +{ + drm_version_t version; + char name[5] = ""; + + memset(&version, 0, sizeof(version)); + version.name_len = 4; + version.name = name; + + if (drmIoctl(fd, DRM_IOCTL_VERSION, &version)) + return 0; + + return strcmp("i915", name) == 0; +} + +static int +is_intel(int fd) +{ + struct drm_i915_getparam gp; + int devid = 0; + + memset(&gp, 0, sizeof(gp)); + gp.param = I915_PARAM_CHIPSET_ID; + gp.value = &devid; + + if (ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp, sizeof(gp))) + return 0; + + if (!IS_INTEL(devid)) + return 0; + + __drm_device_id = devid; + return 1; +} + +static void check_stop_rings(void) +{ + enum stop_ring_flags flags; + flags = igt_get_stop_rings(); + igt_warn_on_f(flags != 0, + "i915_ring_stop flags on exit 0x%x, can't quiescent gpu cleanly\n", + flags); + + if (flags) + igt_set_stop_rings(STOP_RING_NONE); +} + +#define LOCAL_I915_EXEC_VEBOX (4 << 0) +/** + * gem_quiescent_gpu: + * @fd: open i915 drm file descriptor + * + * Ensure the gpu is idle by launching a nop execbuf and stalling for it. This + * is automatically run when opening a drm device node and is also installed as + * an exit handler to have the best assurance that the test is run in a pristine + * and controlled environment. + * + * This function simply allows tests to make additional calls in-between, if so + * desired. + */ +void gem_quiescent_gpu(int fd) +{ + uint32_t batch[2] = {MI_BATCH_BUFFER_END, 0}; + uint32_t handle; + struct drm_i915_gem_execbuffer2 execbuf; + struct drm_i915_gem_exec_object2 gem_exec[1]; + + check_stop_rings(); + + handle = gem_create(fd, 4096); + gem_write(fd, handle, 0, batch, sizeof(batch)); + + gem_exec[0].handle = handle; + gem_exec[0].relocation_count = 0; + gem_exec[0].relocs_ptr = 0; + gem_exec[0].alignment = 0; + gem_exec[0].offset = 0; + gem_exec[0].flags = 0; + gem_exec[0].rsvd1 = 0; + gem_exec[0].rsvd2 = 0; + + execbuf.buffers_ptr = (uintptr_t)gem_exec; + execbuf.buffer_count = 1; + execbuf.batch_start_offset = 0; + execbuf.batch_len = 8; + execbuf.cliprects_ptr = 0; + execbuf.num_cliprects = 0; + execbuf.DR1 = 0; + execbuf.DR4 = 0; + execbuf.flags = 0; + i915_execbuffer2_set_context_id(execbuf, 0); + execbuf.rsvd2 = 0; + + do_ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf); + + if (gem_has_blt(fd)) { + execbuf.flags = I915_EXEC_BLT; + do_ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf); + } + + if (gem_has_bsd(fd)) { + execbuf.flags = I915_EXEC_BSD; + do_ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf); + } + + if (gem_has_vebox(fd)) { + execbuf.flags = LOCAL_I915_EXEC_VEBOX; + do_ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf); + } + + gem_sync(fd, handle); + igt_drop_caches_set(DROP_RETIRE); + gem_close(fd, handle); +} + +/** + * drm_get_card: + * + * Get an i915 drm card index number for use in /dev or /sys. The minor index of + * the legacy node is returned, not of the control or render node. + * + * Returns: + * The i915 drm index or -1 on error + */ +int drm_get_card(void) +{ + char *name; + int i, fd; + + for (i = 0; i < 16; i++) { + int ret; + + ret = asprintf(&name, "/dev/dri/card%u", i); + igt_assert(ret != -1); + + fd = open(name, O_RDWR); + free(name); + + if (fd == -1) + continue; + + if (!is_i915_device(fd) || !is_intel(fd)) { + close(fd); + continue; + } + + close(fd); + return i; + } + + igt_skip("No intel gpu found\n"); + + return -1; +} + +/** Open the first DRM device we can find, searching up to 16 device nodes */ +int __drm_open_any(void) +{ + for (int i = 0; i < 16; i++) { + char name[80]; + int fd; + + sprintf(name, "/dev/dri/card%u", i); + fd = open(name, O_RDWR); + if (fd == -1) + continue; + + if (is_i915_device(fd) && is_intel(fd)) + return fd; + + close(fd); + } + + igt_skip("No intel gpu found\n"); + return -1; +} + +static int __drm_open_any_render(void) +{ + char *name; + int i, fd; + + for (i = 128; i < (128 + 16); i++) { + int ret; + + ret = asprintf(&name, "/dev/dri/renderD%u", i); + igt_assert(ret != -1); + + fd = open(name, O_RDWR); + free(name); + + if (fd == -1) + continue; + + if (!is_i915_device(fd) || !is_intel(fd)) { + close(fd); + fd = -1; + continue; + } + + return fd; + } + + return fd; +} + +static int at_exit_drm_fd = -1; +static int at_exit_drm_render_fd = -1; + +static void quiescent_gpu_at_exit(int sig) +{ + if (at_exit_drm_fd < 0) + return; + + check_stop_rings(); + gem_quiescent_gpu(at_exit_drm_fd); + close(at_exit_drm_fd); + at_exit_drm_fd = -1; +} + +static void quiescent_gpu_at_exit_render(int sig) +{ + if (at_exit_drm_render_fd < 0) + return; + + check_stop_rings(); + gem_quiescent_gpu(at_exit_drm_render_fd); + close(at_exit_drm_render_fd); + at_exit_drm_render_fd = -1; +} + +/** + * drm_open_any: + * + * Open an i915 drm legacy device node. This function always returns a valid + * file descriptor. + * + * Returns: a i915 drm file descriptor + */ +int drm_open_any(void) +{ + static int open_count; + int fd = __drm_open_any(); + + igt_require(fd >= 0); + + if (__sync_fetch_and_add(&open_count, 1)) + return fd; + + gem_quiescent_gpu(fd); + at_exit_drm_fd = __drm_open_any(); + igt_install_exit_handler(quiescent_gpu_at_exit); + + return fd; +} + +/** + * drm_open_any_master: + * + * Open an i915 drm legacy device node and ensure that it is drm master. + * + * Returns: + * The i915 drm file descriptor or -1 on error + */ +int drm_open_any_master(void) +{ + int fd = drm_open_any(); + + igt_require(fd >= 0); + igt_require_f(drmSetMaster(fd) == 0, "Can't become DRM master, " + "please check if no other DRM client is running.\n"); + + return fd; +} + +/** + * drm_open_any_render: + * + * Open an i915 drm render device node. + * + * Returns: + * The i915 drm file descriptor or -1 on error + */ +int drm_open_any_render(void) +{ + static int open_count; + int fd = __drm_open_any_render(); + + /* no render nodes, fallback to drm_open_any() */ + if (fd == -1) + return drm_open_any(); + + if (__sync_fetch_and_add(&open_count, 1)) + return fd; + + at_exit_drm_render_fd = __drm_open_any(); + gem_quiescent_gpu(fd); + igt_install_exit_handler(quiescent_gpu_at_exit_render); + + return fd; +} diff --git a/lib/drmtest.h b/lib/drmtest.h new file mode 100644 index 0000000..508cc83 --- /dev/null +++ b/lib/drmtest.h @@ -0,0 +1,105 @@ +/* + * Copyright © 2007 Intel Corporation + * + * 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: + * Eric Anholt + * + */ + +#ifndef DRMTEST_H +#define DRMTEST_H + +#include +#include +#include +#include +#include + +#include + +#include "intel_batchbuffer.h" + +#ifdef ANDROID +#ifndef HAVE_MMAP64 +extern void* __mmap2(void *, size_t, int, int, int, off_t); + +/* mmap64 is a recent addition to bionic and not available in all android builds. */ +/* I can find no reliable way to know if it is defined or not - so just avoid it */ +#define mmap64 igt_mmap64 +static inline void *igt_mmap64(void *addr, size_t length, int prot, int flags, + int fd, off64_t offset) +{ + return __mmap2(addr, length, prot, flags, fd, offset >> 12); +} +#endif +#endif + +/** + * ARRAY_SIZE: + * @arr: static array + * + * Macro to compute the size of the static array @arr. + */ +#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) + +/** + * ALIGN: + * @v: value to be aligned + * @a: alignment unit in bytes + * + * Macro to align a value @v to a specified unit @a. + */ +#define ALIGN(v, a) (((v) + (a)-1) & ~((a)-1)) + +int drm_get_card(void); +int __drm_open_any(void); +int drm_open_any(void); +int drm_open_any_master(void); +int drm_open_any_render(void); + +void gem_quiescent_gpu(int fd); + +/** + * do_or_die: + * @x: command + * + * Simple macro to execute x and check that it's return value is 0. Presumes + * that in any failure case the return value is non-zero and a precise error is + * logged into errno. Uses igt_assert() internally. + */ +#define do_or_die(x) igt_assert((x) == 0) + +/** + * do_ioctl: + * @fd: open i915 drm file descriptor + * @ioc: ioctl op definition from drm headers + * @ioc_data: data pointer for the ioctl operation + * + * This macro wraps drmIoctl() and uses igt_assert to check that it has been + * successfully executed. + */ +#define do_ioctl(fd, ioc, ioc_data) do { \ + igt_assert(drmIoctl((fd), (ioc), (ioc_data)) == 0); \ + errno = 0; \ +} while (0) + +#endif /* DRMTEST_H */ diff --git a/lib/igt_aux.c b/lib/igt_aux.c new file mode 100644 index 0000000..131ff4b --- /dev/null +++ b/lib/igt_aux.c @@ -0,0 +1,699 @@ +/* + * Copyright © 2007, 2011, 2013, 2014, 2015 Intel Corporation + * + * 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: + * Eric Anholt + * Daniel Vetter + * + */ + +#ifndef ANDROID +#define _GNU_SOURCE +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drmtest.h" +#include "i915_drm.h" +#include "intel_chipset.h" +#include "igt_aux.h" +#include "igt_debugfs.h" +#include "igt_gt.h" +#include "config.h" +#include "intel_reg.h" +#include "ioctl_wrappers.h" +#include "igt_kms.h" + +/** + * SECTION:igt_aux + * @short_description: Auxiliary libraries and support functions + * @title: i-g-t aux + * @include: igt_aux.h + * + * This library provides various auxiliary helper functions that don't really + * fit into any other topic. + */ + + +/* signal interrupt helpers */ +static struct igt_helper_process signal_helper; +long long int sig_stat; +static void __attribute__((noreturn)) signal_helper_process(pid_t pid) +{ + /* Interrupt the parent process at 500Hz, just to be annoying */ + while (1) { + usleep(1000 * 1000 / 500); + if (kill(pid, SIGUSR1)) /* Parent has died, so must we. */ + exit(0); + } +} + +static void sig_handler(int i) +{ + sig_stat++; +} + +/** + * igt_fork_signal_helper: + * + * Fork a child process using #igt_fork_helper to interrupt the parent process + * with a SIGUSR1 signal at regular quick intervals. The corresponding dummy + * signal handler is installed in the parent process. + * + * This is useful to exercise ioctl error paths, at least where those can be + * exercises by interrupting blocking waits, like stalling for the gpu. This + * helper can also be used from children spawned with #igt_fork. + * + * In tests with subtests this function can be called outside of failure + * catching code blocks like #igt_fixture or #igt_subtest. + */ +void igt_fork_signal_helper(void) +{ + if (igt_only_list_subtests()) + return; + + signal(SIGUSR1, sig_handler); + + igt_fork_helper(&signal_helper) { + signal_helper_process(getppid()); + } +} + +/** + * igt_stop_signal_helper: + * + * Stops the child process spawned with igt_fork_signal_helper() again. + * + * In tests with subtests this function can be called outside of failure + * catching code blocks like #igt_fixture or #igt_subtest. + */ +void igt_stop_signal_helper(void) +{ + if (igt_only_list_subtests()) + return; + + igt_stop_helper(&signal_helper); + + sig_stat = 0; +} + +/** + * igt_check_boolean_env_var: + * @env_var: environment variable name + * @default_value: default value for the environment variable + * + * This function should be used to parse boolean environment variable options. + * + * Returns: + * The boolean value of the environment variable @env_var as decoded by atoi() + * if it is set and @default_value if the variable is not set. + */ +bool igt_check_boolean_env_var(const char *env_var, bool default_value) +{ + char *val; + + val = getenv(env_var); + if (!val) + return default_value; + + return atoi(val) != 0; +} + +/** + * igt_aub_dump_enabled: + * + * Returns: + * True if AUB dumping is enabled with IGT_DUMP_AUB=1 in the environment, false + * otherwise. + */ +bool igt_aub_dump_enabled(void) +{ + static int dump_aub = -1; + + if (dump_aub == -1) + dump_aub = igt_check_boolean_env_var("IGT_DUMP_AUB", false); + + return dump_aub; +} + +/* other helpers */ +/** + * igt_exchange_int: + * @array: pointer to the array of integers + * @i: first position + * @j: second position + * + * Exchanges the two values at array indices @i and @j. Useful as an exchange + * function for igt_permute_array(). + */ +void igt_exchange_int(void *array, unsigned i, unsigned j) +{ + int *int_arr, tmp; + int_arr = array; + + tmp = int_arr[i]; + int_arr[i] = int_arr[j]; + int_arr[j] = tmp; +} + +static uint32_t +hars_petruska_f54_1_random_unsafe(void) +{ + static uint32_t state = 0x12345678; +#define rol(x,k) ((x << k) | (x >> (32-k))) + return state = (state ^ rol (state, 5) ^ rol (state, 24)) + 0x37798849; +#undef rol +} + +/** + * igt_permute_array: + * @array: pointer to array + * @size: size of the array + * @exchange_func: function to exchange array elements + * + * This function randomly permutes the array using random() as the PRNG source. + * The @exchange_func function is called to exchange two elements in the array + * when needed. + */ +void igt_permute_array(void *array, unsigned size, + void (*exchange_func)(void *array, + unsigned i, + unsigned j)) +{ + int i; + + for (i = size - 1; i > 1; i--) { + /* yes, not perfectly uniform, who cares */ + long l = hars_petruska_f54_1_random_unsafe() % (i +1); + if (i != l) + exchange_func(array, i, l); + } +} + +__attribute__((format(printf, 1, 2))) +static void igt_interactive_info(const char *format, ...) +{ + va_list args; + + if (!isatty(STDERR_FILENO)) + return; + + if (igt_log_level > IGT_LOG_INFO) + return; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + + +/** + * igt_progress: + * @header: header string to prepend to the progress indicator + * @i: work processed thus far + * @total: total amount of work + * + * This function draws a progress indicator, which is useful for running + * long-winded tests manually on the console. To avoid spamming logfiles in + * automated runs the progress indicator is suppressed when not running on a + * terminal. + */ +void igt_progress(const char *header, uint64_t i, uint64_t total) +{ + int divider = 200; + + if (i+1 >= total) { + igt_interactive_info("\r%s100%%\n", header); + return; + } + + if (total / 200 == 0) + divider = 1; + + /* only bother updating about every 0.5% */ + if (i % (total / divider) == 0) + igt_interactive_info("\r%s%3llu%%", header, + (long long unsigned)i * 100 / total); +} + +/** + * igt_print_activity: + * + * Print a '.' to indicate activity. This is printed without a newline and + * only if output is to a terminal. + */ +void igt_print_activity(void) +{ + igt_interactive_info("."); +} + +/* mappable aperture trasher helper */ +drm_intel_bo **trash_bos; +int num_trash_bos; + +/** + * igt_init_aperture_trashers: + * @bufmgr: libdrm buffer manager + * + * Initialize the aperture trasher using @bufmgr, which can then be run with + * igt_trash_aperture(). + */ +void igt_init_aperture_trashers(drm_intel_bufmgr *bufmgr) +{ + int i; + + num_trash_bos = gem_mappable_aperture_size() / (1024*1024); + + trash_bos = malloc(num_trash_bos * sizeof(drm_intel_bo *)); + igt_assert(trash_bos); + + for (i = 0; i < num_trash_bos; i++) + trash_bos[i] = drm_intel_bo_alloc(bufmgr, "trash bo", 1024*1024, 4096); +} + +/** + * igt_trash_aperture: + * + * Trash the aperture by walking a set of GTT memory mapped objects. + */ +void igt_trash_aperture(void) +{ + int i; + uint8_t *gtt_ptr; + + for (i = 0; i < num_trash_bos; i++) { + drm_intel_gem_bo_map_gtt(trash_bos[i]); + gtt_ptr = trash_bos[i]->virtual; + *gtt_ptr = 0; + drm_intel_gem_bo_unmap_gtt(trash_bos[i]); + } +} + +/** + * igt_cleanup_aperture_trashers: + * + * Clean up all aperture trasher state set up with igt_init_aperture_trashers(). + */ +void igt_cleanup_aperture_trashers(void) +{ + int i; + + for (i = 0; i < num_trash_bos; i++) + drm_intel_bo_unreference(trash_bos[i]); + + free(trash_bos); +} + +/** + * igt_system_suspend_autoresume: + * + * Execute a system suspend-to-mem cycle and automatically wake up again using + * the firmware's resume timer. + * + * This is very handy for implementing any kind of suspend/resume test. + */ +void igt_system_suspend_autoresume(void) +{ + int ret; + + /* FIXME: Simulation doesn't like suspend/resume, and not even a lighter + * approach using /sys/power/pm_test to just test our driver's callbacks + * seems to fare better. We need to investigate what's going on. */ + igt_skip_on_simulation(); + + ret = system("rtcwake -s 30 -m mem"); + igt_assert_f(ret == 0, "This failure means that something is wrong with the rtcwake tool " + "or how your distro is set up. This is not a i915.ko or i-g-t bug."); +} + +/** + * igt_system_hibernate_autoresume: + * + * Execute a system suspend-to-disk cycle and automatically wake up again using + * the firmware's resume timer. + * + * This is very handy for implementing any kind of hibernate/resume test. + */ +void igt_system_hibernate_autoresume(void) +{ + int ret; + + /* FIXME: I'm guessing simulation behaves the same way as with + * suspend/resume, but it might be prudent to make sure + */ + /* FIXME: Simulation doesn't like suspend/resume, and not even a lighter + * approach using /sys/power/pm_test to just test our driver's callbacks + * seems to fare better. We need to investigate what's going on. */ + igt_skip_on_simulation(); + + /* The timeout might need to be adjusted if hibernation takes too long + * or if we have to wait excessively long before resume + */ + ret = system("rtcwake -s 90 -m disk"); + igt_assert_f(ret == 0, "This failure means that something is wrong with the rtcwake tool " + "or how your distro is set up. This is not a i915.ko or i-g-t bug."); +} + +/** + * igt_drop_root: + * + * Drop root privileges and make sure it actually worked. Useful for tests + * which need to check security constraints. Note that this should only be + * called from manually forked processes, since the lack of root privileges + * will wreak havoc with the automatic cleanup handlers. + */ +void igt_drop_root(void) +{ + igt_assert(getuid() == 0); + + igt_assert(setgid(2) == 0); + igt_assert(setuid(2) == 0); + + igt_assert(getgid() == 2); + igt_assert(getuid() == 2); +} + +/** + * igt_debug_wait_for_keypress: + * @var: var lookup to to enable this wait + * + * Waits for a key press when run interactively and when the corresponding debug + * var is set in the --interactive-debug=<var> variable. Multiple keys + * can be specified as a comma-separated list or alternatively "all" if a wait + * should happen for all cases. + * + * When not connected to a terminal interactive_debug is ignored + * and execution immediately continues. + * + * This is useful for display tests where under certain situation manual + * inspection of the display is useful. Or when running a testcase in the + * background. + */ +void igt_debug_wait_for_keypress(const char *var) +{ + struct termios oldt, newt; + + if (!isatty(STDIN_FILENO)) + return; + + if (!igt_interactive_debug) + return; + + if (!strstr(igt_interactive_debug, var) && + !strstr(igt_interactive_debug, "all")) + return; + + igt_info("Press any key to continue ...\n"); + + tcgetattr ( STDIN_FILENO, &oldt ); + newt = oldt; + newt.c_lflag &= ~( ICANON | ECHO ); + tcsetattr ( STDIN_FILENO, TCSANOW, &newt ); + getchar(); + tcsetattr ( STDIN_FILENO, TCSANOW, &oldt ); +} + +#define POWER_DIR "/sys/devices/pci0000:00/0000:00:02.0/power" +/* We just leak this on exit ... */ +int pm_status_fd = -1; + +/** + * igt_setup_runtime_pm: + * + * Sets up the runtime PM helper functions and enables runtime PM. To speed up + * tests the autosuspend delay is set to 0. + * + * Returns: + * True if runtime pm is available, false otherwise. + */ +bool igt_setup_runtime_pm(void) +{ + int fd; + ssize_t size; + char buf[6]; + + if (pm_status_fd >= 0) + return true; + + /* The Audio driver can get runtime PM references, so we need to make + * sure its runtime PM is enabled, so it can release the refs and + * actually enable us to runtime suspend. */ + fd = open("/sys/module/snd_hda_intel/parameters/power_save", O_WRONLY); + if (fd >= 0) { + igt_assert(write(fd, "1\n", 2) == 2); + close(fd); + } + fd = open("/sys/bus/pci/devices/0000:00:03.0/power/control", O_WRONLY); + if (fd >= 0) { + igt_assert(write(fd, "auto\n", 5) == 5); + close(fd); + } + + /* Our implementation uses autosuspend. Try to set it to 0ms so the test + * suite goes faster and we have a higher probability of triggering race + * conditions. */ + fd = open(POWER_DIR "/autosuspend_delay_ms", O_WRONLY); + igt_assert_f(fd >= 0, + "Can't open " POWER_DIR "/autosuspend_delay_ms\n"); + + /* If we fail to write to the file, it means this system doesn't support + * runtime PM. */ + size = write(fd, "0\n", 2); + + close(fd); + + if (size != 2) + return false; + + /* We know we support runtime PM, let's try to enable it now. */ + fd = open(POWER_DIR "/control", O_RDWR); + igt_assert_f(fd >= 0, "Can't open " POWER_DIR "/control\n"); + + size = write(fd, "auto\n", 5); + igt_assert(size == 5); + + lseek(fd, 0, SEEK_SET); + size = read(fd, buf, ARRAY_SIZE(buf)); + igt_assert(size == 5); + igt_assert(strncmp(buf, "auto\n", 5) == 0); + + close(fd); + + pm_status_fd = open(POWER_DIR "/runtime_status", O_RDONLY); + igt_assert_f(pm_status_fd >= 0, + "Can't open " POWER_DIR "/runtime_status\n"); + + return true; +} + +/** + * igt_get_runtime_pm_status: + * + * Returns: The current runtime PM status. + */ +enum igt_runtime_pm_status igt_get_runtime_pm_status(void) +{ + ssize_t n_read; + char buf[32]; + + lseek(pm_status_fd, 0, SEEK_SET); + n_read = read(pm_status_fd, buf, ARRAY_SIZE(buf)); + igt_assert(n_read >= 0); + buf[n_read] = '\0'; + + if (strncmp(buf, "suspended\n", n_read) == 0) + return IGT_RUNTIME_PM_STATUS_SUSPENDED; + else if (strncmp(buf, "active\n", n_read) == 0) + return IGT_RUNTIME_PM_STATUS_ACTIVE; + else if (strncmp(buf, "suspending\n", n_read) == 0) + return IGT_RUNTIME_PM_STATUS_SUSPENDING; + else if (strncmp(buf, "resuming\n", n_read) == 0) + return IGT_RUNTIME_PM_STATUS_RESUMING; + + igt_assert_f(false, "Unknown status %s\n", buf); + return IGT_RUNTIME_PM_STATUS_UNKNOWN; +} + +/** + * igt_wait_for_pm_status: + * @status: desired runtime PM status + * + * Waits until for the driver to switch to into the desired runtime PM status, + * with a 10 second timeout. + * + * Some subtests call this function while the signal helper is active, so we + * can't assume each usleep() call will sleep for 100ms. + * + * Returns: + * True if the desired runtime PM status was attained, false if the operation + * timed out. + */ +bool igt_wait_for_pm_status(enum igt_runtime_pm_status status) +{ + struct timeval start, end, diff; + + igt_assert(gettimeofday(&start, NULL) == 0); + do { + if (igt_get_runtime_pm_status() == status) + return true; + + usleep(100 * 1000); + + igt_assert(gettimeofday(&end, NULL) == 0); + timersub(&end, &start, &diff); + } while (diff.tv_sec < 10); + + return false; +} + +/* Functions with prefix kmstest_ independent of cairo library are pulled out + * from file igt_kms.c since this file is skipped in lib/Android.mk when flag + * ANDROID_HAS_CAIRO is 0. This ensures the usability of these functions even + * when cairo library is not present on Android. + */ + +struct type_name { + int type; + const char *name; +}; + +#define type_name_fn(res) \ +const char * kmstest_##res##_str(int type) { \ + unsigned int i; \ + for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \ + if (res##_names[i].type == type) \ + return res##_names[i].name; \ + } \ + return "(invalid)"; \ +} + +struct type_name encoder_type_names[] = { + { DRM_MODE_ENCODER_NONE, "none" }, + { DRM_MODE_ENCODER_DAC, "DAC" }, + { DRM_MODE_ENCODER_TMDS, "TMDS" }, + { DRM_MODE_ENCODER_LVDS, "LVDS" }, + { DRM_MODE_ENCODER_TVDAC, "TVDAC" }, +}; + +type_name_fn(encoder_type) + +struct type_name connector_status_names[] = { + { DRM_MODE_CONNECTED, "connected" }, + { DRM_MODE_DISCONNECTED, "disconnected" }, + { DRM_MODE_UNKNOWNCONNECTION, "unknown" }, +}; + +type_name_fn(connector_status) + +struct type_name connector_type_names[] = { + { DRM_MODE_CONNECTOR_Unknown, "unknown" }, + { DRM_MODE_CONNECTOR_VGA, "VGA" }, + { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, + { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, + { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, + { DRM_MODE_CONNECTOR_Composite, "composite" }, + { DRM_MODE_CONNECTOR_SVIDEO, "s-video" }, + { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, + { DRM_MODE_CONNECTOR_Component, "component" }, + { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" }, + { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, + { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, + { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, + { DRM_MODE_CONNECTOR_TV, "TV" }, + { DRM_MODE_CONNECTOR_eDP, "eDP" }, +}; + +type_name_fn(connector_type) + + +/** + * igt_lock_mem: + * @size: the amount of memory to lock into RAM, in MB + * + * Allocate @size MB of memory and lock it into RAM. This releases any + * previously locked memory. + * + * Use #igt_unlock_mem to release the currently locked memory. + */ +static char *locked_mem; +static size_t locked_size; + +void igt_lock_mem(size_t size) +{ + long pagesize = sysconf(_SC_PAGESIZE); + size_t i; + int ret; + + if (size == 0) { + return; + } + + if (locked_mem) { + igt_unlock_mem(); + igt_warn("Unlocking previously locked memory.\n"); + } + + locked_size = size * 1024 * 1024; + + locked_mem = malloc(locked_size); + igt_require_f(locked_mem, + "Could not allocate enough memory to lock.\n"); + + /* write into each page to ensure it is allocated */ + for (i = 0; i < locked_size; i += pagesize) + locked_mem[i] = i; + + ret = mlock(locked_mem, locked_size); + igt_assert_f(ret == 0, "Could not lock memory into RAM.\n"); +} + +/** + * igt_unlock_mem: + * + * Release and free the RAM used by #igt_lock_mem. + */ +void igt_unlock_mem(void) +{ + if (!locked_mem) + return; + + munlock(locked_mem, locked_size); + + free(locked_mem); + locked_mem = NULL; +} diff --git a/lib/igt_aux.h b/lib/igt_aux.h new file mode 100644 index 0000000..0c361f2 --- /dev/null +++ b/lib/igt_aux.h @@ -0,0 +1,104 @@ +/* + * Copyright © 2014, 2015 Intel Corporation + * + * 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: + * Daniel Vetter + * + */ + +#ifndef IGT_AUX_H +#define IGT_AUX_H + +#include +#include + +extern drm_intel_bo **trash_bos; +extern int num_trash_bos; + +/* auxialiary igt helpers from igt_aux.c */ +/* generally useful helpers */ +void igt_fork_signal_helper(void); +void igt_stop_signal_helper(void); + +void igt_exchange_int(void *array, unsigned i, unsigned j); +void igt_permute_array(void *array, unsigned size, + void (*exchange_func)(void *array, + unsigned i, + unsigned j)); +void igt_progress(const char *header, uint64_t i, uint64_t total); +void igt_print_activity(void); +bool igt_check_boolean_env_var(const char *env_var, bool default_value); + +bool igt_aub_dump_enabled(void); + +/* helpers based upon the libdrm buffer manager */ +void igt_init_aperture_trashers(drm_intel_bufmgr *bufmgr); +void igt_trash_aperture(void); +void igt_cleanup_aperture_trashers(void); + +/* suspend/hibernate and auto-resume system */ +void igt_system_suspend_autoresume(void); +void igt_system_hibernate_autoresume(void); + +/* dropping priviledges */ +void igt_drop_root(void); + +void igt_debug_wait_for_keypress(const char *var); + +enum igt_runtime_pm_status { + IGT_RUNTIME_PM_STATUS_ACTIVE, + IGT_RUNTIME_PM_STATUS_SUSPENDED, + IGT_RUNTIME_PM_STATUS_SUSPENDING, + IGT_RUNTIME_PM_STATUS_RESUMING, + IGT_RUNTIME_PM_STATUS_UNKNOWN, +}; +bool igt_setup_runtime_pm(void); +enum igt_runtime_pm_status igt_get_runtime_pm_status(void); +bool igt_wait_for_pm_status(enum igt_runtime_pm_status status); + +/* sysinfo cross-arch wrappers from intel_os.c */ + +/* These are separate to allow easier testing when porting, see the comment at + * the bottom of intel_os.c. */ +void intel_purge_vm_caches(void); +uint64_t intel_get_avail_ram_mb(void); +uint64_t intel_get_total_ram_mb(void); +uint64_t intel_get_total_swap_mb(void); + +void intel_require_memory(uint32_t count, uint32_t size, unsigned mode); +#define CHECK_RAM 0x1 +#define CHECK_SWAP 0x2 + + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) + +#define igt_swap(a, b) do { \ + typeof(a) _tmp = (a); \ + (a) = (b); \ + (b) = _tmp; \ +} while (0) + +void igt_lock_mem(size_t size); +void igt_unlock_mem(void); + +#endif /* IGT_AUX_H */ diff --git a/lib/igt_core.c b/lib/igt_core.c new file mode 100644 index 0000000..8f75e48 --- /dev/null +++ b/lib/igt_core.c @@ -0,0 +1,1689 @@ +/* + * Copyright © 2007, 2011, 2013, 2014 Intel Corporation + * + * 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: + * Eric Anholt + * Daniel Vetter + * + */ + +#ifndef ANDROID +#define _GNU_SOURCE +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "drmtest.h" +#include "intel_chipset.h" +#include "intel_io.h" +#include "igt_debugfs.h" +#include "version.h" +#include "config.h" + +#include "igt_core.h" +#include "igt_aux.h" + +#ifdef HAVE_LIBGEN_H +#include /* for basename() on Solaris */ +#endif + +/** + * SECTION:igt_core + * @short_description: Core i-g-t testing support + * @title: i-g-t core + * @include: igt_core.h + * + * This library implements the core of the i-g-t test support infrastructure. + * Main features are the subtest enumeration, cmdline option parsing helpers for + * subtest handling and various helpers to structure testcases with subtests and + * handle subtest test results. + * + * Auxiliary code provides exit handlers, support for forked processes with test + * result propagation. Other generally useful functionality includes optional + * structure logging infrastructure and some support code for running reduced + * test set on in simulated hardware environments. + * + * When writing tests with subtests it is extremely important that nothing + * interferes with the subtest enumeration. In i-g-t subtests are enumerated at + * runtime, which allows powerful testcase enumeration. But it makes subtest + * enumeration a bit more tricky since the test code needs to be careful to + * never run any code which might fail (like trying to do privileged operations + * or opening device driver nodes). + * + * To allow this i-g-t provides #igt_fixture code blocks for setup code outside + * of subtests and automatically skips the subtest code blocks themselves. For + * special cases igt_only_list_subtests() is also provided. + * + * # Magic Control Blocks + * + * i-g-t makes heavy use of C macros which serve as magic control blocks. They + * work fairly well and transparently but since C doesn't have full-blown + * closures there are caveats: + * + * - Asynchronous blocks which are used to spawn children internally use fork(). + * Which means that nonsensical control flow like jumping out of the control + * block is possible, but it will badly confuse the i-g-t library code. And of + * course all caveats of a real fork() call apply, namely that file + * descriptors are copied, but still point at the original file. This will + * terminally upset the libdrm buffer manager if both parent and child keep on + * using the same open instance of the drm device. Usually everything related + * to interacting with the kernel driver must be reinitialized to avoid such + * issues. + * + * - Code blocks with magic control flow are implemented with setjmp() and + * longjmp(). This applies to #igt_fixture and #igt_subtest blocks and all the + * three variants to finish test: igt_success(), igt_skip() and igt_fail(). + * Mostly this is of no concern, except when such a control block changes + * stack variables defined in the same function as the control block resides. + * Any store/load behaviour after a longjmp() is ill-defined for these + * variables. Avoid such code. + * + * Quoting the man page for longjmp(): + * + * "The values of automatic variables are unspecified after a call to + * longjmp() if they meet all the following criteria:" + * - "they are local to the function that made the corresponding setjmp() call; + * - "their values are changed between the calls to setjmp() and longjmp(); and + * - "they are not declared as volatile." + * + * # Best Practices for Test Helper Libraries Design + * + * Kernel tests itself tend to have fairly complex logic already. It is + * therefore paramount that helper code, both in libraries and test-private + * functions, add as little boilerplate code to the main test logic as possible. + * But then dense code is hard to understand without constantly consulting + * the documentation and implementation of all the helper functions if it + * doesn't follow some clear patterns. Hence follow these established best + * practices: + * + * - Make extensive use of the implicit control flow afforded by igt_skip(), + * igt_fail and igt_success(). When dealing with optional kernel features + * combine igt_skip() with igt_fail() to skip when the kernel support isn't + * available but fail when anything else goes awry. void should be the most + * common return type in all your functions, except object constructors of + * course. + * + * - The main test logic should have no explicit control flow for failure + * conditions, but instead such assumptions should be written in a declarative + * style. Use one of the many macros which encapsulate i-g-t's implicit + * control flow. Pick the most suitable one to have as much debug output as + * possible without polluting the code unnecessarily. For example + * igt_assert_cmpint() for comparing integers or do_ioctl() for running ioctls + * and checking their results. Feel free to add new ones to the library or + * wrap up a set of checks into a private function to further condense your + * test logic. + * + * - When adding a new feature test function which uses igt_skip() internally, + * use the <prefix>_require_<feature_name> naming scheme. When you + * instead add a feature test function which returns a boolean, because your + * main test logic must take different actions depending upon the feature's + * availability, then instead use the <prefix>_has_<feature_name>. + * + * - As already mentioned eschew explicit error handling logic as much as + * possible. If your test absolutely has to handle the error of some function + * the customary naming pattern is to prefix those variants with __. Try to + * restrict explicit error handling to leaf functions. For the main test flow + * simply pass the expected error condition down into your helper code, which + * results in tidy and declarative test logic. + * + * - Make your library functions as simple to use as possible. Automatically + * register cleanup handlers through igt_install_exit_handler(). Reduce the + * amount of setup boilerplate needed by using implicit singletons and lazy + * structure initialization and similar design patterns. + * + * - Don't shy away from refactoring common code, even when there are just 2-3 + * users and even if it's not a net reduction in code. As long as it helps to + * remove boilerplate and makes the code more declarative the resulting + * clearer test flow is worth it. All i-g-t library code has been organically + * extracted from testcases in this fashion. + * + * - For general coding style issues please follow the kernel's rules laid out + * in + * [CodingStyle](https://www.kernel.org/doc/Documentation/CodingStyle). + * + * # Interface with Testrunners + * + * i-g-t testcase are all executables which should be run as root on an + * otherwise completely idle system. The test status is reflected in the + * exitcode. #IGT_EXIT_SUCCESS means "success", #IGT_EXIT_SKIP "skip", + * #IGT_EXIT_TIMEOUT that some operation "timed out". All other exit codes + * encode a failed test result, including any abnormal termination of the test + * (e.g. by SIGKILL). + * + * On top of that tests may report unexpected results and minor issues to + * stderr. If stderr is non-empty the test result should be treated as "warn". + * + * The test lists are generated at build time. Simple testcases are listed in + * tests/single-tests.txt and tests with subtests are listed in + * tests/multi-tests.txt. When running tests with subtest from a test runner it + * is recommend to run each subtest individually, since otherwise the return + * code will only reflect the overall result. + * + * To do that obtain the lists of subtests with "--list-subtests", which can be + * run as non-root and doesn't require the i915 driver to be loaded (or any + * intel gpu to be present). Then individual subtests can be run with + * "--run-subtest". Usage help for tests with subtests can be obtained with the + * "--help" command line option. + */ + +static unsigned int exit_handler_count; + +/* subtests helpers */ +static bool list_subtests = false; +static char *run_single_subtest = NULL; +static bool run_single_subtest_found = false; +static const char *in_subtest = NULL; +static struct timespec subtest_time; +static bool in_fixture = false; +static bool test_with_subtests = false; +static enum { + CONT = 0, SKIP, FAIL +} skip_subtests_henceforth = CONT; + +/* fork support state */ +pid_t *test_children; +int num_test_children; +int test_children_sz; +bool test_child; + +enum { + OPT_LIST_SUBTESTS, + OPT_RUN_SUBTEST, + OPT_DESCRIPTION, + OPT_DEBUG, + OPT_INTERACTIVE_DEBUG, + OPT_HELP = 'h' +}; + +static int igt_exitcode = IGT_EXIT_SUCCESS; +static const char *command_str; + +static char* igt_log_domain_filter; +static struct { + char *entries[256]; + uint8_t start, end; +} log_buffer; +static pthread_mutex_t log_buffer_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void _igt_log_buffer_append(char *line) +{ + pthread_mutex_lock(&log_buffer_mutex); + + free(log_buffer.entries[log_buffer.end]); + log_buffer.entries[log_buffer.end] = line; + log_buffer.end++; + if (log_buffer.end == log_buffer.start) + log_buffer.start++; + + pthread_mutex_unlock(&log_buffer_mutex); +} + +static void _igt_log_buffer_reset(void) +{ + pthread_mutex_lock(&log_buffer_mutex); + + log_buffer.start = log_buffer.end = 0; + + pthread_mutex_unlock(&log_buffer_mutex); +} + +static void _igt_log_buffer_dump(void) +{ + uint8_t i; + + if (in_subtest) + fprintf(stderr, "Subtest %s failed.\n", in_subtest); + else + fprintf(stderr, "Test %s failed.\n", command_str); + + if (log_buffer.start == log_buffer.end) { + fprintf(stderr, "No log.\n"); + return; + } + + pthread_mutex_lock(&log_buffer_mutex); + + fprintf(stderr, "Log Start\n"); + + i = log_buffer.start; + do { + char *last_line = log_buffer.entries[i]; + fprintf(stderr, "%s", last_line); + i++; + } while (i != log_buffer.start && i != log_buffer.end); + + /* reset the buffer */ + log_buffer.start = log_buffer.end = 0; + + pthread_mutex_unlock(&log_buffer_mutex); + + fprintf(stderr, "Log End\n"); +} + +__attribute__((format(printf, 1, 2))) +static void kmsg(const char *format, ...) +#define KERN_EMER "<0>" +#define KERN_ALERT "<1>" +#define KERN_CRIT "<2>" +#define KERN_ERR "<3>" +#define KERN_WARNING "<4>" +#define KERN_NOTICE "<5>" +#define KERN_INFO "<6>" +#define KERN_DEBUG "<7>" +{ + va_list ap; + FILE *file; + + file = fopen("/dev/kmsg", "w"); + if (file == NULL) + return; + + va_start(ap, format); + vfprintf(file, format, ap); + va_end(ap); + + fclose(file); +} + +static void gettime(struct timespec *ts) +{ + memset(ts, 0, sizeof(*ts)); + +#ifdef CLOCK_MONOTONIC_COARSE + if (clock_gettime(CLOCK_MONOTONIC_COARSE, ts)) +#endif + clock_gettime(CLOCK_MONOTONIC, ts); +} + +bool __igt_fixture(void) +{ + assert(!in_fixture); + + if (igt_only_list_subtests()) + return false; + + if (skip_subtests_henceforth) + return false; + + in_fixture = true; + return true; +} + +void __igt_fixture_complete(void) +{ + assert(in_fixture); + + in_fixture = false; +} + +void __igt_fixture_end(void) +{ + assert(in_fixture); + + in_fixture = false; + longjmp(igt_subtest_jmpbuf, 1); +} + +/* + * Some of the IGT tests put quite a lot of pressure on memory and when + * running on Android they are sometimes killed by the Android low memory killer. + * This seems to be due to some incompatibility between the kswapd free memory + * targets and the way the lowmemorykiller assesses free memory. + * The low memory killer really isn't usefull in this context and has no + * interaction with the gpu driver that we are testing, so the following + * function is used to disable it by modifying one of its module parameters. + * We still have the normal linux oom killer to protect the kernel. + * Apparently it is also possible for the lowmemorykiller to get included + * in some linux distributions; so rather than check for Android we directly + * check for the existence of the module parameter we want to adjust. + * + * In future, if we can get the lowmemorykiller to play nicely then we can + * remove this hack. + */ +static void low_mem_killer_disable(bool disable) +{ + static const char* adj_fname="/sys/module/lowmemorykiller/parameters/adj"; + static const char no_lowmem_killer[] = "9999"; + int fd; + struct stat buf; + /* The following must persist across invocations */ + static char prev_adj_scores[256]; + static int adj_scores_len = 0; + + /* capture the permissions bits for the lowmemkiller adj pseudo-file. + * Bail out if the stat fails; it probably means that there is no + * lowmemorykiller, but in any case we're doomed. */ + if (stat(adj_fname, &buf)) { + igt_assert(errno == ENOENT); + return; + } + + /* make sure the file can be read/written - by default it is write-only */ + chmod(adj_fname, S_IRUSR | S_IWUSR); + + if (disable) { + /* read the current oom adj parameters for lowmemorykiller */ + fd = open(adj_fname, O_RDWR); + igt_assert(fd != -1); + adj_scores_len = read(fd, (void*)prev_adj_scores, 255); + igt_assert(adj_scores_len > 0); + + /* writing 9999 to this module parameter effectively diables the + * low memory killer. This is not a real file, so we dont need to + * seek to the start or truncate it */ + write(fd, no_lowmem_killer, sizeof(no_lowmem_killer)); + close(fd); + } else { + /* just re-enstate the original settings */ + fd = open(adj_fname, O_WRONLY); + igt_assert(fd != -1); + write(fd, prev_adj_scores, adj_scores_len); + close(fd); + } + + /* re-enstate the file permissions */ + chmod(adj_fname, buf.st_mode); +} + +bool igt_exit_called; +static void common_exit_handler(int sig) +{ + low_mem_killer_disable(false); + + /* When not killed by a signal check that igt_exit() has been properly + * called. */ + assert(sig != 0 || igt_exit_called); +} + +static void print_test_description(void) +{ + if (&__igt_test_description) + printf("%s\n", __igt_test_description); +} + +static void print_version(void) +{ + struct utsname uts; + + if (list_subtests) + return; + + uname(&uts); + + fprintf(stdout, "IGT-Version: %s-%s (%s) (%s: %s %s)\n", PACKAGE_VERSION, + IGT_GIT_SHA1, TARGET_CPU_PLATFORM, + uts.sysname, uts.release, uts.machine); +} + +static void print_usage(const char *help_str, bool output_on_stderr) +{ + FILE *f = output_on_stderr ? stderr : stdout; + + fprintf(f, "Usage: %s [OPTIONS]\n", command_str); + fprintf(f, " --list-subtests\n" + " --run-subtest \n" + " --debug[=log-domain]\n" + " --interactive-debug[=domain]\n" + " --help-description\n" + " --help\n"); + if (help_str) + fprintf(f, "%s\n", help_str); +} + + +static void oom_adjust_for_doom(void) +{ + int fd; + const char always_kill[] = "1000"; + + fd = open("/proc/self/oom_score_adj", O_WRONLY); + igt_assert(fd != -1); + igt_assert(write(fd, always_kill, sizeof(always_kill)) == sizeof(always_kill)); + close(fd); + + low_mem_killer_disable(true); +} + +static int common_init(int *argc, char **argv, + const char *extra_short_opts, + struct option *extra_long_opts, + const char *help_str, + igt_opt_handler_t extra_opt_handler) +{ + int c, option_index = 0, i, x; + static struct option long_options[] = { + {"list-subtests", 0, 0, OPT_LIST_SUBTESTS}, + {"run-subtest", 1, 0, OPT_RUN_SUBTEST}, + {"help-description", 0, 0, OPT_DESCRIPTION}, + {"debug", optional_argument, 0, OPT_DEBUG}, + {"interactive-debug", optional_argument, 0, OPT_INTERACTIVE_DEBUG}, + {"help", 0, 0, OPT_HELP}, + {0, 0, 0, 0} + }; + char *short_opts; + const char *std_short_opts = "h"; + struct option *combined_opts; + int extra_opt_count; + int all_opt_count; + int ret = 0; + char *env = getenv("IGT_LOG_LEVEL"); + + if (env) { + if (strcmp(env, "debug") == 0) + igt_log_level = IGT_LOG_DEBUG; + else if (strcmp(env, "info") == 0) + igt_log_level = IGT_LOG_INFO; + else if (strcmp(env, "warn") == 0) + igt_log_level = IGT_LOG_WARN; + else if (strcmp(env, "none") == 0) + igt_log_level = IGT_LOG_NONE; + } + + command_str = argv[0]; + if (strrchr(command_str, '/')) + command_str = strrchr(command_str, '/') + 1; + + /* First calculate space for all passed-in extra long options */ + all_opt_count = 0; + while (extra_long_opts && extra_long_opts[all_opt_count].name) { + + /* check for conflicts with standard long option values */ + for (i = 0; long_options[i].name; i++) + if (extra_long_opts[all_opt_count].val == long_options[i].val) + igt_warn("Conflicting long option values between --%s and --%s\n", + extra_long_opts[all_opt_count].name, + long_options[i].name); + + /* check for conflicts with short options */ + if (extra_long_opts[all_opt_count].val != ':' + && strchr(std_short_opts, extra_long_opts[all_opt_count].val)) { + igt_warn("Conflicting long and short option values between --%s and -%s\n", + extra_long_opts[all_opt_count].name, + long_options[i].name); + } + + + all_opt_count++; + } + extra_opt_count = all_opt_count; + + /* check for conflicts in extra short options*/ + for (i = 0; extra_short_opts && extra_short_opts[i]; i++) { + + if (extra_short_opts[i] == ':') + continue; + + /* check for conflicts with standard short options */ + if (strchr(std_short_opts, extra_short_opts[i])) + igt_warn("Conflicting short option: -%c\n", std_short_opts[i]); + + /* check for conflicts with standard long option values */ + for (x = 0; long_options[x].name; x++) + if (long_options[x].val == extra_short_opts[i]) + igt_warn("Conflicting short option and long option value: --%s and -%c\n", + long_options[x].name, extra_short_opts[i]); + } + + all_opt_count += ARRAY_SIZE(long_options); + + combined_opts = malloc(all_opt_count * sizeof(*combined_opts)); + memcpy(combined_opts, extra_long_opts, + extra_opt_count * sizeof(*combined_opts)); + + /* Copy the subtest long options (and the final NULL entry) */ + memcpy(&combined_opts[extra_opt_count], long_options, + ARRAY_SIZE(long_options) * sizeof(*combined_opts)); + + ret = asprintf(&short_opts, "%s%s", + extra_short_opts ? extra_short_opts : "", + std_short_opts); + assert(ret >= 0); + + while ((c = getopt_long(*argc, argv, short_opts, combined_opts, + &option_index)) != -1) { + switch(c) { + case OPT_INTERACTIVE_DEBUG: + if (optarg && strlen(optarg) > 0) + igt_interactive_debug = strdup(optarg); + else + igt_interactive_debug = "all"; + break; + case OPT_DEBUG: + igt_log_level = IGT_LOG_DEBUG; + if (optarg && strlen(optarg) > 0) + igt_log_domain_filter = strdup(optarg); + break; + case OPT_LIST_SUBTESTS: + if (!run_single_subtest) + list_subtests = true; + break; + case OPT_RUN_SUBTEST: + if (!list_subtests) + run_single_subtest = strdup(optarg); + break; + case OPT_DESCRIPTION: + print_test_description(); + ret = -1; + goto out; + case OPT_HELP: + print_usage(help_str, false); + ret = -1; + goto out; + case '?': + print_usage(help_str, true); + ret = -2; + goto out; + default: + ret = extra_opt_handler(c, option_index); + if (ret) + goto out; + } + } + +out: + free(short_opts); + free(combined_opts); + + /* exit immediately if this test has no subtests and a subtest or the + * list of subtests has been requested */ + if (!test_with_subtests) { + if (run_single_subtest) { + igt_warn("Unknown subtest: %s\n", run_single_subtest); + exit(IGT_EXIT_INVALID); + } + if (list_subtests) + exit(IGT_EXIT_INVALID); + } + + if (ret < 0) + /* exit with no error for -h/--help */ + exit(ret == -1 ? 0 : IGT_EXIT_INVALID); + + if (!list_subtests) { + kmsg(KERN_INFO "%s: executing\n", command_str); + print_version(); + + oom_adjust_for_doom(); + } + + /* install exit handler, to ensure we clean up */ + igt_install_exit_handler(common_exit_handler); + + if (!test_with_subtests) + gettime(&subtest_time); + + for (i = 0; (optind + i) < *argc; i++) + argv[i + 1] = argv[optind + i]; + + *argc = *argc - optind + 1; + + return ret; +} + + +/** + * igt_subtest_init_parse_opts: + * @argc: argc from the test's main() + * @argv: argv from the test's main() + * @extra_short_opts: getopt_long() compliant list with additional short options + * @extra_long_opts: getopt_long() compliant list with additional long options + * @help_str: help string for the additional options + * @extra_opt_handler: handler for the additional options + * + * This function handles the subtest related cmdline options and allows an + * arbitrary set of additional options. This is useful for tests which have + * additional knobs to tune when run manually like the number of rounds execute + * or the size of the allocated buffer objects. + * + * Tests without special needs should just use igt_subtest_init() or use + * #igt_main directly instead of their own main() function. + * + * Returns: Forwards any option parsing errors from getopt_long. + */ +int igt_subtest_init_parse_opts(int *argc, char **argv, + const char *extra_short_opts, + struct option *extra_long_opts, + const char *help_str, + igt_opt_handler_t extra_opt_handler) +{ + int ret; + + test_with_subtests = true; + ret = common_init(argc, argv, extra_short_opts, extra_long_opts, + help_str, extra_opt_handler); + + return ret; +} + +enum igt_log_level igt_log_level = IGT_LOG_INFO; + +/** + * igt_simple_init_parse_opts: + * @argc: argc from the test's main() + * @argv: argv from the test's main() + * @extra_short_opts: getopt_long() compliant list with additional short options + * @extra_long_opts: getopt_long() compliant list with additional long options + * @help_str: help string for the additional options + * @extra_opt_handler: handler for the additional options + * + * This initializes a simple test without any support for subtests and allows + * an arbitrary set of additional options. + */ +void igt_simple_init_parse_opts(int *argc, char **argv, + const char *extra_short_opts, + struct option *extra_long_opts, + const char *help_str, + igt_opt_handler_t extra_opt_handler) +{ + common_init(argc, argv, extra_short_opts, extra_long_opts, help_str, + extra_opt_handler); +} + +/* + * Note: Testcases which use these helpers MUST NOT output anything to stdout + * outside of places protected by igt_run_subtest checks - the piglit + * runner adds every line to the subtest list. + */ +bool __igt_run_subtest(const char *subtest_name) +{ + int i; + + assert(!in_subtest); + assert(!in_fixture); + assert(test_with_subtests); + + /* check the subtest name only contains a-z, A-Z, 0-9, '-' and '_' */ + for (i = 0; subtest_name[i] != '\0'; i++) + if (subtest_name[i] != '_' && subtest_name[i] != '-' + && !isalnum(subtest_name[i])) { + igt_critical("Invalid subtest name \"%s\".\n", + subtest_name); + igt_exit(); + } + + if (list_subtests) { + printf("%s\n", subtest_name); + return false; + } + + if (run_single_subtest) { + if (strcmp(subtest_name, run_single_subtest) != 0) + return false; + else + run_single_subtest_found = true; + } + + if (skip_subtests_henceforth) { + printf("Subtest %s: %s\n", subtest_name, + skip_subtests_henceforth == SKIP ? + "SKIP" : "FAIL"); + return false; + } + + kmsg(KERN_INFO "%s: starting subtest %s\n", command_str, subtest_name); + igt_debug("Starting subtest: %s\n", subtest_name); + + _igt_log_buffer_reset(); + + gettime(&subtest_time); + return (in_subtest = subtest_name); +} + +/** + * igt_subtest_name: + * + * Returns: The name of the currently executed subtest or NULL if called from + * outside a subtest block. + */ +const char *igt_subtest_name(void) +{ + return in_subtest; +} + +/** + * igt_only_list_subtests: + * + * Returns: Returns true if only subtest should be listed and any setup code + * must be skipped, false otherwise. + */ +bool igt_only_list_subtests(void) +{ + return list_subtests; +} + +static bool skipped_one = false; +static bool succeeded_one = false; +static bool failed_one = false; + +static void exit_subtest(const char *) __attribute__((noreturn)); +static void exit_subtest(const char *result) +{ + struct timespec now; + double elapsed; + + gettime(&now); + elapsed = now.tv_sec - subtest_time.tv_sec; + elapsed += (now.tv_nsec - subtest_time.tv_nsec) * 1e-9; + + printf("Subtest %s: %s (%.3fs)\n", in_subtest, result, elapsed); + in_subtest = NULL; + longjmp(igt_subtest_jmpbuf, 1); +} + +/** + * igt_skip: + * @f: format string + * @...: optional arguments used in the format string + * + * Subtest aware test skipping. The format string is printed to stderr as the + * reason why the test skipped. + * + * For tests with subtests this will either bail out of the current subtest or + * mark all subsequent subtests as SKIP (presuming some global setup code + * failed). + * + * For normal tests without subtest it will directly exit. + */ +void igt_skip(const char *f, ...) +{ + va_list args; + skipped_one = true; + + assert(!test_child); + + if (!igt_only_list_subtests()) { + va_start(args, f); + vprintf(f, args); + va_end(args); + } + + if (in_subtest) { + exit_subtest("SKIP"); + } else if (test_with_subtests) { + skip_subtests_henceforth = SKIP; + assert(in_fixture); + __igt_fixture_end(); + } else { + igt_exitcode = IGT_EXIT_SKIP; + igt_exit(); + } +} + +void __igt_skip_check(const char *file, const int line, + const char *func, const char *check, + const char *f, ...) +{ + va_list args; + int err = errno; + char *err_str = NULL; + + if (err) + asprintf(&err_str, "Last errno: %i, %s\n", err, strerror(err)); + + if (f) { + static char *buf; + + /* igt_skip never returns, so try to not leak too badly. */ + if (buf) + free(buf); + + va_start(args, f); + vasprintf(&buf, f, args); + va_end(args); + + igt_skip("Test requirement not met in function %s, file %s:%i:\n" + "Test requirement: %s\n%s" + "%s", + func, file, line, check, buf, err_str ?: ""); + } else { + igt_skip("Test requirement not met in function %s, file %s:%i:\n" + "Test requirement: %s\n" + "%s", + func, file, line, check, err_str ?: ""); + } +} + +/** + * igt_success: + * + * Complete a (subtest) as successful + * + * This bails out of a subtests and marks it as successful. For global tests it + * it won't bail out of anything. + */ +void igt_success(void) +{ + succeeded_one = true; + if (in_subtest) + exit_subtest("SUCCESS"); +} + +/** + * igt_fail: + * @exitcode: exitcode + * + * Fail a testcase. The exitcode is used as the exit code of the test process. + * It may not be 0 (which indicates success) or 77 (which indicates a skipped + * test). + * + * For tests with subtests this will either bail out of the current subtest or + * mark all subsequent subtests as FAIL (presuming some global setup code + * failed). + * + * For normal tests without subtest it will directly exit with the given + * exitcode. + */ +void igt_fail(int exitcode) +{ + assert(exitcode != IGT_EXIT_SUCCESS && exitcode != IGT_EXIT_SKIP); + + if (!failed_one) + igt_exitcode = exitcode; + + failed_one = true; + + /* Silent exit, parent will do the yelling. */ + if (test_child) + exit(exitcode); + + _igt_log_buffer_dump(); + + if (in_subtest) { + if (exitcode == IGT_EXIT_TIMEOUT) + exit_subtest("TIMEOUT"); + else + exit_subtest("FAIL"); + } else { + assert(!test_with_subtests || in_fixture); + + if (in_fixture) { + skip_subtests_henceforth = FAIL; + __igt_fixture_end(); + } + + igt_exit(); + } +} + +static bool run_under_gdb(void) +{ + char buf[1024]; + + sprintf(buf, "/proc/%d/exe", getppid()); + return (readlink (buf, buf, sizeof (buf)) != -1 && + strncmp(basename(buf), "gdb", 3) == 0); +} + +void __igt_fail_assert(int exitcode, const char *domain, const char *file, + const int line, const char *func, const char *assertion, + const char *f, ...) +{ + va_list args; + int err = errno; + + igt_log(domain, IGT_LOG_CRITICAL, + "Test assertion failure function %s, file %s:%i:\n", func, file, + line); + igt_log(domain, IGT_LOG_CRITICAL, "Failed assertion: %s\n", assertion); + if (err) + igt_log(domain, IGT_LOG_CRITICAL, "Last errno: %i, %s\n", err, + strerror(err)); + + if (f) { + va_start(args, f); + igt_vlog(domain, IGT_LOG_CRITICAL, f, args); + va_end(args); + } + + if (run_under_gdb()) + abort(); + igt_fail(exitcode); +} + +/** + * igt_exit: + * + * exit() for both types (simple and with subtests) of i-g-t tests. + * + * This will exit the test with the right exit code when subtests have been + * skipped. For normal tests it exits with a successful exit code, presuming + * everything has worked out. For subtests it also checks that at least one + * subtest has been run (save when only listing subtests. + * + * It is an error to normally exit a test calling igt_exit() - without it the + * result reporting will be wrong. To avoid such issues it is highly recommended + * to use #igt_main or #igt_simple_main instead of a hand-rolled main() function. + */ +void igt_exit(void) +{ + igt_exit_called = true; + + if (run_single_subtest && !run_single_subtest_found) { + igt_warn("Unknown subtest: %s\n", run_single_subtest); + exit(IGT_EXIT_INVALID); + } + + + if (igt_only_list_subtests()) + exit(IGT_EXIT_SUCCESS); + + kmsg(KERN_INFO "%s: exiting, ret=%d\n", command_str, igt_exitcode); + igt_debug("Exiting with status code %d\n", igt_exitcode); + + if (!test_with_subtests) { + struct timespec now; + double elapsed; + const char *result; + + gettime(&now); + elapsed = now.tv_sec - subtest_time.tv_sec; + elapsed += (now.tv_nsec - subtest_time.tv_nsec) * 1e-9; + + switch (igt_exitcode) { + case IGT_EXIT_SUCCESS: + result = "SUCCESS"; + break; + case IGT_EXIT_TIMEOUT: + result = "TIMEOUT"; + break; + case IGT_EXIT_SKIP: + result = "SKIP"; + break; + default: + result = "FAIL"; + } + + + printf("%s (%.3fs)\n", result, elapsed); + exit(igt_exitcode); + } + + /* Calling this without calling one of the above is a failure */ + assert(skipped_one || succeeded_one || failed_one); + + if (failed_one) + exit(igt_exitcode); + else if (succeeded_one) + exit(IGT_EXIT_SUCCESS); + else + exit(IGT_EXIT_SKIP); +} + +/* fork support code */ +static int helper_process_count; +static pid_t helper_process_pids[] = +{ -1, -1, -1, -1}; + +static void reset_helper_process_list(void) +{ + for (int i = 0; i < ARRAY_SIZE(helper_process_pids); i++) + helper_process_pids[i] = -1; + helper_process_count = 0; +} + +static int __waitpid(pid_t pid) +{ + int status = -1; + while (waitpid(pid, &status, 0) == -1 && + errno == EINTR) + ; + + return status; +} + +static void fork_helper_exit_handler(int sig) +{ + /* Inside a signal handler, play safe */ + for (int i = 0; i < ARRAY_SIZE(helper_process_pids); i++) { + pid_t pid = helper_process_pids[i]; + if (pid != -1) { + kill(pid, SIGTERM); + __waitpid(pid); + helper_process_count--; + } + } + + assert(helper_process_count == 0); +} + +bool __igt_fork_helper(struct igt_helper_process *proc) +{ + pid_t pid; + int id; + int tmp_count; + + assert(!proc->running); + assert(helper_process_count < ARRAY_SIZE(helper_process_pids)); + + for (id = 0; helper_process_pids[id] != -1; id++) + ; + + igt_install_exit_handler(fork_helper_exit_handler); + + /* + * Avoid races when the parent stops the child before the setup code + * had a chance to run. This happens e.g. when skipping tests wrapped in + * the signal helper. + */ + tmp_count = exit_handler_count; + exit_handler_count = 0; + + /* ensure any buffers are flushed before fork */ + fflush(NULL); + + switch (pid = fork()) { + case -1: + exit_handler_count = tmp_count; + igt_assert(0); + case 0: + reset_helper_process_list(); + oom_adjust_for_doom(); + + return true; + default: + exit_handler_count = tmp_count; + proc->running = true; + proc->pid = pid; + proc->id = id; + helper_process_pids[id] = pid; + helper_process_count++; + + return false; + } + +} + +/** + * igt_wait_helper: + * @proc: #igt_helper_process structure + * + * Joins a helper process. It is an error to call this on a helper process which + * hasn't been spawned yet. + */ +int igt_wait_helper(struct igt_helper_process *proc) +{ + int status; + + assert(proc->running); + + status = __waitpid(proc->pid); + + proc->running = false; + + helper_process_pids[proc->id] = -1; + helper_process_count--; + + return status; +} + +/** + * igt_stop_helper: + * @proc: #igt_helper_process structure + * + * Terminates a helper process. It is an error to call this on a helper process + * which hasn't been spawned yet. + */ +void igt_stop_helper(struct igt_helper_process *proc) +{ + int status; + + /* failure here means the pid is already dead and so waiting is safe */ + kill(proc->pid, proc->use_SIGKILL ? SIGKILL : SIGTERM); + + status = igt_wait_helper(proc); + assert(WIFSIGNALED(status) && + WTERMSIG(status) == (proc->use_SIGKILL ? SIGKILL : SIGTERM)); +} + +static void children_exit_handler(int sig) +{ + int status; + + /* The exit handler can be called from a fatal signal, so play safe */ + while (num_test_children-- && wait(&status)) + ; +} + +bool __igt_fork(void) +{ + assert(!test_with_subtests || in_subtest); + assert(!test_child); + + igt_install_exit_handler(children_exit_handler); + + if (num_test_children >= test_children_sz) { + if (!test_children_sz) + test_children_sz = 4; + else + test_children_sz *= 2; + + test_children = realloc(test_children, + sizeof(pid_t)*test_children_sz); + igt_assert(test_children); + } + + /* ensure any buffers are flushed before fork */ + fflush(NULL); + + switch (test_children[num_test_children++] = fork()) { + case -1: + igt_assert(0); + case 0: + test_child = true; + exit_handler_count = 0; + reset_helper_process_list(); + oom_adjust_for_doom(); + + return true; + default: + return false; + } + +} + +/** + * igt_waitchildren: + * + * Wait for all children forked with igt_fork. + * + * The magic here is that exit codes from children will be correctly propagated + * to the main thread, including the relevant exit code if a child thread failed. + * Of course if multiple children failed with different exit codes the resulting + * exit code will be non-deterministic. + * + * Note that igt_skip() will not be forwarded, feature tests need to be done + * before spawning threads with igt_fork(). + */ +void igt_waitchildren(void) +{ + int err = 0; + int count; + + assert(!test_child); + + count = 0; + while (count < num_test_children) { + int status = -1; + pid_t pid; + int c; + + pid = wait(&status); + if (pid == -1) + continue; + + for (c = 0; c < num_test_children; c++) + if (pid == test_children[c]) + break; + if (c == num_test_children) + continue; + + if (err == 0 && status != 0) { + if (WIFEXITED(status)) { + printf("child %i failed with exit status %i\n", + c, WEXITSTATUS(status)); + err = WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + printf("child %i died with signal %i, %s\n", + c, WTERMSIG(status), + strsignal(WTERMSIG(status))); + err = 128 + WTERMSIG(status); + } else { + printf("Unhandled failure [%d] in child %i\n", status, c); + err = 256; + } + + for (c = 0; c < num_test_children; c++) + kill(test_children[c], SIGKILL); + } + + count++; + } + + num_test_children = 0; + if (err) + igt_fail(err); +} + +/* exit handler code */ +#define MAX_SIGNALS 32 +#define MAX_EXIT_HANDLERS 10 + +#ifndef HAVE_SIGHANDLER_T +typedef void (*sighandler_t)(int); +#endif + +static struct { + sighandler_t handler; + bool installed; +} orig_sig[MAX_SIGNALS]; + +static igt_exit_handler_t exit_handler_fn[MAX_EXIT_HANDLERS]; +static bool exit_handler_disabled; +static sigset_t saved_sig_mask; +#define SIGDEF(x) { x, #x, sizeof(#x) - 1 } +static const struct { int number; const char *name; size_t name_len; } handled_signals[] = + { SIGDEF(SIGINT), SIGDEF(SIGHUP), SIGDEF(SIGTERM), SIGDEF(SIGQUIT), + SIGDEF(SIGPIPE), SIGDEF(SIGABRT), SIGDEF(SIGSEGV), SIGDEF(SIGBUS) }; + +static int install_sig_handler(int sig_num, sighandler_t handler) +{ + orig_sig[sig_num].handler = signal(sig_num, handler); + + if (orig_sig[sig_num].handler == SIG_ERR) + return -1; + + orig_sig[sig_num].installed = true; + + return 0; +} + +static void restore_sig_handler(int sig_num) +{ + /* Just restore the default so that we properly fall over. */ + signal(sig_num, SIG_DFL); +} + +static void restore_all_sig_handler(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(orig_sig); i++) + restore_sig_handler(i); +} + +static void call_exit_handlers(int sig) +{ + int i; + + if (!exit_handler_count) { + return; + } + + for (i = exit_handler_count - 1; i >= 0; i--) + exit_handler_fn[i](sig); + + /* ensure we don't get called twice */ + exit_handler_count = 0; +} + +static void igt_atexit_handler(void) +{ + restore_all_sig_handler(); + + if (!exit_handler_disabled) + call_exit_handlers(0); +} + +static void fatal_sig_handler(int sig) +{ + int i; + + restore_all_sig_handler(); + + for (i = 0; i < ARRAY_SIZE(handled_signals); i++) { + if (handled_signals[i].number == sig) { + write(STDERR_FILENO, "Received signal ", 16); + write(STDERR_FILENO, handled_signals[i].name, + handled_signals[i].name_len); + write(STDERR_FILENO, ".\n", 2); + break; + } + } + + /* + * exit_handler_disabled is always false here, since when we set it + * we also block signals. + */ + call_exit_handlers(sig); + + { +#ifdef __linux__ + /* Workaround cached PID and TID races on glibc and Bionic libc. */ + pid_t pid = syscall(SYS_getpid); + pid_t tid = syscall(SYS_gettid); + + syscall(SYS_tgkill, pid, tid, sig); +#else + pthread_t tid = pthread_self(); + union sigval value = { .sival_ptr = NULL }; + + pthread_sigqueue(tid, sig, value); +#endif + } +} + +/** + * igt_install_exit_handler: + * @fn: exit handler function + * + * Set a handler that will be called either when the process calls exit() or + * returns from the main function, or one of the signals in + * 'handled_signals' is raised. MAX_EXIT_HANDLERS handlers can be installed, + * each of which will be called only once, even if a subsequent signal is + * raised. If the exit handlers are called due to a signal, the signal will be + * re-raised with the original signal disposition after all handlers returned. + * + * The handler will be passed the signal number if called due to a signal, or + * 0 otherwise. Exit handlers can also be used from test children spawned with + * igt_fork(), but not from within helper processes spawned with + * igt_fork_helper(). The list of exit handlers is reset when forking to + * avoid issues with children cleanup up the parent's state too early. + */ +void igt_install_exit_handler(igt_exit_handler_t fn) +{ + int i; + + for (i = 0; i < exit_handler_count; i++) + if (exit_handler_fn[i] == fn) + return; + + igt_assert(exit_handler_count < MAX_EXIT_HANDLERS); + + exit_handler_fn[exit_handler_count] = fn; + exit_handler_count++; + + if (exit_handler_count > 1) + return; + + for (i = 0; i < ARRAY_SIZE(handled_signals); i++) { + if (install_sig_handler(handled_signals[i].number, + fatal_sig_handler)) + goto err; + } + + if (atexit(igt_atexit_handler)) + goto err; + + return; +err: + restore_all_sig_handler(); + exit_handler_count--; + + igt_assert_f(0, "failed to install the signal handler\n"); +} + +/** + * igt_disable_exit_handler: + * + * Temporarily disable all exit handlers. Useful for library code doing tricky + * things. + */ +void igt_disable_exit_handler(void) +{ + sigset_t set; + int i; + + if (exit_handler_disabled) + return; + + sigemptyset(&set); + for (i = 0; i < ARRAY_SIZE(handled_signals); i++) + sigaddset(&set, handled_signals[i].number); + + if (sigprocmask(SIG_BLOCK, &set, &saved_sig_mask)) { + perror("sigprocmask"); + return; + } + + exit_handler_disabled = true; +} + +/** + * igt_enable_exit_handler: + * + * Re-enable all exit handlers temporarily disabled with + * igt_disable_exit_handler(). + */ +void igt_enable_exit_handler(void) +{ + if (!exit_handler_disabled) + return; + + if (sigprocmask(SIG_SETMASK, &saved_sig_mask, NULL)) { + perror("sigprocmask"); + return; + } + + exit_handler_disabled = false; +} + +/* simulation enviroment support */ + +/** + * igt_run_in_simulation: + * + * This function can be used to select a reduced test set when running in + * simulation environments. This i-g-t mode is selected by setting the + * INTEL_SIMULATION environment variable to 1. + * + * Returns: True when run in simulation mode, false otherwise. + */ +bool igt_run_in_simulation(void) +{ + static int simulation = -1; + + if (simulation == -1) + simulation = igt_check_boolean_env_var("INTEL_SIMULATION", false); + + return simulation; +} + +/** + * igt_skip_on_simulation: + * + * Skip tests when INTEL_SIMULATION environment variable is set. It uses + * igt_skip() internally and hence is fully subtest aware. + * + * Note that in contrast to all other functions which use igt_skip() internally + * it is allowed to use this outside of an #igt_fixture block in a test with + * subtests. This is because in contrast to most other test requirements, + * checking for simulation mode doesn't depend upon the present hardware and it + * so makes a lot of sense to have this check in the outermost #igt_main block. + */ +void igt_skip_on_simulation(void) +{ + if (igt_only_list_subtests()) + return; + + if (!in_fixture && !in_subtest) { + igt_fixture + igt_require(!igt_run_in_simulation()); + } else + igt_require(!igt_run_in_simulation()); +} + +/* structured logging */ + +/** + * igt_log: + * @domain: the log domain, or NULL for no domain + * @level: #igt_log_level + * @format: format string + * @...: optional arguments used in the format string + * + * This is the generic structured logging helper function. i-g-t testcase should + * output all normal message to stdout. Warning level message should be printed + * to stderr and the test runner should treat this as an intermediate result + * between SUCESS and FAILURE. + * + * The log level can be set through the IGT_LOG_LEVEL environment variable with + * values "debug", "info", "warn", "critical" and "none". By default verbose + * debug message are disabled. "none" completely disables all output and is not + * recommended since crucial issues only reported at the IGT_LOG_WARN level are + * ignored. + */ +void igt_log(const char *domain, enum igt_log_level level, const char *format, ...) +{ + va_list args; + + va_start(args, format); + igt_vlog(domain, level, format, args); + va_end(args); +} + +/** + * igt_vlog: + * @domain: the log domain, or NULL for no domain + * @level: #igt_log_level + * @format: format string + * @args: variable arguments lists + * + * This is the generic logging helper function using an explicit varargs + * structure and hence useful to implement domain-specific logging + * functions. + * + * If there is no need to wrap up a vararg list in the caller it is simpler to + * just use igt_log(). + */ +void igt_vlog(const char *domain, enum igt_log_level level, const char *format, va_list args) +{ + FILE *file; + char *line, *formatted_line; + const char *program_name; + const char *igt_log_level_str[] = { + "DEBUG", + "INFO", + "WARNING", + "CRITICAL", + "NONE" + }; + static bool line_continuation = false; + + assert(format); + +#ifdef __GLIBC__ + program_name = program_invocation_short_name; +#else + program_name = command_str; +#endif + + if (list_subtests && level <= IGT_LOG_WARN) + return; + + if (vasprintf(&line, format, args) == -1) + return; + + if (line_continuation) { + formatted_line = strdup(line); + if (!formatted_line) + goto out; + } else if (asprintf(&formatted_line, "(%s:%d) %s%s%s: %s", program_name, + getpid(), (domain) ? domain : "", (domain) ? "-" : "", + igt_log_level_str[level], line) == -1) { + goto out; + } + + line_continuation = line[strlen(line)] != '\n'; + + /* append log buffer */ + _igt_log_buffer_append(formatted_line); + + /* check print log level */ + if (igt_log_level > level) + goto out; + + /* check domain filter */ + if (igt_log_domain_filter) { + /* if null domain and filter is not "application", return */ + if (!domain && strcmp(igt_log_domain_filter, "application")) + goto out; + /* else if domain and filter do not match, return */ + else if (domain && strcmp(igt_log_domain_filter, domain)) + goto out; + } + + /* use stderr for warning messages and above */ + if (level >= IGT_LOG_WARN) { + file = stderr; + fflush(stdout); + } + else + file = stdout; + + /* prepend all except information messages with process, domain and log + * level information */ + if (level != IGT_LOG_INFO) + fwrite(formatted_line, sizeof(char), strlen(formatted_line), + file); + else + fwrite(line, sizeof(char), strlen(line), file); + +out: + free(line); +} + +static void igt_alarm_handler(int signal) +{ + /* exit with timeout status */ + igt_fail(IGT_EXIT_TIMEOUT); +} + +/** + * igt_set_timeout: + * @seconds: number of seconds before timeout + * + * Fail a test and exit with #IGT_EXIT_TIMEOUT status after the specified + * number of seconds have elapsed. If the current test has subtests and the + * timeout occurs outside a subtest, subsequent subtests will be skipped and + * marked as failed. + * + * Any previous timer is cancelled and no timeout is scheduled if @seconds is + * zero. + */ +void igt_set_timeout(unsigned int seconds) +{ + struct sigaction sa; + + sa.sa_handler = igt_alarm_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + if (seconds == 0) + sigaction(SIGALRM, NULL, NULL); + else + sigaction(SIGALRM, &sa, NULL); + + alarm(seconds); +} diff --git a/lib/igt_core.h b/lib/igt_core.h new file mode 100644 index 0000000..c2c820d --- /dev/null +++ b/lib/igt_core.h @@ -0,0 +1,652 @@ +/* + * Copyright © 2007,2014 Intel Corporation + * + * 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: + * Eric Anholt + * Daniel Vetter + * + */ + + +#ifndef IGT_CORE_H +#define IGT_CORE_H + +#include +#include +#include +#include +#include +#include +#include + +#ifndef IGT_LOG_DOMAIN +#define IGT_LOG_DOMAIN (NULL) +#endif + + +extern const char* __igt_test_description __attribute__((weak)); + +/** + * IGT_TEST_DESCRIPTION: + * @str: description string + * + * Defines a description for a test. This is used as the output for the + * "--help-description" option and is also included in the generated + * documentation. + */ +#define IGT_TEST_DESCRIPTION(str) const char* __igt_test_description = str + +/** + * IGT_EXIT_TIMEOUT: + * + * Exit status indicating a timeout occurred. + */ +#define IGT_EXIT_TIMEOUT 78 + +/** + * IGT_EXIT_SKIP: + * + * Exit status indicating the test was skipped. + */ +#define IGT_EXIT_SKIP 77 + +/** + * IGT_EXIT_SUCCESS + * + * Exit status indicating the test executed successfully. + */ +#define IGT_EXIT_SUCCESS 0 + +/** + * IGT_EXIT_INVALID + * + * Exit status indicating an invalid option or subtest was specified + */ +#define IGT_EXIT_INVALID 79 + + +bool __igt_fixture(void); +void __igt_fixture_complete(void); +void __igt_fixture_end(void) __attribute__((noreturn)); +/** + * igt_fixture: + * + * Annotate global test fixture code + * + * Testcase with subtests often need to set up a bunch of global state as the + * common test fixture. To avoid such code interferring with the subtest + * enumeration (e.g. when enumerating on systemes without an intel gpu) such + * blocks should be annotated with igt_fixture. + */ +#define igt_fixture for (int igt_tokencat(__tmpint,__LINE__) = 0; \ + igt_tokencat(__tmpint,__LINE__) < 1 && \ + __igt_fixture() && \ + (setjmp(igt_subtest_jmpbuf) == 0); \ + igt_tokencat(__tmpint,__LINE__) ++, \ + __igt_fixture_complete()) + +/* subtest infrastructure */ +jmp_buf igt_subtest_jmpbuf; +typedef int (*igt_opt_handler_t)(int opt, int opt_index); +#ifndef __GTK_DOC_IGNORE__ /* gtkdoc wants to document this forward decl */ +struct option; +#endif +int igt_subtest_init_parse_opts(int *argc, char **argv, + const char *extra_short_opts, + struct option *extra_long_opts, + const char *help_str, + igt_opt_handler_t extra_opt_handler); + + +/** + * igt_subtest_init: + * @argc: argc from the test's main() + * @argv: argv from the test's main() + * + * This initializes the for tests with subtests without the need for additional + * cmdline options. It is just a simplified version of + * igt_subtest_init_parse_opts(). + * + * If there's not a reason to the contrary it's less error prone to just use an + * #igt_main block instead of stitching the test's main() function together + * manually. + */ +#define igt_subtest_init(argc, argv) igt_subtest_init_parse_opts(&argc, argv, NULL, NULL, NULL, NULL); + +bool __igt_run_subtest(const char *subtest_name); +#define __igt_tokencat2(x, y) x ## y + +/** + * igt_tokencat: + * @x: first variable + * @y: second variable + * + * C preprocessor helper to concatenate two variables while properly expanding + * them. + */ +#define igt_tokencat(x, y) __igt_tokencat2(x, y) + +/** + * igt_subtest: + * @name: name of the subtest + * + * This is a magic control flow block which denotes a subtest code block. Within + * that code block igt_skip|success will only bail out of the subtest. The _f + * variant accepts a printf format string, which is useful for constructing + * combinatorial tests. + * + * This is a simpler version of igt_subtest_f() + */ +#define igt_subtest(name) for (; __igt_run_subtest((name)) && \ + (setjmp(igt_subtest_jmpbuf) == 0); \ + igt_success()) +#define __igt_subtest_f(tmp, format...) \ + for (char tmp [256]; \ + snprintf( tmp , sizeof( tmp ), \ + format), \ + __igt_run_subtest( tmp ) && \ + (setjmp(igt_subtest_jmpbuf) == 0); \ + igt_success()) + +/** + * igt_subtest_f: + * @...: format string and optional arguments + * + * This is a magic control flow block which denotes a subtest code block. Within + * that code block igt_skip|success will only bail out of the subtest. The _f + * variant accepts a printf format string, which is useful for constructing + * combinatorial tests. + * + * Like igt_subtest(), but also accepts a printf format string instead of a + * static string. + */ +#define igt_subtest_f(f...) \ + __igt_subtest_f(igt_tokencat(__tmpchar, __LINE__), f) + +const char *igt_subtest_name(void); +bool igt_only_list_subtests(void); + +/** + * igt_main: + * + * This is a magic control flow block used instead of a main() function for + * tests with subtests. Open-coding the main() function is only recommended if + * the test needs to parse additional cmdline arguments of its own. + */ +#define igt_main \ + static void igt_tokencat(__real_main, __LINE__)(void); \ + int main(int argc, char **argv) { \ + igt_subtest_init_parse_opts(&argc, argv, NULL, NULL, NULL, NULL); \ + igt_tokencat(__real_main, __LINE__)(); \ + igt_exit(); \ + } \ + static void igt_tokencat(__real_main, __LINE__)(void) \ + + +void igt_simple_init_parse_opts(int *argc, char **argv, + const char *extra_short_opts, + struct option *extra_long_opts, + const char *help_str, + igt_opt_handler_t extra_opt_handler); + +/** + * igt_simple_init: + * @argc: argc from the test's main() + * @argv: argv from the test's main() + * + * This initializes a simple test without any support for subtests. + * + * If there's not a reason to the contrary it's less error prone to just use an + * #igt_simple_main block instead of stitching the test's main() function together + * manually. + */ +#define igt_simple_init(argc, argv) igt_simple_init_parse_opts(&argc, argv, NULL, NULL, NULL, NULL); + +/** + * igt_simple_main: + * + * This is a magic control flow block used instead of a main() function for + * simple tests. Open-coding the main() function is only recommended if + * the test needs to parse additional cmdline arguments of its own. + */ +#define igt_simple_main \ + static void igt_tokencat(__real_main, __LINE__)(void); \ + int main(int argc, char **argv) { \ + igt_simple_init_parse_opts(&argc, argv, NULL, NULL, NULL, NULL); \ + igt_tokencat(__real_main, __LINE__)(); \ + igt_exit(); \ + } \ + static void igt_tokencat(__real_main, __LINE__)(void) \ + +__attribute__((format(printf, 1, 2))) +void igt_skip(const char *f, ...) __attribute__((noreturn)); +__attribute__((format(printf, 5, 6))) +void __igt_skip_check(const char *file, const int line, + const char *func, const char *check, + const char *format, ...) __attribute__((noreturn)); +#define igt_skip_check(E, F...) \ + __igt_skip_check(__FILE__, __LINE__, __func__, E, F) +void igt_success(void); + +void igt_fail(int exitcode) __attribute__((noreturn)); +__attribute__((format(printf, 7, 8))) +void __igt_fail_assert(int exitcode, const char *domain, const char *file, + const int line, const char *func, const char *assertion, + const char *format, ...) + __attribute__((noreturn)); +void igt_exit(void) __attribute__((noreturn)); + +/** + * igt_assert: + * @expr: condition to test + * + * Fails (sub-)test if the condition is not met. + * + * Should be used everywhere where a test checks results. + */ +#define igt_assert(expr) \ + do { if (!(expr)) \ + __igt_fail_assert(99, IGT_LOG_DOMAIN, __FILE__, __LINE__, __func__, #expr , NULL); \ + } while (0) + +/** + * igt_assert_f: + * @expr: condition to test + * @...: format string and optional arguments + * + * Fails (sub-)test if the condition is not met. + * + * Should be used everywhere where a test checks results. + * + * In addition to the plain igt_assert() helper this allows to print additional + * information to help debugging test failures. + */ +#define igt_assert_f(expr, f...) \ + do { if (!(expr)) \ + __igt_fail_assert(99, IGT_LOG_DOMAIN, __FILE__, __LINE__, __func__, #expr , f); \ + } while (0) + +/** + * igt_fail_on: + * @expr: condition to test + * + * Fails (sub-)test if the condition is met. + * + * Should be used everywhere where a test checks results. + */ +#define igt_fail_on(expr) igt_assert(!(expr)) + +/** + * igt_fail_on_f: + * @expr: condition to test + * @...: format string and optional arguments + * + * Fails (sub-)test if the condition is met. + * + * Should be used everywhere where a test checks results. + * + * In addition to the plain igt_assert() helper this allows to print additional + * information to help debugging test failures. + */ +#define igt_fail_on_f(expr, f...) igt_assert_f(!(expr), f) + +/** + * igt_assert_cmpint: + * @n1: first value + * @cmp: compare operator + * @ncmp: negated version of @cmp + * @n2: second value + * + * Fails (sub-)test if the condition is not met + * + * Should be used everywhere where a test compares two integer values. + * + * Like igt_assert(), but displays the values being compared on failure instead + * of simply printing the stringified expression. + */ +#define igt_assert_cmpint(n1, cmp, ncmp, n2) \ + do { \ + int __n1 = (n1), __n2 = (n2); \ + if (__n1 cmp __n2) ; else \ + __igt_fail_assert(99, IGT_LOG_DOMAIN, __FILE__, __LINE__, __func__, \ + #n1 " " #cmp " " #n2, \ + "error: %d " #ncmp " %d\n", __n1, __n2); \ + } while (0) + +#define igt_assert_cmpuint(n1, cmp, ncmp, n2) \ + do { \ + uint32_t __n1 = (n1), __n2 = (n2); \ + if (__n1 cmp __n2) ; else \ + __igt_fail_assert(99, IGT_LOG_DOMAIN, __FILE__, __LINE__, __func__, \ + #n1 " " #cmp " " #n2, \ + "error: %#x " #ncmp " %#x\n", __n1, __n2); \ + } while (0) + +/** + * igt_assert_eq: + * @n1: first integer + * @n2: second integer + * + * Fails (sub-)test if the two integers are not equal. Beware that for now this + * only works on integers. + * + * Like igt_assert(), but displays the values being compared on failure instead + * of simply printing the stringified expression. + */ +#define igt_assert_eq(n1, n2) igt_assert_cmpint(n1, ==, !=, n2) +#define igt_assert_eq_u32(n1, n2) igt_assert_cmpuint(n1, ==, !=, n2) + +/** + * igt_assert_neq: + * @n1: first integer + * @n2: second integer + * + * Fails (sub-)test if the two integers are equal. Beware that for now this + * only works on integers. + * + * Like igt_assert(), but displays the values being compared on failure instead + * of simply printing the stringified expression. + */ +#define igt_assert_neq(n1, n2) igt_assert_cmpint(n1, !=, ==, n2) + +/** + * igt_assert_lte: + * @n1: first integer + * @n2: second integer + * + * Fails (sub-)test if the second integers is greater than the first. + * Beware that for now this only works on integers. + * + * Like igt_assert(), but displays the values being compared on failure instead + * of simply printing the stringified expression. + */ +#define igt_assert_lte(n1, n2) igt_assert_cmpint(n1, <=, >, n2) + +/** + * igt_assert_lt: + * @n1: first integer + * @n2: second integer + * + * Fails (sub-)test if the second integers is strictly smaller than the first. + * Beware that for now this only works on integers. + * + * Like igt_assert(), but displays the values being compared on failure instead + * of simply printing the stringified expression. + */ +#define igt_assert_lt(n1, n2) igt_assert_cmpint(n1, <, >=, n2) + +/** + * igt_require: + * @expr: condition to test + * + * Skip a (sub-)test if a condition is not met. + * + * Should be used everywhere where a test checks results to decide about + * skipping. This is useful to streamline the skip logic since it allows for a more flat + * code control flow, similar to igt_assert() + */ +#define igt_require(expr) do { \ + if (!(expr)) igt_skip_check(#expr , NULL); \ + else igt_debug("Test requirement passed: "#expr"\n"); \ +} while (0) + +/** + * igt_skip_on: + * @expr: condition to test + * + * Skip a (sub-)test if a condition is met. + * + * Should be used everywhere where a test checks results to decide about + * skipping. This is useful to streamline the skip logic since it allows for a more flat + * code control flow, similar to igt_assert() + */ +#define igt_skip_on(expr) do { \ + if ((expr)) igt_skip_check("!(" #expr ")" , NULL); \ + else igt_debug("Test requirement passed: !("#expr")\n"); \ +} while (0) + +/** + * igt_require_f: + * @expr: condition to test + * @...: format string and optional arguments + * + * Skip a (sub-)test if a condition is not met. + * + * Should be used everywhere where a test checks results to decide about + * skipping. This is useful to streamline the skip logic since it allows for a more flat + * code control flow, similar to igt_assert() + * + * In addition to the plain igt_require() helper this allows to print additional + * information to help debugging test failures. + */ +#define igt_require_f(expr, f...) do { \ + if (!(expr)) igt_skip_check(#expr , f); \ + else igt_debug("Test requirement passed: "#expr"\n"); \ +} while (0) + +/** + * igt_skip_on_f: + * @expr: condition to test + * @...: format string and optional arguments + * + * Skip a (sub-)test if a condition is met. + * + * Should be used everywhere where a test checks results to decide about + * skipping. This is useful to streamline the skip logic since it allows for a more flat + * code control flow, similar to igt_assert() + * + * In addition to the plain igt_skip_on() helper this allows to print additional + * information to help debugging test failures. + */ +#define igt_skip_on_f(expr, f...) do { \ + if ((expr)) igt_skip_check("!("#expr")", f); \ + else igt_debug("Test requirement passed: !("#expr")\n"); \ +} while (0) + +/* fork support code */ +bool __igt_fork(void); + +/** + * igt_fork: + * @child: name of the int variable with the child number + * @num_children: number of children to fork + * + * This is a magic control flow block which spawns parallel test threads with + * fork(). + * + * The test children execute in parallel to the main test thread. Joining all + * test threads should be done with igt_waitchildren to ensure that the exit + * codes of all children are properly reflected in the test status. + * + * Note that igt_skip() will not be forwarded, feature tests need to be done + * before spawning threads with igt_fork(). + */ +#define igt_fork(child, num_children) \ + for (int child = 0; child < (num_children); child++) \ + for (; __igt_fork(); exit(0)) +void igt_waitchildren(void); + +/** + * igt_helper_process: + * @running: indicates whether the process is currently running + * @use_SIGKILL: whether the helper should be terminated with SIGKILL or SIGTERM + * @pid: pid of the helper if @running is true + * @id: internal id + * + * Tracking structure for helper processes. Users of the i-g-t library should + * only set @use_SIGKILL directly. + */ +struct igt_helper_process { + bool running; + bool use_SIGKILL; + pid_t pid; + int id; +}; +bool __igt_fork_helper(struct igt_helper_process *proc); + +/** + * igt_fork_helper: + * @proc: #igt_helper_process structure + * + * This is a magic control flow block which denotes an asynchronous helper + * process block. The difference compared to igt_fork() is that failures from + * the child process will not be forwarded, making this construct more suitable + * for background processes. Common use cases are regular interference of the + * main test thread through e.g. sending signals or evicting objects through + * debugfs. Through the explicit #igt_helper_process they can also be controlled + * in a more fine-grained way than test children spawned through igt_fork(). + * + * For tests with subtest helper process can be started outside of a + * #igt_subtest block. + * + * Calling igt_wait_helper() joins a helper process and igt_stop_helper() + * forcefully terminates it. + */ +#define igt_fork_helper(proc) \ + for (; __igt_fork_helper(proc); exit(0)) +int igt_wait_helper(struct igt_helper_process *proc); +void igt_stop_helper(struct igt_helper_process *proc); + +/* exit handler code */ + +/** + * igt_exit_handler_t: + * @sig: Signal number which caused the exit or 0. + * + * Exit handler type used by igt_install_exit_handler(). Note that exit handlers + * can potentially be run from signal handling contexts, the @sig parameter can + * be used to figure this out and act accordingly. + */ +typedef void (*igt_exit_handler_t)(int sig); + +/* reliable atexit helpers, also work when killed by a signal (if possible) */ +void igt_install_exit_handler(igt_exit_handler_t fn); +void igt_enable_exit_handler(void); +void igt_disable_exit_handler(void); + +/* helpers to automatically reduce test runtime in simulation */ +bool igt_run_in_simulation(void); +/** + * SLOW_QUICK: + * @slow: value in simulation mode + * @quick: value in normal mode + * + * Simple macro to select between two values (e.g. number of test rounds or test + * buffer size) depending upon whether i-g-t is run in simulation mode or not. + */ +#define SLOW_QUICK(slow,quick) (igt_run_in_simulation() ? (quick) : (slow)) + +void igt_skip_on_simulation(void); + +const char *igt_interactive_debug; + +/* structured logging */ +enum igt_log_level { + IGT_LOG_DEBUG, + IGT_LOG_INFO, + IGT_LOG_WARN, + IGT_LOG_CRITICAL, + IGT_LOG_NONE, +}; +__attribute__((format(printf, 3, 4))) +void igt_log(const char *domain, enum igt_log_level level, const char *format, ...); +__attribute__((format(printf, 3, 0))) +void igt_vlog(const char *domain, enum igt_log_level level, const char *format, va_list args); + +/** + * igt_debug: + * @...: format string and optional arguments + * + * Wrapper for igt_log() for message at the IGT_LOG_DEBUG level. + */ +#define igt_debug(f...) igt_log(IGT_LOG_DOMAIN, IGT_LOG_DEBUG, f) + +/** + * igt_info: + * @...: format string and optional arguments + * + * Wrapper for igt_log() for message at the IGT_LOG_INFO level. + */ +#define igt_info(f...) igt_log(IGT_LOG_DOMAIN, IGT_LOG_INFO, f) + +/** + * igt_warn: + * @...: format string and optional arguments + * + * Wrapper for igt_log() for message at the IGT_LOG_WARN level. + */ +#define igt_warn(f...) igt_log(IGT_LOG_DOMAIN, IGT_LOG_WARN, f) + +/** + * igt_critical: + * @...: format string and optional arguments + * + * Wrapper for igt_log() for message at the IGT_LOG_CRITICAL level. + */ +#define igt_critical(f...) igt_log(IGT_LOG_DOMAIN, IGT_LOG_CRITICAL, f) + +extern enum igt_log_level igt_log_level; + +/** + * igt_warn_on: + * @condition: condition to test + * + * Print a IGT_LOG_WARN level message if a condition is not met. + * + * Should be used everywhere where a test checks results to decide about + * printing warnings. This is useful to streamline the test logic since it + * allows for a more flat code control flow, similar to igt_assert() + */ +#define igt_warn_on(condition) do {\ + if (condition) \ + igt_warn("Warning on condition %s in fucntion %s, file %s:%i\n", \ + #condition, __func__, __FILE__, __LINE__); \ + } while (0) + +/** + * igt_warn_on_f: + * @condition: condition to test + * @...: format string and optional arguments + * + * Skip a (sub-)test if a condition is not met. + * + * Print a IGT_LOG_WARN level message if a condition is not met. + * + * Should be used everywhere where a test checks results to decide about + * printing warnings. This is useful to streamline the test logic since it + * allows for a more flat code control flow, similar to igt_assert() + * + * In addition to the plain igt_warn_on_f() helper this allows to print + * additional information (again as warnings) to help debugging test failures. + */ +#define igt_warn_on_f(condition, f...) do {\ + if (condition) {\ + igt_warn("Warning on condition %s in fucntion %s, file %s:%i\n", \ + #condition, __func__, __FILE__, __LINE__); \ + igt_warn(f); \ + } \ + } while (0) + + +void igt_set_timeout(unsigned int seconds); + +#endif /* IGT_CORE_H */ diff --git a/lib/igt_debugfs.c b/lib/igt_debugfs.c new file mode 100644 index 0000000..a2cec45 --- /dev/null +++ b/lib/igt_debugfs.c @@ -0,0 +1,615 @@ +/* + * Copyright © 2013 Intel Corporation + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drmtest.h" +#include "igt_kms.h" +#include "igt_debugfs.h" + +/** + * SECTION:igt_debugfs + * @short_description: Support code for debugfs features + * @title: i-g-t debugfs + * @include: igt_debugfs.h + * + * This library provides helpers to access debugfs features. On top of some + * basic functions to access debugfs files with e.g. igt_debugfs_open() it also + * provides higher-level wrappers for some debugfs features + * + * # Pipe CRC Support + * + * This library wraps up the kernel's support for capturing pipe CRCs into a + * neat and tidy package. For the detailed usage see all the functions which + * work on #igt_pipe_crc_t. This is supported on all platforms and outputs. + * + * Actually using pipe CRCs to write modeset tests is a bit tricky though, so + * there is no way to directly check a CRC: Both the details of the plane + * blending, color correction and other hardware and how exactly the CRC is + * computed at each tap point vary by hardware generation and are not disclosed. + * + * The only way to use #igt_crc_t CRCs therefore is to compare CRCs among each + * another either for equality or difference. Otherwise CRCs must be treated as + * completely opaque values. Note that not even CRCs from different pipes or tap + * points on the same platform can be compared. Hence only use igt_crc_is_null() + * and igt_crc_equal() to inspect CRC values captured by the same + * #igt_pipe_crc_t object. + * + * # Other debugfs interface wrappers + * + * This covers the miscellaneous debugfs interface wrappers: + * + * - drm/i915 supports interfaces to evict certain clases of gem buffer objects, + * see igt_drop_caches_set(). + * + * - drm/i915 supports an interface to disable prefaulting, useful to test + * slowpaths in ioctls. See igt_disable_prefault(). + */ + +/* + * General debugfs helpers + */ + +typedef struct { + char root[128]; + char dri_path[128]; +} igt_debugfs_t; + +static bool __igt_debugfs_init(igt_debugfs_t *debugfs) +{ + const char *path = "/sys/kernel/debug"; + struct stat st; + int n; + + if (stat("/debug/dri", &st) == 0) { + path = "/debug/dri"; + goto find_minor; + } + + if (stat("/sys/kernel/debug/dri", &st) == 0) + goto find_minor; + + igt_assert(stat("/sys/kernel/debug", &st) == 0); + + mount("debug", "/sys/kernel/debug", "debugfs", 0, 0); + +find_minor: + strcpy(debugfs->root, path); + for (n = 0; n < 16; n++) { + int len = sprintf(debugfs->dri_path, "%s/dri/%d", path, n); + sprintf(debugfs->dri_path + len, "/i915_error_state"); + if (stat(debugfs->dri_path, &st) == 0) { + debugfs->dri_path[len] = '\0'; + return true; + } + } + + debugfs->dri_path[0] = '\0'; + + return false; +} + +static igt_debugfs_t *__igt_debugfs_singleton(void) +{ + static igt_debugfs_t singleton; + static bool init_done = false; + + if (init_done) + return &singleton; + + if (__igt_debugfs_init(&singleton)) { + init_done = true; + return &singleton; + } else { + return NULL; + } +} + +/** + * igt_debugfs_open: + * @filename: name of the debugfs node to open + * @mode: mode bits as used by open() + * + * This opens a debugfs file as a Unix file descriptor. The filename should be + * relative to the drm device's root, i.e without "drm/<minor>". + * + * Returns: + * The Unix file descriptor for the debugfs file or -1 if that didn't work out. + */ +int igt_debugfs_open(const char *filename, int mode) +{ + char buf[1024]; + igt_debugfs_t *debugfs = __igt_debugfs_singleton(); + + if (!debugfs) + return -1; + + sprintf(buf, "%s/%s", debugfs->dri_path, filename); + return open(buf, mode); +} + +/** + * igt_debugfs_fopen: + * @filename: name of the debugfs node to open + * @mode: mode string as used by fopen() + * + * This opens a debugfs file as a libc FILE. The filename should be + * relative to the drm device's root, i.e without "drm/<minor>". + * + * Returns: + * The libc FILE pointer for the debugfs file or NULL if that didn't work out. + */ +FILE *igt_debugfs_fopen(const char *filename, + const char *mode) +{ + char buf[1024]; + + igt_debugfs_t *debugfs = __igt_debugfs_singleton(); + + if (!debugfs) + return NULL; + + sprintf(buf, "%s/%s", debugfs->dri_path, filename); + return fopen(buf, mode); +} + +/* + * Pipe CRC + */ + +/** + * igt_crc_is_null: + * @crc: pipe CRC value to check + * + * Returns: True if the CRC is null/invalid, false if it represents a captured + * valid CRC. + */ +bool igt_crc_is_null(igt_crc_t *crc) +{ + int i; + + for (i = 0; i < crc->n_words; i++) { + igt_warn_on_f(crc->crc[i] == 0xffffffff, + "Suspicious CRC: it looks like the CRC " + "read back was from a register in a powered " + "down well\n"); + if (crc->crc[i]) + return false; + } + + return true; +} + +/** + * igt_crc_equal: + * @a: first pipe CRC value + * @b: second pipe CRC value + * + * Compares two CRC values. + * + * Returns: true if the two CRCs match, false otherwise. + */ +bool igt_crc_equal(igt_crc_t *a, igt_crc_t *b) +{ + int i; + + if (a->n_words != b->n_words) + return false; + + for (i = 0; i < a->n_words; i++) + if (a->crc[i] != b->crc[i]) + return false; + + return true; +} + +/** + * igt_crc_to_string: + * @crc: pipe CRC value to print + * + * This formats @crc into a string buffer which is owned by igt_crc_to_string(). + * The next call will override the buffer again, which makes this multithreading + * unsafe. + * + * This should only ever be used for diagnostic debug output. + */ +char *igt_crc_to_string(igt_crc_t *crc) +{ + char buf[128]; + + if (crc->n_words == 5) + sprintf(buf, "%08x %08x %08x %08x %08x", crc->crc[0], + crc->crc[1], crc->crc[2], crc->crc[3], crc->crc[4]); + else + igt_assert(0); + + return strdup(buf); +} + +/* (6 fields, 8 chars each, space separated (5) + '\n') */ +#define PIPE_CRC_LINE_LEN (6 * 8 + 5 + 1) +/* account for \'0' */ +#define PIPE_CRC_BUFFER_LEN (PIPE_CRC_LINE_LEN + 1) + +struct _igt_pipe_crc { + int ctl_fd; + int crc_fd; + int line_len; + int buffer_len; + + enum pipe pipe; + enum intel_pipe_crc_source source; +}; + +static const char *pipe_crc_sources[] = { + "none", + "plane1", + "plane2", + "pf", + "pipe", + "TV", + "DP-B", + "DP-C", + "DP-D", + "auto" +}; + +static const char *pipe_crc_source_name(enum intel_pipe_crc_source source) +{ + return pipe_crc_sources[source]; +} + +static bool igt_pipe_crc_do_start(igt_pipe_crc_t *pipe_crc) +{ + char buf[64]; + + /* Stop first just to make sure we don't have lingering state left. */ + igt_pipe_crc_stop(pipe_crc); + + sprintf(buf, "pipe %s %s", kmstest_pipe_name(pipe_crc->pipe), + pipe_crc_source_name(pipe_crc->source)); + errno = 0; + write(pipe_crc->ctl_fd, buf, strlen(buf)); + if (errno != 0) + return false; + + return true; +} + +static void igt_pipe_crc_pipe_off(int fd, enum pipe pipe) +{ + char buf[32]; + + sprintf(buf, "pipe %s none", kmstest_pipe_name(pipe)); + write(fd, buf, strlen(buf)); +} + +static void igt_pipe_crc_reset(void) +{ + int fd; + + fd = igt_debugfs_open("i915_display_crc_ctl", O_WRONLY); + + igt_pipe_crc_pipe_off(fd, PIPE_A); + igt_pipe_crc_pipe_off(fd, PIPE_B); + igt_pipe_crc_pipe_off(fd, PIPE_C); + + close(fd); +} + +static void pipe_crc_exit_handler(int sig) +{ + igt_pipe_crc_reset(); +} + +/** + * igt_require_pipe_crc: + * + * Convenience helper to check whether pipe CRC capturing is supported by the + * kernel. Uses igt_skip to automatically skip the test/subtest if this isn't + * the case. + */ +void igt_require_pipe_crc(void) +{ + const char *cmd = "pipe A none"; + FILE *ctl; + size_t written; + int ret; + + ctl = igt_debugfs_fopen("i915_display_crc_ctl", "r+"); + igt_require_f(ctl, + "No display_crc_ctl found, kernel too old\n"); + written = fwrite(cmd, 1, strlen(cmd), ctl); + ret = fflush(ctl); + igt_require_f((written == strlen(cmd) && ret == 0) || errno != ENODEV, + "CRCs not supported on this platform\n"); + + fclose(ctl); +} + +/** + * igt_pipe_crc_new: + * @pipe: display pipe to use as source + * @source: CRC tap point to use as source + * + * This sets up a new pipe CRC capture object for the given @pipe and @source. + * + * Returns: A pipe CRC object if the given @pipe and @source. The library + * assumes that the source is always available since recent kernels support at + * least INTEL_PIPE_CRC_SOURCE_AUTO everywhere. + */ +igt_pipe_crc_t * +igt_pipe_crc_new(enum pipe pipe, enum intel_pipe_crc_source source) +{ + igt_pipe_crc_t *pipe_crc; + char buf[128]; + + igt_install_exit_handler(pipe_crc_exit_handler); + + pipe_crc = calloc(1, sizeof(struct _igt_pipe_crc)); + + pipe_crc->ctl_fd = igt_debugfs_open("i915_display_crc_ctl", O_WRONLY); + igt_assert(pipe_crc->ctl_fd != -1); + + sprintf(buf, "i915_pipe_%s_crc", kmstest_pipe_name(pipe)); + pipe_crc->crc_fd = igt_debugfs_open(buf, O_RDONLY); + igt_assert(pipe_crc->crc_fd != -1); + + pipe_crc->line_len = PIPE_CRC_LINE_LEN; + pipe_crc->buffer_len = PIPE_CRC_BUFFER_LEN; + pipe_crc->pipe = pipe; + pipe_crc->source = source; + + return pipe_crc; +} + +/** + * igt_pipe_crc_free: + * @pipe_crc: pipe CRC object + * + * Frees all resources associated with @pipe_crc. + */ +void igt_pipe_crc_free(igt_pipe_crc_t *pipe_crc) +{ + if (!pipe_crc) + return; + + close(pipe_crc->ctl_fd); + close(pipe_crc->crc_fd); + free(pipe_crc); +} + +/** + * igt_pipe_crc_start: + * @pipe_crc: pipe CRC object + * + * Starts the CRC capture process on @pipe_crc. + */ +void igt_pipe_crc_start(igt_pipe_crc_t *pipe_crc) +{ + igt_crc_t *crcs = NULL; + + igt_assert(igt_pipe_crc_do_start(pipe_crc)); + + /* + * For some no yet identified reason, the first CRC is bonkers. So + * let's just wait for the next vblank and read out the buggy result. + * + * On CHV sometimes the second CRC is bonkers as well, so don't trust + * that one either. + */ + igt_pipe_crc_get_crcs(pipe_crc, 2, &crcs); + free(crcs); +} + +/** + * igt_pipe_crc_stop: + * @pipe_crc: pipe CRC object + * + * Stops the CRC capture process on @pipe_crc. + */ +void igt_pipe_crc_stop(igt_pipe_crc_t *pipe_crc) +{ + char buf[32]; + + sprintf(buf, "pipe %s none", kmstest_pipe_name(pipe_crc->pipe)); + write(pipe_crc->ctl_fd, buf, strlen(buf)); +} + +static bool pipe_crc_init_from_string(igt_crc_t *crc, const char *line) +{ + int n; + + crc->n_words = 5; + n = sscanf(line, "%8u %8x %8x %8x %8x %8x", &crc->frame, &crc->crc[0], + &crc->crc[1], &crc->crc[2], &crc->crc[3], &crc->crc[4]); + return n == 6; +} + +static bool read_one_crc(igt_pipe_crc_t *pipe_crc, igt_crc_t *out) +{ + ssize_t bytes_read; + char buf[pipe_crc->buffer_len]; + + igt_set_timeout(5); + bytes_read = read(pipe_crc->crc_fd, &buf, pipe_crc->line_len); + igt_set_timeout(0); + + igt_assert_eq(bytes_read, pipe_crc->line_len); + buf[bytes_read] = '\0'; + + if (!pipe_crc_init_from_string(out, buf)) + return false; + + return true; +} + +/** + * igt_pipe_crc_get_crcs: + * @pipe_crc: pipe CRC object + * @n_crcs: number of CRCs to capture + * @out_crcs: buffer pointer for the captured CRC values + * + * Read @n_crcs from @pipe_crc. This function blocks until @n_crcs are + * retrieved. @out_crcs is alloced by this function and must be released with + * free() by the caller. + * + * Callers must start and stop the capturing themselves by calling + * igt_pipe_crc_start() and igt_pipe_crc_stop(). + */ +void +igt_pipe_crc_get_crcs(igt_pipe_crc_t *pipe_crc, int n_crcs, + igt_crc_t **out_crcs) +{ + igt_crc_t *crcs; + int n = 0; + + crcs = calloc(n_crcs, sizeof(igt_crc_t)); + + do { + igt_crc_t *crc = &crcs[n]; + + if (!read_one_crc(pipe_crc, crc)) + continue; + + n++; + } while (n < n_crcs); + + *out_crcs = crcs; +} + +/** + * igt_pipe_crc_collect_crc: + * @pipe_crc: pipe CRC object + * @out_crc: buffer for the captured CRC values + * + * Read a single CRC from @pipe_crc. This function blocks until the CRC is + * retrieved. @out_crc must be allocated by the caller. + * + * This function takes care of the pipe_crc book-keeping, it will start/stop + * the collection of the CRC. + */ +void igt_pipe_crc_collect_crc(igt_pipe_crc_t *pipe_crc, igt_crc_t *out_crc) +{ + igt_pipe_crc_start(pipe_crc); + read_one_crc(pipe_crc, out_crc); + igt_pipe_crc_stop(pipe_crc); + + igt_assert(!igt_crc_is_null(out_crc)); +} + +/* + * Drop caches + */ + +/** + * igt_drop_caches_set: + * @val: bitmask for DROP_* values + * + * This calls the debugfs interface the drm/i915 GEM driver exposes to drop or + * evict certain classes of gem buffer objects. + */ +void igt_drop_caches_set(uint64_t val) +{ + int fd; + char data[19]; + size_t nbytes; + + sprintf(data, "0x%" PRIx64, val); + + fd = igt_debugfs_open("i915_gem_drop_caches", O_WRONLY); + + igt_assert(fd >= 0); + do { + nbytes = write(fd, data, strlen(data) + 1); + } while (nbytes == -1 && (errno == EINTR || errno == EAGAIN)); + igt_assert(nbytes == strlen(data) + 1); + close(fd); +} + +/* + * Prefault control + */ + +#define PREFAULT_DEBUGFS "/sys/module/i915/parameters/prefault_disable" +static void igt_prefault_control(bool enable) +{ + const char *name = PREFAULT_DEBUGFS; + int fd; + char buf[2] = {'Y', 'N'}; + int index; + + fd = open(name, O_RDWR); + igt_require(fd >= 0); + + if (enable) + index = 1; + else + index = 0; + + igt_require(write(fd, &buf[index], 1) == 1); + + close(fd); +} + +static void enable_prefault_at_exit(int sig) +{ + igt_enable_prefault(); +} + +/** + * igt_disable_prefault: + * + * Disable prefaulting in certain gem ioctls through the debugfs interface. As + * usual this installs an exit handler to clean up and re-enable prefaulting + * even when the test exited abnormally. + * + * igt_enable_prefault() will enable normale operation again. + */ +void igt_disable_prefault(void) +{ + igt_prefault_control(false); + + igt_install_exit_handler(enable_prefault_at_exit); +} + +/** + * igt_enable_prefault: + * + * Enable prefault (again) through the debugfs interface. + */ +void igt_enable_prefault(void) +{ + igt_prefault_control(true); +} diff --git a/lib/igt_debugfs.h b/lib/igt_debugfs.h new file mode 100644 index 0000000..8285029 --- /dev/null +++ b/lib/igt_debugfs.h @@ -0,0 +1,145 @@ +/* + * Copyright © 2013 Intel Corporation + * + * 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 __IGT_DEBUGFS_H__ +#define __IGT_DEBUGFS_H__ + +#include +#include +#include + +enum pipe; + +int igt_debugfs_open(const char *filename, int mode); +FILE *igt_debugfs_fopen(const char *filename, + const char *mode); + +/* + * Pipe CRC + */ + +/** + * igt_pipe_crc_t: + * + * Pipe CRC support structure. Needs to be allocated and set up with + * igt_pipe_crc_new() for a specific pipe and pipe CRC source value. + */ +typedef struct _igt_pipe_crc igt_pipe_crc_t; + +/** + * igt_crc_t: + * @frame: frame number of the capture CRC + * @n_words: internal field, don't access + * @crc: internal field, don't access + * + * Pipe CRC value. All other members than @frame are private and should not be + * inspected by testcases. + */ +typedef struct { + uint32_t frame; + int n_words; + uint32_t crc[5]; +} igt_crc_t; + +/** + * intel_pipe_crc_source: + * + * Enumeration of all supported pipe CRC sources. Not all platforms and all + * outputs support all of them. Generic tests should just use + * INTEL_PIPE_CRC_SOURCE_AUTO. It should always map to an end-of-pipe CRC + * suitable for checking planes, cursor, color correction and any other + * output-agnostic features. + */ +enum intel_pipe_crc_source { + INTEL_PIPE_CRC_SOURCE_NONE, + INTEL_PIPE_CRC_SOURCE_PLANE1, + INTEL_PIPE_CRC_SOURCE_PLANE2, + INTEL_PIPE_CRC_SOURCE_PF, + INTEL_PIPE_CRC_SOURCE_PIPE, + INTEL_PIPE_CRC_SOURCE_TV, + INTEL_PIPE_CRC_SOURCE_DP_B, + INTEL_PIPE_CRC_SOURCE_DP_C, + INTEL_PIPE_CRC_SOURCE_DP_D, + INTEL_PIPE_CRC_SOURCE_AUTO, + INTEL_PIPE_CRC_SOURCE_MAX, +}; + +bool igt_crc_is_null(igt_crc_t *crc); +bool igt_crc_equal(igt_crc_t *a, igt_crc_t *b); +char *igt_crc_to_string(igt_crc_t *crc); + +void igt_require_pipe_crc(void); +igt_pipe_crc_t * +igt_pipe_crc_new(enum pipe pipe, enum intel_pipe_crc_source source); +void igt_pipe_crc_free(igt_pipe_crc_t *pipe_crc); +void igt_pipe_crc_start(igt_pipe_crc_t *pipe_crc); +void igt_pipe_crc_stop(igt_pipe_crc_t *pipe_crc); +void igt_pipe_crc_get_crcs(igt_pipe_crc_t *pipe_crc, int n_crcs, + igt_crc_t **out_crcs); +void igt_pipe_crc_collect_crc(igt_pipe_crc_t *pipe_crc, igt_crc_t *out_crc); + +/* + * Drop caches + */ + +/** + * DROP_UNBOUND: + * + * Drop all currently unbound gem buffer objects from the cache. + */ +#define DROP_UNBOUND 0x1 +/** + * DROP_BOUND: + * + * Drop all inactive objects which are bound into some gpu address space. + */ +#define DROP_BOUND 0x2 +/** + * DROP_RETIRE: + * + * Wait for all outstanding gpu commands to complete, but do not take any + * further actions. + */ +#define DROP_RETIRE 0x4 +/** + * DROP_ACTIVE: + * + * Also drop active objects once retired. + */ +#define DROP_ACTIVE 0x8 +#define DROP_ALL (DROP_UNBOUND | \ + DROP_BOUND | \ + DROP_RETIRE | \ + DROP_ACTIVE) + +void igt_drop_caches_set(uint64_t val); + +/* + * Prefault control + */ + +void igt_disable_prefault(void); +void igt_enable_prefault(void); + +#endif /* __IGT_DEBUGFS_H__ */ diff --git a/lib/igt_fb.c b/lib/igt_fb.c new file mode 100644 index 0000000..9b41301 --- /dev/null +++ b/lib/igt_fb.c @@ -0,0 +1,807 @@ +/* + * Copyright © 2013,2014 Intel Corporation + * + * 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: + * Daniel Vetter + * Damien Lespiau + */ + +#define _GNU_SOURCE +#include +#include + +#include "drmtest.h" +#include "igt_fb.h" +#include "ioctl_wrappers.h" + +/** + * SECTION:igt_fb + * @short_description: Framebuffer handling and drawing library + * @title: i-g-t framebuffer + * @include: igt_fb.h + * + * This library contains helper functions for handling kms framebuffer objects + * using #igt_fb structures to track all the metadata. igt_create_fb() creates + * a basic framebufffer and igt_remove_fb() cleans everything up again. + * + * It also supports drawing using the cairo library and provides some simplified + * helper functions to easily draw test patterns. The main function to create a + * cairo drawing context for a framebuffer object is igt_get_cairo_ctx(). + * + * Finally it also pulls in the drm fourcc headers and provides some helper + * functions to work with these pixel format codes. + */ + +/* drm fourcc/cairo format maps */ +#define DF(did, cid, _bpp, _depth) \ + { DRM_FORMAT_##did, CAIRO_FORMAT_##cid, # did, _bpp, _depth } +static struct format_desc_struct { + uint32_t drm_id; + cairo_format_t cairo_id; + const char *name; + int bpp; + int depth; +} format_desc[] = { + DF(RGB565, RGB16_565, 16, 16), + //DF(RGB888, INVALID, 24, 24), + DF(XRGB8888, RGB24, 32, 24), + DF(XRGB2101010, RGB30, 32, 30), + DF(ARGB8888, ARGB32, 32, 32), +}; +#undef DF + +#define for_each_format(f) \ + for (f = format_desc; f - format_desc < ARRAY_SIZE(format_desc); f++) + + +/* helpers to create nice-looking framebuffers */ +static int create_bo_for_fb(int fd, int width, int height, int bpp, + unsigned int tiling, unsigned bo_size, + uint32_t *gem_handle_ret, + unsigned *size_ret, + unsigned *stride_ret) +{ + uint32_t gem_handle; + int size, ret = 0; + unsigned stride; + + if (tiling) { + int v; + + /* Round the tiling up to the next power-of-two and the + * region up to the next pot fence size so that this works + * on all generations. + * + * This can still fail if the framebuffer is too large to + * be tiled. But then that failure is expected. + */ + + v = width * bpp / 8; + for (stride = 512; stride < v; stride *= 2) + ; + + v = stride * height; + for (size = 1024*1024; size < v; size *= 2) + ; + } else { + /* Scan-out has a 64 byte alignment restriction */ + stride = (width * (bpp / 8) + 63) & ~63; + size = stride * height; + } + + if (bo_size == 0) + bo_size = size; + gem_handle = gem_create(fd, bo_size); + + if (tiling) + ret = __gem_set_tiling(fd, gem_handle, tiling, stride); + + *stride_ret = stride; + *size_ret = size; + *gem_handle_ret = gem_handle; + + return ret; +} + +/** + * igt_paint_color: + * @cr: cairo drawing context + * @x: pixel x-coordination of the fill rectangle + * @y: pixel y-coordination of the fill rectangle + * @w: width of the fill rectangle + * @h: height of the fill rectangle + * @r: red value to use as fill color + * @g: gree value to use as fill color + * @b: blue value to use as fill color + * + * This functions draws a solid rectangle with the given color using the drawing + * context @cr. + */ +void igt_paint_color(cairo_t *cr, int x, int y, int w, int h, + double r, double g, double b) +{ + cairo_rectangle(cr, x, y, w, h); + cairo_set_source_rgb(cr, r, g, b); + cairo_fill(cr); +} + +/** + * igt_paint_color_alpha: + * @cr: cairo drawing context + * @x: pixel x-coordination of the fill rectangle + * @y: pixel y-coordination of the fill rectangle + * @w: width of the fill rectangle + * @h: height of the fill rectangle + * @r: red value to use as fill color + * @g: gree value to use as fill color + * @b: blue value to use as fill color + * @a: alpha value to use as fill color + * + * This functions draws a rectangle with the given color and alpha values using + * the drawing context @cr. + */ +void igt_paint_color_alpha(cairo_t *cr, int x, int y, int w, int h, + double r, double g, double b, double a) +{ + cairo_rectangle(cr, x, y, w, h); + cairo_set_source_rgba(cr, r, g, b, a); + cairo_fill(cr); +} + +/** + * igt_paint_color_gradient: + * @cr: cairo drawing context + * @x: pixel x-coordination of the fill rectangle + * @y: pixel y-coordination of the fill rectangle + * @w: width of the fill rectangle + * @h: height of the fill rectangle + * @r: red value to use as fill color + * @g: gree value to use as fill color + * @b: blue value to use as fill color + * + * This functions draws a gradient into the rectangle which fades in from black + * to the given values using the drawing context @cr. + */ +void +igt_paint_color_gradient(cairo_t *cr, int x, int y, int w, int h, + int r, int g, int b) +{ + cairo_pattern_t *pat; + + pat = cairo_pattern_create_linear(x, y, x + w, y + h); + cairo_pattern_add_color_stop_rgba(pat, 1, 0, 0, 0, 1); + cairo_pattern_add_color_stop_rgba(pat, 0, r, g, b, 1); + + cairo_rectangle(cr, x, y, w, h); + cairo_set_source(cr, pat); + cairo_fill(cr); + cairo_pattern_destroy(pat); +} + +static void +paint_test_patterns(cairo_t *cr, int width, int height) +{ + double gr_height, gr_width; + int x, y; + + y = height * 0.10; + gr_width = width * 0.75; + gr_height = height * 0.08; + x = (width / 2) - (gr_width / 2); + + igt_paint_color_gradient(cr, x, y, gr_width, gr_height, 1, 0, 0); + + y += gr_height; + igt_paint_color_gradient(cr, x, y, gr_width, gr_height, 0, 1, 0); + + y += gr_height; + igt_paint_color_gradient(cr, x, y, gr_width, gr_height, 0, 0, 1); + + y += gr_height; + igt_paint_color_gradient(cr, x, y, gr_width, gr_height, 1, 1, 1); +} + +/** + * igt_cairo_printf_line: + * @cr: cairo drawing context + * @align: text alignment + * @yspacing: additional y-direction feed after this line + * @fmt: format string + * @...: optional arguments used in the format string + * + * This is a little helper to draw text onto framebuffers. All the initial setup + * (like setting the font size and the moving to the starting position) still + * needs to be done manually with explicit cairo calls on @cr. + * + * Returns: + * The width of the drawn text. + */ +int igt_cairo_printf_line(cairo_t *cr, enum igt_text_align align, + double yspacing, const char *fmt, ...) +{ + double x, y, xofs, yofs; + cairo_text_extents_t extents; + char *text; + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vasprintf(&text, fmt, ap); + igt_assert(ret >= 0); + va_end(ap); + + cairo_text_extents(cr, text, &extents); + + xofs = yofs = 0; + if (align & align_right) + xofs = -extents.width; + else if (align & align_hcenter) + xofs = -extents.width / 2; + + if (align & align_top) + yofs = extents.height; + else if (align & align_vcenter) + yofs = extents.height / 2; + + cairo_get_current_point(cr, &x, &y); + if (xofs || yofs) + cairo_rel_move_to(cr, xofs, yofs); + + cairo_text_path(cr, text); + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_stroke_preserve(cr); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_fill(cr); + + cairo_move_to(cr, x, y + extents.height + yspacing); + + free(text); + + return extents.width; +} + +static void +paint_marker(cairo_t *cr, int x, int y) +{ + enum igt_text_align align; + int xoff, yoff; + + cairo_move_to(cr, x, y - 20); + cairo_line_to(cr, x, y + 20); + cairo_move_to(cr, x - 20, y); + cairo_line_to(cr, x + 20, y); + cairo_new_sub_path(cr); + cairo_arc(cr, x, y, 10, 0, M_PI * 2); + cairo_set_line_width(cr, 4); + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_stroke_preserve(cr); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_set_line_width(cr, 2); + cairo_stroke(cr); + + xoff = x ? -20 : 20; + align = x ? align_right : align_left; + + yoff = y ? -20 : 20; + align |= y ? align_bottom : align_top; + + cairo_move_to(cr, x + xoff, y + yoff); + cairo_set_font_size(cr, 18); + igt_cairo_printf_line(cr, align, 0, "(%d, %d)", x, y); +} + +/** + * igt_paint_test_pattern: + * @cr: cairo drawing context + * @width: width of the visible area + * @height: height of the visible area + * + * This functions draws an entire set of test patterns for the given visible + * area using the drawing context @cr. This is useful for manual visual + * inspection of displayed framebuffers. + * + * The test patterns include + * - corner markers to check for over/underscan and + * - a set of color and b/w gradients. + */ +void igt_paint_test_pattern(cairo_t *cr, int width, int height) +{ + paint_test_patterns(cr, width, height); + + cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE); + + /* Paint corner markers */ + paint_marker(cr, 0, 0); + paint_marker(cr, width, 0); + paint_marker(cr, 0, height); + paint_marker(cr, width, height); + + igt_assert(!cairo_status(cr)); +} + +/** + * igt_paint_image: + * @cr: cairo drawing context + * @filename: filename of the png image to draw + * @dst_x: pixel x-coordination of the destination rectangle + * @dst_y: pixel y-coordination of the destination rectangle + * @dst_width: width of the destination rectangle + * @dst_height: height of the destination rectangle + * + * This function can be used to draw a scaled version of the supplied png image. + * This is currently only used by the CR-code based testing in the "testdisplay" + * testcase. + */ +void igt_paint_image(cairo_t *cr, const char *filename, + int dst_x, int dst_y, int dst_width, int dst_height) +{ + cairo_surface_t *image; + int img_width, img_height; + double scale_x, scale_y; + + image = cairo_image_surface_create_from_png(filename); + igt_assert(cairo_surface_status(image) == CAIRO_STATUS_SUCCESS); + + img_width = cairo_image_surface_get_width(image); + img_height = cairo_image_surface_get_height(image); + + scale_x = (double)dst_width / img_width; + scale_y = (double)dst_height / img_height; + + cairo_save(cr); + + cairo_translate(cr, dst_x, dst_y); + cairo_scale(cr, scale_x, scale_y); + cairo_set_source_surface(cr, image, 0, 0); + cairo_paint(cr); + + cairo_surface_destroy(image); + + cairo_restore(cr); +} + +/** + * igt_create_fb_with_bo_size: + * @fd: open i915 drm file descriptor + * @width: width of the framebuffer in pixel + * @height: height of the framebuffer in pixel + * @format: drm fourcc pixel format code + * @tiling: tiling layout of the framebuffer + * @fb: pointer to an #igt_fb structure + * @bo_size: size of the backing bo (0 for minimum needed size) + * + * This function allocates a gem buffer object suitable to back a framebuffer + * with the requested properties and then wraps it up in a drm framebuffer + * object of the requested size. All metadata is stored in @fb. + * + * The backing storage of the framebuffer is filled with all zeros, i.e. black + * for rgb pixel formats. + * + * Returns: + * The kms id of the created framebuffer. + */ +unsigned int +igt_create_fb_with_bo_size(int fd, int width, int height, + uint32_t format, unsigned int tiling, + struct igt_fb *fb, unsigned bo_size) +{ + uint32_t handles[4]; + uint32_t pitches[4]; + uint32_t offsets[4]; + uint32_t fb_id; + int bpp; + + memset(fb, 0, sizeof(*fb)); + memset(handles, 0, sizeof(handles)); + memset(pitches, 0, sizeof(pitches)); + memset(offsets, 0, sizeof(offsets)); + + bpp = igt_drm_format_to_bpp(format); + + igt_debug("%s(width=%d, height=%d, format=0x%x [bpp=%d], tiling=%d, size=%d\n", + __func__, width, height, format, bpp, tiling, bo_size); + do_or_die(create_bo_for_fb(fd, width, height, bpp, tiling, bo_size, + &fb->gem_handle, &fb->size, &fb->stride)); + + handles[0] = fb->gem_handle; + pitches[0] = fb->stride; + + igt_debug("%s(handle=%d, pitch=%d)\n", + __func__, handles[0], pitches[0]); + do_or_die(drmModeAddFB2(fd, width, height, format, + handles, pitches, offsets, + &fb_id, 0)); + + fb->width = width; + fb->height = height; + fb->tiling = tiling; + fb->drm_format = format; + fb->fb_id = fb_id; + + return fb_id; +} + +/** + * igt_create_fb: + * @fd: open i915 drm file descriptor + * @width: width of the framebuffer in pixel + * @height: height of the framebuffer in pixel + * @format: drm fourcc pixel format code + * @tiling: tiling layout of the framebuffer + * @fb: pointer to an #igt_fb structure + * + * This function allocates a gem buffer object suitable to back a framebuffer + * with the requested properties and then wraps it up in a drm framebuffer + * object. All metadata is stored in @fb. + * + * The backing storage of the framebuffer is filled with all zeros, i.e. black + * for rgb pixel formats. + * + * Returns: + * The kms id of the created framebuffer. + */ +unsigned int igt_create_fb(int fd, int width, int height, uint32_t format, + unsigned int tiling, struct igt_fb *fb) +{ + return igt_create_fb_with_bo_size(fd, width, height, format, tiling, fb, 0); +} + +/** + * igt_create_color_fb: + * @fd: open i915 drm file descriptor + * @width: width of the framebuffer in pixel + * @height: height of the framebuffer in pixel + * @format: drm fourcc pixel format code + * @tiling: tiling layout of the framebuffer + * @r: red value to use as fill color + * @g: gree value to use as fill color + * @b: blue value to use as fill color + * @fb: pointer to an #igt_fb structure + * + * This function allocates a gem buffer object suitable to back a framebuffer + * with the requested properties and then wraps it up in a drm framebuffer + * object. All metadata is stored in @fb. + * + * Compared to igt_create_fb() this function also fills the entire framebuffer + * with the given color, which is useful for some simple pipe crc based tests. + * + * Returns: + * The kms id of the created framebuffer on success or a negative error code on + * failure. + */ +unsigned int igt_create_color_fb(int fd, int width, int height, + uint32_t format, unsigned int tiling, + double r, double g, double b, + struct igt_fb *fb /* out */) +{ + unsigned int fb_id; + cairo_t *cr; + + fb_id = igt_create_fb(fd, width, height, format, tiling, fb); + igt_assert(fb_id); + + cr = igt_get_cairo_ctx(fd, fb); + igt_paint_color(cr, 0, 0, width, height, r, g, b); + igt_assert(cairo_status(cr) == 0); + cairo_destroy(cr); + + return fb_id; +} + +struct box { + int x, y, width, height; +}; + +struct stereo_fb_layout { + int fb_width, fb_height; + struct box left, right; +}; + +static void box_init(struct box *box, int x, int y, int bwidth, int bheight) +{ + box->x = x; + box->y = y; + box->width = bwidth; + box->height = bheight; +} + + +static void stereo_fb_layout_from_mode(struct stereo_fb_layout *layout, + drmModeModeInfo *mode) +{ + unsigned int format = mode->flags & DRM_MODE_FLAG_3D_MASK; + const int hdisplay = mode->hdisplay, vdisplay = mode->vdisplay; + int middle; + + switch (format) { + case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM: + layout->fb_width = hdisplay; + layout->fb_height = vdisplay; + + middle = vdisplay / 2; + box_init(&layout->left, 0, 0, hdisplay, middle); + box_init(&layout->right, + 0, middle, hdisplay, vdisplay - middle); + break; + case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF: + layout->fb_width = hdisplay; + layout->fb_height = vdisplay; + + middle = hdisplay / 2; + box_init(&layout->left, 0, 0, middle, vdisplay); + box_init(&layout->right, + middle, 0, hdisplay - middle, vdisplay); + break; + case DRM_MODE_FLAG_3D_FRAME_PACKING: + { + int vactive_space = mode->vtotal - vdisplay; + + layout->fb_width = hdisplay; + layout->fb_height = 2 * vdisplay + vactive_space; + + box_init(&layout->left, + 0, 0, hdisplay, vdisplay); + box_init(&layout->right, + 0, vdisplay + vactive_space, hdisplay, vdisplay); + break; + } + default: + igt_assert(0); + } +} + +/** + * igt_create_stereo_fb: + * @drm_fd: open i915 drm file descriptor + * @mode: A stereo 3D mode. + * @format: drm fourcc pixel format code + * @tiling: tiling layout of the framebuffer + * + * Create a framebuffer for use with the stereo 3D mode specified by @mode. + * + * Returns: + * The kms id of the created framebuffer on success or a negative error code on + * failure. + */ +unsigned int igt_create_stereo_fb(int drm_fd, drmModeModeInfo *mode, + uint32_t format, unsigned int tiling) +{ + struct stereo_fb_layout layout; + cairo_t *cr; + uint32_t fb_id; + struct igt_fb fb; + + stereo_fb_layout_from_mode(&layout, mode); + fb_id = igt_create_fb(drm_fd, layout.fb_width, layout.fb_height, format, + tiling, &fb); + cr = igt_get_cairo_ctx(drm_fd, &fb); + + igt_paint_image(cr, IGT_DATADIR"/1080p-left.png", + layout.left.x, layout.left.y, + layout.left.width, layout.left.height); + igt_paint_image(cr, IGT_DATADIR"/1080p-right.png", + layout.right.x, layout.right.y, + layout.right.width, layout.right.height); + + cairo_destroy(cr); + + return fb_id; +} + +static cairo_format_t drm_format_to_cairo(uint32_t drm_format) +{ + struct format_desc_struct *f; + + for_each_format(f) + if (f->drm_id == drm_format) + return f->cairo_id; + + igt_assert_f(0, "can't find a cairo format for %08x (%s)\n", + drm_format, igt_format_str(drm_format)); +} + +static void destroy_cairo_surface__gtt(void *arg) +{ + struct igt_fb *fb = arg; + + munmap(cairo_image_surface_get_data(fb->cairo_surface), fb->size); + fb->cairo_surface = NULL; +} + +static void create_cairo_surface__gtt(int fd, struct igt_fb *fb) +{ + fb->cairo_surface = + cairo_image_surface_create_for_data(gem_mmap(fd, fb->gem_handle, fb->size, PROT_READ | PROT_WRITE), + drm_format_to_cairo(fb->drm_format), + fb->width, fb->height, fb->stride); + + cairo_surface_set_user_data(fb->cairo_surface, + (cairo_user_data_key_t *)create_cairo_surface__gtt, + fb, destroy_cairo_surface__gtt); +} + +static cairo_surface_t *get_cairo_surface(int fd, struct igt_fb *fb) +{ + if (fb->cairo_surface == NULL) + create_cairo_surface__gtt(fd, fb); + + gem_set_domain(fd, fb->gem_handle, + I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU); + + igt_assert(cairo_surface_status(fb->cairo_surface) == CAIRO_STATUS_SUCCESS); + return fb->cairo_surface; +} + +/** + * igt_get_cairo_ctx: + * @fd: open i915 drm file descriptor + * @fb: pointer to an #igt_fb structure + * + * This initializes a cairo surface for @fb and then allocates a drawing context + * for it. The return cairo drawing context should be released by calling + * cairo_destroy(). This also sets a default font for drawing text on + * framebuffers. + * + * Returns: + * The created cairo drawing context. + */ +cairo_t *igt_get_cairo_ctx(int fd, struct igt_fb *fb) +{ + cairo_surface_t *surface; + cairo_t *cr; + + surface = get_cairo_surface(fd, fb); + cr = cairo_create(surface); + cairo_surface_destroy(surface); + igt_assert(cairo_status(cr) == CAIRO_STATUS_SUCCESS); + + cairo_select_font_face(cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + igt_assert(cairo_status(cr) == CAIRO_STATUS_SUCCESS); + + return cr; +} + +/** + * igt_write_fb_to_png: + * @fd: open i915 drm file descriptor + * @fb: pointer to an #igt_fb structure + * @filename: target name for the png image + * + * This function stores the contents of the supplied framebuffer into a png + * image stored at @filename. + */ +void igt_write_fb_to_png(int fd, struct igt_fb *fb, const char *filename) +{ + cairo_surface_t *surface; + cairo_status_t status; + + surface = get_cairo_surface(fd, fb); + status = cairo_surface_write_to_png(surface, filename); + cairo_surface_destroy(surface); + + igt_assert(status == CAIRO_STATUS_SUCCESS); +} + +/** + * igt_remove_fb: + * @fd: open i915 drm file descriptor + * @fb: pointer to an #igt_fb structure + * + * This function releases all resources allocated in igt_create_fb() for @fb. + * Note that if this framebuffer is still in use on a primary plane the kernel + * will disable the corresponding crtc. + */ +void igt_remove_fb(int fd, struct igt_fb *fb) +{ + cairo_surface_destroy(fb->cairo_surface); + do_or_die(drmModeRmFB(fd, fb->fb_id)); + gem_close(fd, fb->gem_handle); +} + +/** + * igt_bpp_depth_to_drm_format: + * @bpp: desired bits per pixel + * @depth: desired depth + * + * Returns: + * The rgb drm fourcc pixel format code corresponding to the given @bpp and + * @depth values. Fails hard if no match was found. + */ +uint32_t igt_bpp_depth_to_drm_format(int bpp, int depth) +{ + struct format_desc_struct *f; + + for_each_format(f) + if (f->bpp == bpp && f->depth == depth) + return f->drm_id; + + + igt_assert_f(0, "can't find drm format with bpp=%d, depth=%d\n", bpp, + depth); +} + +/** + * igt_drm_format_to_bpp: + * @drm_format: drm fourcc pixel format code + * + * Returns: + * The bits per pixel for the given drm fourcc pixel format code. Fails hard if + * no match was found. + */ +uint32_t igt_drm_format_to_bpp(uint32_t drm_format) +{ + struct format_desc_struct *f; + + for_each_format(f) + if (f->drm_id == drm_format) + return f->bpp; + + igt_assert_f(0, "can't find a bpp format for %08x (%s)\n", + drm_format, igt_format_str(drm_format)); +} + +/** + * igt_format_str: + * @drm_format: drm fourcc pixel format code + * + * Returns: + * Human-readable fourcc pixel format code for @drm_format or "invalid" no match + * was found. + */ +const char *igt_format_str(uint32_t drm_format) +{ + struct format_desc_struct *f; + + for_each_format(f) + if (f->drm_id == drm_format) + return f->name; + + return "invalid"; +} + +/** + * igt_get_all_formats: + * @formats: pointer to pointer to store the allocated formats array + * @format_count: pointer to integer to store the size of the allocated array + * + * This functions returns an array of all the drm fourcc codes supported by this + * library. The caller must free the allocated array again with free(). + */ +void igt_get_all_formats(const uint32_t **formats, int *format_count) +{ + static uint32_t *drm_formats; + + if (!drm_formats) { + struct format_desc_struct *f; + uint32_t *format; + + drm_formats = calloc(ARRAY_SIZE(format_desc), + sizeof(*drm_formats)); + format = &drm_formats[0]; + for_each_format(f) + *format++ = f->drm_id; + } + + *formats = drm_formats; + *format_count = ARRAY_SIZE(format_desc); +} diff --git a/lib/igt_fb.h b/lib/igt_fb.h new file mode 100644 index 0000000..d9fb6bb --- /dev/null +++ b/lib/igt_fb.h @@ -0,0 +1,105 @@ +/* + * Copyright © 2013,2014 Intel Corporation + * + * 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: + * Daniel Vetter + * Damien Lespiau + */ + +#ifndef __IGT_FB_H__ +#define __IGT_FB_H__ + +/* cairo is assumed available on linux. On Android we check for ANDROID_HAS_CAIRO */ +#if (!defined(ANDROID)) || (defined(ANDROID) && ANDROID_HAS_CAIRO) +#include +#else +typedef struct _cairo_surface cairo_surface_t; +typedef struct _cairo cairo_t; +#endif + +#include +#include +#include +#include + +#include + +/* helpers to create nice-looking framebuffers */ +struct igt_fb { + uint32_t fb_id; + uint32_t gem_handle; + uint32_t drm_format; + int width; + int height; + unsigned stride; + unsigned tiling; + unsigned size; + cairo_surface_t *cairo_surface; +}; + +enum igt_text_align { + align_left, + align_bottom = align_left, + align_right = 0x01, + align_top = 0x02, + align_vcenter = 0x04, + align_hcenter = 0x08, +}; + +unsigned int +igt_create_fb_with_bo_size(int fd, int width, int height, + uint32_t format, unsigned int tiling, + struct igt_fb *fb, unsigned bo_size); +unsigned int igt_create_fb(int fd, int width, int height, uint32_t format, + unsigned int tiling, struct igt_fb *fb); +unsigned int igt_create_color_fb(int fd, int width, int height, + uint32_t format, unsigned int tiling, + double r, double g, double b, + struct igt_fb *fb /* out */); +unsigned int igt_create_stereo_fb(int drm_fd, drmModeModeInfo *mode, + uint32_t format, unsigned int tiling); +void igt_remove_fb(int fd, struct igt_fb *fb); + +/* cairo-based painting */ +cairo_t *igt_get_cairo_ctx(int fd, struct igt_fb *fb); +void igt_paint_color(cairo_t *cr, int x, int y, int w, int h, + double r, double g, double b); +void igt_paint_color_alpha(cairo_t *cr, int x, int y, int w, int h, + double r, double g, double b, double a); +void igt_paint_color_gradient(cairo_t *cr, int x, int y, int w, int h, + int r, int g, int b); +void igt_paint_test_pattern(cairo_t *cr, int width, int height); +void igt_paint_image(cairo_t *cr, const char *filename, + int dst_x, int dst_y, int dst_width, int dst_height); +void igt_write_fb_to_png(int fd, struct igt_fb *fb, const char *filename); +int igt_cairo_printf_line(cairo_t *cr, enum igt_text_align align, + double yspacing, const char *fmt, ...) + __attribute__((format (printf, 4, 5))); + +/* helpers to handle drm fourcc codes */ +uint32_t igt_bpp_depth_to_drm_format(int bpp, int depth); +uint32_t igt_drm_format_to_bpp(uint32_t drm_format); +const char *igt_format_str(uint32_t drm_format); +void igt_get_all_formats(const uint32_t **formats, int *format_count); + +#endif /* __IGT_FB_H__ */ + diff --git a/lib/igt_gt.c b/lib/igt_gt.c new file mode 100644 index 0000000..84aa5d3 --- /dev/null +++ b/lib/igt_gt.c @@ -0,0 +1,341 @@ +/* + * Copyright © 2014 Intel Corporation + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "drmtest.h" +#include "igt_core.h" +#include "igt_gt.h" +#include "igt_debugfs.h" +#include "ioctl_wrappers.h" +#include "intel_reg.h" +#include "intel_chipset.h" + +/** + * SECTION:igt_gt + * @short_description: GT support library + * @title: i-g-t gt + * @include: igt_gt.h + * + * This library provides various auxiliary helper functions to handle general + * interactions with the GT like forcewake handling, injecting hangs or stopping + * engines. + */ + + +/** + * igt_require_hang_ring: + * @fd: open i915 drm file descriptor + * @ring: execbuf ring flag + * + * Convenience helper to check whether advanced hang injection is supported by + * the kernel. Uses igt_skip to automatically skip the test/subtest if this + * isn't the case. + */ +void igt_require_hang_ring(int fd, int ring) +{ + gem_context_require_param(fd, LOCAL_CONTEXT_PARAM_BAN_PERIOD); + igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5); +} + +/** + * igt_hang_ring: + * @fd: open i915 drm file descriptor + * @ring: execbuf ring flag + * + * This helper function injects a hanging batch into @ring. It returns a + * #igt_hang_ring_t structure which must be passed to igt_post_hang_ring() for + * hang post-processing (after the gpu hang interaction has been tested. + * + * Returns: + * Structure with helper internal state for igt_post_hang_ring(). + */ +igt_hang_ring_t igt_hang_ring(int fd, int ring) +{ + struct drm_i915_gem_relocation_entry reloc; + struct drm_i915_gem_execbuffer2 execbuf; + struct drm_i915_gem_exec_object2 exec; + struct local_i915_gem_context_param param; + uint32_t b[8]; + unsigned ban; + unsigned len; + + param.context = 0; + param.size = 0; + param.param = LOCAL_CONTEXT_PARAM_BAN_PERIOD; + param.value = 0; + gem_context_get_param(fd, ¶m); + ban = param.value; + + param.value = 0; + gem_context_set_param(fd, ¶m); + + memset(&reloc, 0, sizeof(reloc)); + memset(&exec, 0, sizeof(exec)); + memset(&execbuf, 0, sizeof(execbuf)); + + exec.handle = gem_create(fd, 4096); + exec.relocation_count = 1; + exec.relocs_ptr = (uintptr_t)&reloc; + + len = 2; + if (intel_gen(intel_get_drm_devid(fd)) >= 8) + len++; + b[0] = MI_BATCH_BUFFER_START | (len - 2); + b[len] = MI_BATCH_BUFFER_END; + b[len+1] = MI_NOOP; + gem_write(fd, exec.handle, 0, b, sizeof(b)); + + reloc.offset = 4; + reloc.target_handle = exec.handle; + reloc.read_domains = I915_GEM_DOMAIN_COMMAND; + + execbuf.buffers_ptr = (uintptr_t)&exec; + execbuf.buffer_count = 1; + execbuf.batch_len = sizeof(b); + execbuf.flags = ring; + gem_execbuf(fd, &execbuf); + + return (struct igt_hang_ring){ exec.handle, ban }; +} + +/** + * igt_post_hang_ring: + * @fd: open i915 drm file descriptor + * @arg: hang state from igt_hang_ring() + * + * This function does the necessary post-processing after a gpu hang injected + * with igt_hang_ring(). + */ +void igt_post_hang_ring(int fd, struct igt_hang_ring arg) +{ + struct local_i915_gem_context_param param; + + if (arg.handle == 0) + return; + + gem_set_domain(fd, arg.handle, + I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); + gem_close(fd, arg.handle); + + param.context = 0; + param.size = 0; + param.param = LOCAL_CONTEXT_PARAM_BAN_PERIOD; + param.value = arg.ban; + gem_context_set_param(fd, ¶m); +} + +/* GPU abusers */ +static struct igt_helper_process hang_helper; +static void __attribute__((noreturn)) +hang_helper_process(pid_t pid, int fd) +{ + while (1) { + if (kill(pid, 0)) /* Parent has died, so must we. */ + exit(0); + + igt_post_hang_ring(fd, + igt_hang_ring(fd, I915_EXEC_DEFAULT)); + + sleep(1); + } +} + +/** + * igt_fork_hang_helper: + * + * Fork a child process using #igt_fork_helper to hang the default engine + * of the GPU at regular intervals. + * + * This is useful to exercise slow running code (such as aperture placement) + * which needs to be robust against a GPU reset. + * + * In tests with subtests this function can be called outside of failure + * catching code blocks like #igt_fixture or #igt_subtest. + */ +int igt_fork_hang_helper(void) +{ + int fd, gen; + + if (igt_only_list_subtests()) + return 1; + + fd = drm_open_any(); + if (fd == -1) + return 0; + + gen = intel_gen(intel_get_drm_devid(fd)); + if (gen < 5) { + close(fd); + return 0; + } + + igt_fork_helper(&hang_helper) + hang_helper_process(getppid(), fd); + + close(fd); + return 1; +} + +/** + * igt_stop_hang_helper: + * + * Stops the child process spawned with igt_fork_hang_helper(). + * + * In tests with subtests this function can be called outside of failure + * catching code blocks like #igt_fixture or #igt_subtest. + */ +void igt_stop_hang_helper(void) +{ + if (igt_only_list_subtests()) + return; + + igt_stop_helper(&hang_helper); +} + +/** + * igt_open_forcewake_handle: + * + * This functions opens the debugfs forcewake file and so prevents the GT from + * suspending. The reference is automatically dropped when the is closed. + * + * Returns: + * The file descriptor of the forcewake handle or -1 if that didn't work out. + */ +int igt_open_forcewake_handle(void) +{ + if (getenv("IGT_NO_FORCEWAKE")) + return -1; + return igt_debugfs_open("i915_forcewake_user", O_WRONLY); +} + +/** + * igt_to_stop_ring_flag: + * @ring: the specified ring flag from execbuf ioctl (I915_EXEC_*) + * + * This converts the specified ring to a ring flag to be used + * with igt_get_stop_rings() and igt_set_stop_rings(). + * + * Returns: + * Ring flag for the given ring. + */ +enum stop_ring_flags igt_to_stop_ring_flag(int ring) { + if (ring == I915_EXEC_DEFAULT) + return STOP_RING_RENDER; + + igt_assert(ring && ((ring & ~I915_EXEC_RING_MASK) == 0)); + return 1 << (ring - 1); +} + +static void stop_rings_write(uint32_t mask) +{ + int fd; + char buf[80]; + + igt_assert(snprintf(buf, sizeof(buf), "0x%08x", mask) == 10); + fd = igt_debugfs_open("i915_ring_stop", O_WRONLY); + igt_assert(fd >= 0); + + igt_assert(write(fd, buf, strlen(buf)) == strlen(buf)); + close(fd); +} + +/** + * igt_get_stop_rings: + * + * Read current ring flags from 'i915_ring_stop' debugfs entry. + * + * Returns: + * Current ring flags. + */ +enum stop_ring_flags igt_get_stop_rings(void) +{ + int fd; + char buf[80]; + int l; + unsigned long long ring_mask; + + fd = igt_debugfs_open("i915_ring_stop", O_RDONLY); + igt_assert(fd >= 0); + l = read(fd, buf, sizeof(buf)-1); + igt_assert(l > 0); + igt_assert(l < sizeof(buf)); + + buf[l] = '\0'; + + close(fd); + + errno = 0; + ring_mask = strtoull(buf, NULL, 0); + igt_assert(errno == 0); + return ring_mask; +} + +/** + * igt_set_stop_rings: + * @flags: Ring flags to write + * + * This writes @flags to 'i915_ring_stop' debugfs entry. Driver will + * prevent the CPU from writing tail pointer for the ring that @flags + * specify. Note that the ring is not stopped right away. Instead any + * further command emissions won't be executed after the flag is set. + * + * This is the least invasive way to make the GPU stuck. Hence you must + * set this after a batch submission with it's own invalid or endless + * looping instructions. In this case it is merely for giving notification + * for the driver that this was simulated hang, as the batch would have + * caused hang in any case. On the other hand if you use a valid or noop + * batch and want to hang the ring (GPU), you must set corresponding flag + * before submitting the batch. + * + * Driver checks periodically if a ring is making any progress, and if + * it is not, it will declare the ring to be hung and will reset the GPU. + * After reset, the driver will clear flags in 'i915_ring_stop' + * + * Note: Always when hanging the GPU, use igt_set_stop_rings() to + * notify the driver. Driver controls hang log messaging based on + * these flags and thus prevents false positives on logs. + */ +void igt_set_stop_rings(enum stop_ring_flags flags) +{ + enum stop_ring_flags current; + + igt_assert((flags & ~(STOP_RING_ALL | + STOP_RING_ALLOW_BAN | + STOP_RING_ALLOW_ERRORS)) == 0); + + current = igt_get_stop_rings(); + igt_assert_f(flags == 0 || current == 0, + "previous i915_ring_stop is still 0x%x\n", current); + + stop_rings_write(flags); + current = igt_get_stop_rings(); + igt_warn_on_f(current != flags, + "i915_ring_stop readback mismatch 0x%x vs 0x%x\n", + flags, current); +} diff --git a/lib/igt_gt.h b/lib/igt_gt.h new file mode 100644 index 0000000..5105423 --- /dev/null +++ b/lib/igt_gt.h @@ -0,0 +1,77 @@ +/* + * Copyright © 2014 Intel Corporation + * + * 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 IGT_GT_H +#define IGT_GT_H + +#include "igt_debugfs.h" + +void igt_require_hang_ring(int fd, int ring); + +typedef struct igt_hang_ring { + unsigned handle; + unsigned ban; +} igt_hang_ring_t; + +struct igt_hang_ring igt_hang_ring(int fd, int ring); +void igt_post_hang_ring(int fd, struct igt_hang_ring arg); + +int igt_fork_hang_helper(void); +void igt_stop_hang_helper(void); + +int igt_open_forcewake_handle(void); + +/** + * stop_ring_flags: + * @STOP_RING_NONE: Can be used to clear the pending stop (warning: hang might + * be declared already). Returned by igt_get_stop_rings() if there is + * no currently stopped rings. + * @STOP_RING_RENDER: Render ring + * @STOP_RING_BSD: Video encoding/decoding ring + * @STOP_RING_BLT: Blitter ring + * @STOP_RING_VEBOX: Video enhancement ring + * @STOP_RING_ALL: All rings + * @STOP_RING_ALLOW_ERRORS: Driver will not omit expected DRM_ERRORS + * @STOP_RING_ALLOW_BAN: Driver will use context ban policy + * @STOP_RING_DEFAULTS: STOP_RING_ALL | STOP_RING_ALLOW_ERRORS + * + * Enumeration of all supported flags for igt_set_stop_rings(). + * + */ +enum stop_ring_flags { + STOP_RING_NONE = 0x00, + STOP_RING_RENDER = (1 << 0), + STOP_RING_BSD = (1 << 1), + STOP_RING_BLT = (1 << 2), + STOP_RING_VEBOX = (1 << 3), + STOP_RING_ALL = 0xff, + STOP_RING_ALLOW_ERRORS = (1 << 30), + STOP_RING_ALLOW_BAN = (1 << 31), + STOP_RING_DEFAULTS = STOP_RING_ALL | STOP_RING_ALLOW_ERRORS, +}; + +enum stop_ring_flags igt_to_stop_ring_flag(int ring); +void igt_set_stop_rings(enum stop_ring_flags flags); +enum stop_ring_flags igt_get_stop_rings(void); + +#endif /* IGT_GT_H */ diff --git a/lib/igt_kms.c b/lib/igt_kms.c new file mode 100644 index 0000000..9c131f0 --- /dev/null +++ b/lib/igt_kms.c @@ -0,0 +1,1853 @@ +/* + * Copyright © 2013 Intel Corporation + * + * 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: + * Daniel Vetter + * Damien Lespiau + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LINUX_KD_H +#include +#elif HAVE_SYS_KD_H +#include +#endif +#include +#include + +#include + +#include "drmtest.h" +#include "igt_kms.h" +#include "igt_aux.h" +#include "intel_chipset.h" +#include "igt_debugfs.h" + +/* + * There hasn't been a release of libdrm containing these #define's yet, so + * copy them here to allow compilation to succeed in the mean time. + * + * We can drop these #define's and just make i-g-t depend on the proper libdrm + * version in the future. + */ +#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 +#define DRM_PLANE_TYPE_OVERLAY 0 +#define DRM_PLANE_TYPE_PRIMARY 1 +#define DRM_PLANE_TYPE_CURSOR 2 + +/* list of connectors that need resetting on exit */ +#define MAX_CONNECTORS 32 +static char *forced_connectors[MAX_CONNECTORS + 1]; + + +#define VFREQ 60 +#define CLOCK 148500 +#define HACTIVE 1920 +#define HBLANK 280 +#define VACTIVE 1080 +#define VBLANK 45 +#define HOFFSET 88 +#define HPULSE 44 +#define VOFFSET 4 +#define VPULSE 5 + +#define HSIZE 52 +#define VSIZE 30 + +#define GAMMA(x) (x * 100) - 100 + +#define MANUFACTURER_ID(a, b, c) (a - '@') << 2 | (b - '@') >> 3, \ + (b - '@') << 5 | (c - '@') + + +#define ab(x, y) (x & 0xff), (y & 0xff), ((x & 0xf00) >> 4) | ((y & 0xf00) >> 8) +#define op(ho, hp, vo, vp) (ho & 0xff), (hp & 0xff), \ + ((vo & 0xf) << 4) | (vp & 0xf), \ + ((ho & 0x300) >> 2) | ((hp & 0x300) >> 4) \ + | ((vo & 0x30) >> 2) | (vp & 0x30 >> 4) + +static unsigned char base_edid[EDID_LENGTH] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, /* header */ + MANUFACTURER_ID('I', 'G', 'T'), + /* product code, serial number, week and year of manufacture */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x03, /* edid version (1.3) */ + /* basic display parameters */ + /* digital display, maximum horizontal image size, maximum vertical + * image size, gamma, features: RGB 4:4:4, native pixel format and + * refresh rate in descriptor 1 */ + 0x80, HSIZE, VSIZE, GAMMA(2.20), 0x02, + /* chromaticity coordinates */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* established timings: 640x480 60Hz, 800x600 60Hz, 1024x768 60Hz */ + 0x21, 0x08, 0x00, + /* standard timings */ + 0xd1, 0xc0, /* 1920x1080 60Hz */ + 0x81, 0xc0, /* 1280x720 60Hz */ + 0x61, 0x40, /* 1024x768 60Hz */ + 0x45, 0x40, /* 800x600 60Hz */ + 0x31, 0x40, /* 640x480 60Hz */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + /* descriptor 1 (preferred timing) */ + (CLOCK / 10) & 0x00ff, ((CLOCK / 10) & 0xff00) >> 8, + ab(HACTIVE, HBLANK), ab(VACTIVE, VBLANK), + op(HOFFSET, HPULSE, VOFFSET, VPULSE), + ab(HSIZE * 10, VSIZE * 10), + 0x00, 0x00, 0x00, + /* descriptor 2 (monitor range limits) */ + 0x00, 0x00, 0x00, 0xfd, 0x00, + VFREQ - 1, VFREQ + 1, /* minimum, maximum vertical field rate */ + (CLOCK / (HACTIVE + HBLANK)) - 1, /* minimum horizontal line rate */ + (CLOCK / (HACTIVE + HBLANK)) + 1, /* maximum horizontal line rate */ + (CLOCK / 10000) + 1, /* maximum pixel clock rate */ + 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + /* descriptor 3 (name descriptor) */ + 0x00, 0x00, 0x00, 0xfc, 0x00, 'I', 'G', 'T', 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + /* descriptor 4 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* extensions, checksum */ + 0x00, 0x00 +}; + +/** + * igt_kms_get_base_edid: + * + * Get the base edid block, which includes the following modes: + * + * - 1920x1080 60Hz + * - 1280x720 60Hz + * - 1024x768 60Hz + * - 800x600 60Hz + * - 640x480 60Hz + * + * This can be extended with further features using functions such as + * #kmstest_edid_add_3d. + * + * Returns: a basic edid block + */ +const unsigned char* igt_kms_get_base_edid(void) +{ + int i, sum = 0; + struct tm *tm; + time_t t; + + /* year of manufacture */ + t = time(NULL); + tm = localtime(&t); + base_edid[17] = tm->tm_year - 90; + + /* calculate checksum */ + for (i = 0; i < 127; i++) { + sum = sum + base_edid[i]; + } + base_edid[127] = 256 - sum; + + return base_edid; +} + + +/** + * SECTION:igt_kms + * @short_description: Kernel modesetting support library + * @title: i-g-t kms + * @include: igt_kms.h + * + * This library provides support to enumerate and set modeset configurations. + * + * There are two parts in this library: First the low level helper function + * which directly build on top of raw ioctls or the interfaces provided by + * libdrm. Those functions all have a kmstest_ prefix. + * + * The second part is a high-level library to manage modeset configurations + * which abstracts away some of the low-level details like the difference + * between legacy and universal plane support for setting cursors or in the + * future the difference between legacy and atomic commit. These high-level + * functions have all igt_ prefixes. This part is still very much work in + * progress and so also lacks a bit documentation for the individual functions. + * + * Note that this library's header pulls in the [i-g-t framebuffer](intel-gpu-tools-i-g-t-framebuffer.html) + * library as a dependency. + */ + +/** + * kmstest_pipe_name: + * @pipe: display pipe + * + * Returns: String represnting @pipe, e.g. "A". + */ +const char *kmstest_pipe_name(enum pipe pipe) +{ + const char *str[] = { "A", "B", "C" }; + + if (pipe > 2) + return "invalid"; + + return str[pipe]; +} + +/** + * kmstest_plane_name: + * @plane: display plane + * + * Returns: String represnting @pipe, e.g. "plane1". + */ +const char *kmstest_plane_name(enum igt_plane plane) +{ + static const char *names[] = { + [IGT_PLANE_1] = "plane1", + [IGT_PLANE_2] = "plane2", + [IGT_PLANE_3] = "plane3", + [IGT_PLANE_CURSOR] = "cursor", + }; + + igt_assert(plane < ARRAY_SIZE(names) && names[plane]); + + return names[plane]; +} + +static const char *mode_stereo_name(const drmModeModeInfo *mode) +{ + switch (mode->flags & DRM_MODE_FLAG_3D_MASK) { + case DRM_MODE_FLAG_3D_FRAME_PACKING: + return "FP"; + case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE: + return "FA"; + case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE: + return "LA"; + case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL: + return "SBSF"; + case DRM_MODE_FLAG_3D_L_DEPTH: + return "LD"; + case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH: + return "LDGFX"; + case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM: + return "TB"; + case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF: + return "SBSH"; + default: + return NULL; + } +} + +/** + * kmstest_dump_mode: + * @mode: libdrm mode structure + * + * Prints @mode to stdout in a huma-readable form. + */ +void kmstest_dump_mode(drmModeModeInfo *mode) +{ + const char *stereo = mode_stereo_name(mode); + + igt_info(" %s %d %d %d %d %d %d %d %d %d 0x%x 0x%x %d%s%s%s\n", + mode->name, mode->vrefresh, + mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal, + mode->flags, mode->type, mode->clock, + stereo ? " (3D:" : "", + stereo ? stereo : "", stereo ? ")" : ""); +} + +/** + * kmstest_get_pipe_from_crtc_id: + * @fd: DRM fd + * @crtc_id: DRM CRTC id + * + * Returns: The pipe number for the given DRM CRTC @crtc_id. This maps directly + * to an enum pipe value used in other helper functions. + */ +int kmstest_get_pipe_from_crtc_id(int fd, int crtc_id) +{ + struct drm_i915_get_pipe_from_crtc_id pfci; + int ret; + + memset(&pfci, 0, sizeof(pfci)); + pfci.crtc_id = crtc_id; + ret = drmIoctl(fd, DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID, &pfci); + igt_assert(ret == 0); + + return pfci.pipe; +} + +/* + * Returns: the previous mode, or KD_GRAPHICS if no /dev/tty0 was + * found and nothing was done. + */ +static signed long set_vt_mode(unsigned long mode) +{ + int fd; + unsigned long prev_mode; + static const char TTY0[] = "/dev/tty0"; + + if (access(TTY0, F_OK)) { + /* errno message should be "No such file". Do not + hardcode but ask strerror() in the very unlikely + case something else happened. */ + igt_debug("VT: %s: %s, cannot change its mode\n", + TTY0, strerror(errno)); + return KD_GRAPHICS; + } + + fd = open(TTY0, O_RDONLY); + if (fd < 0) + return -errno; + + prev_mode = 0; + if (drmIoctl(fd, KDGETMODE, &prev_mode)) + goto err; + if (drmIoctl(fd, KDSETMODE, (void *)mode)) + goto err; + + close(fd); + + return prev_mode; +err: + close(fd); + + return -errno; +} + +static unsigned long orig_vt_mode = -1UL; + +/** + * kmstest_restore_vt_mode: + * + * Restore the VT mode in use before #kmstest_set_vt_graphics_mode was called. + */ +void kmstest_restore_vt_mode(void) +{ + long ret; + + if (orig_vt_mode != -1UL) { + ret = set_vt_mode(orig_vt_mode); + + igt_assert(ret >= 0); + igt_debug("VT: original mode 0x%lx restored\n", orig_vt_mode); + orig_vt_mode = -1UL; + } +} + +/** + * kmstest_set_vt_graphics_mode: + * + * Sets the controlling VT (if available) into graphics/raw mode and installs + * an igt exit handler to set the VT back to text mode on exit. Use + * #kmstest_restore_vt_mode to restore the previous VT mode manually. + * + * All kms tests must call this function to make sure that the fbcon doesn't + * interfere by e.g. blanking the screen. + */ +void kmstest_set_vt_graphics_mode(void) +{ + long ret; + + igt_install_exit_handler((igt_exit_handler_t) kmstest_restore_vt_mode); + + igt_disable_exit_handler(); + ret = set_vt_mode(KD_GRAPHICS); + igt_enable_exit_handler(); + + igt_assert(ret >= 0); + orig_vt_mode = ret; + + igt_debug("VT: graphics mode set (mode was 0x%lx)\n", ret); +} + + +static void reset_connectors_at_exit(int sig) +{ + igt_reset_connectors(); +} + +/** + * kmstest_force_connector: + * @fd: drm file descriptor + * @connector: connector + * @state: state to force on @connector + * + * Force the specified state on the specified connector. + * + * Returns: true on success + */ +bool kmstest_force_connector(int drm_fd, drmModeConnector *connector, + enum kmstest_force_connector_state state) +{ + char *path, **tmp; + const char *value; + int debugfs_fd, ret, len; + uint32_t devid; + + devid = intel_get_drm_devid(drm_fd); + + /* forcing hdmi or dp connectors on HSW and BDW doesn't currently work, + * so fail early to allow the test to skip if required */ + if ((connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || + connector->connector_type == DRM_MODE_CONNECTOR_HDMIB || + connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) + && (IS_HASWELL(devid) || IS_BROADWELL(devid))) + return false; + + switch (state) { + case FORCE_CONNECTOR_ON: + value = "on"; + break; + case FORCE_CONNECTOR_DIGITAL: + value = "digital"; + break; + case FORCE_CONNECTOR_OFF: + value = "off"; + break; + + default: + case FORCE_CONNECTOR_UNSPECIFIED: + value = "unspecified"; + break; + } + + asprintf(&path, "%s-%d/force", + kmstest_connector_type_str(connector->connector_type), + connector->connector_type_id); + debugfs_fd = igt_debugfs_open(path, O_WRONLY | O_TRUNC); + + if (debugfs_fd == -1) { + return false; + } + + ret = write(debugfs_fd, value, strlen(value)); + close(debugfs_fd); + + for (len = 0, tmp = forced_connectors; *tmp; tmp++) { + /* check the connector is not already present */ + if (strcmp(*tmp, path) == 0) { + len = -1; + break; + } + len++; + } + + if (len != -1 && len < MAX_CONNECTORS) + forced_connectors[len] = path; + + if (len >= MAX_CONNECTORS) + igt_warn("Connector limit reached, %s will not be reset\n", + path); + + igt_debug("Connector %s is now forced %s\n", path, value); + igt_debug("Current forced connectors:\n"); + tmp = forced_connectors; + while (*tmp) { + igt_debug("\t%s\n", *tmp); + tmp++; + } + + igt_install_exit_handler(reset_connectors_at_exit); + + igt_assert(ret != -1); + return (ret == -1) ? false : true; +} + +/** + * kmstest_force_edid: + * @drm_fd: drm file descriptor + * @connector: connector to set @edid on + * @edid: An EDID data block + * @length: length of the EDID data. #EDID_LENGTH defines the standard EDID + * length + * + * Set the EDID data on @connector to @edid. See also #igt_kms_get_base_edid. + * + * If @length is zero, the forced EDID will be removed. + */ +void kmstest_force_edid(int drm_fd, drmModeConnector *connector, + const unsigned char *edid, size_t length) +{ + char *path; + int debugfs_fd, ret; + + asprintf(&path, "%s-%d/edid_override", + kmstest_connector_type_str(connector->connector_type), + connector->connector_type_id); + debugfs_fd = igt_debugfs_open(path, O_WRONLY | O_TRUNC); + free(path); + + igt_assert(debugfs_fd != -1); + + if (length == 0) + ret = write(debugfs_fd, "reset", 5); + else + ret = write(debugfs_fd, edid, length); + close(debugfs_fd); + + igt_assert(ret != -1); +} + +/** + * kmstest_get_connector_default_mode: + * @drm_fd: DRM fd + * @connector: libdrm connector + * @mode: libdrm mode + * + * Retrieves the default mode for @connector and stores it in @mode. + * + * Returns: true on success, false on failure + */ +bool kmstest_get_connector_default_mode(int drm_fd, drmModeConnector *connector, + drmModeModeInfo *mode) +{ + int i; + + if (!connector->count_modes) { + igt_warn("no modes for connector %d\n", + connector->connector_id); + return false; + } + + for (i = 0; i < connector->count_modes; i++) { + if (i == 0 || + connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) { + *mode = connector->modes[i]; + if (mode->type & DRM_MODE_TYPE_PREFERRED) + break; + } + } + + return true; +} + +/** + * kmstest_get_connector_config: + * @drm_fd: DRM fd + * @connector_id: DRM connector id + * @crtc_idx_mask: mask of allowed DRM CRTC indices + * @config: structure filled with the possible configuration + * + * This tries to find a suitable configuration for the given connector and CRTC + * constraint and fills it into @config. + */ +bool kmstest_get_connector_config(int drm_fd, uint32_t connector_id, + unsigned long crtc_idx_mask, + struct kmstest_connector_config *config) +{ + drmModeRes *resources; + drmModeConnector *connector; + drmModeEncoder *encoder; + int i, j; + + resources = drmModeGetResources(drm_fd); + if (!resources) { + igt_warn("drmModeGetResources failed"); + goto err1; + } + + /* First, find the connector & mode */ + connector = drmModeGetConnector(drm_fd, connector_id); + if (!connector) + goto err2; + + if (connector->connection != DRM_MODE_CONNECTED) + goto err3; + + if (!connector->count_modes) { + igt_warn("connector %d has no modes\n", connector_id); + goto err3; + } + + if (connector->connector_id != connector_id) { + igt_warn("connector id doesn't match (%d != %d)\n", + connector->connector_id, connector_id); + goto err3; + } + + /* + * Find given CRTC if crtc_id != 0 or else the first CRTC not in use. + * In both cases find the first compatible encoder and skip the CRTC + * if there is non such. + */ + encoder = NULL; /* suppress GCC warning */ + for (i = 0; i < resources->count_crtcs; i++) { + if (!resources->crtcs[i] || !(crtc_idx_mask & (1 << i))) + continue; + + /* Now get a compatible encoder */ + for (j = 0; j < connector->count_encoders; j++) { + encoder = drmModeGetEncoder(drm_fd, + connector->encoders[j]); + + if (!encoder) { + igt_warn("could not get encoder %d: %s\n", + resources->encoders[j], + strerror(errno)); + + continue; + } + + if (encoder->possible_crtcs & (1 << i)) + goto found; + + drmModeFreeEncoder(encoder); + } + } + + goto err3; + +found: + if (!kmstest_get_connector_default_mode(drm_fd, connector, + &config->default_mode)) + goto err4; + + config->connector = connector; + config->encoder = encoder; + config->crtc = drmModeGetCrtc(drm_fd, resources->crtcs[i]); + config->crtc_idx = i; + config->pipe = kmstest_get_pipe_from_crtc_id(drm_fd, + config->crtc->crtc_id); + + drmModeFreeResources(resources); + + return true; +err4: + drmModeFreeEncoder(encoder); +err3: + drmModeFreeConnector(connector); +err2: + drmModeFreeResources(resources); +err1: + return false; +} + +/** + * kmstest_free_connector_config: + * @config: connector configuration structure + * + * Free any resources in @config allocated in kmstest_get_connector_config(). + */ +void kmstest_free_connector_config(struct kmstest_connector_config *config) +{ + drmModeFreeCrtc(config->crtc); + drmModeFreeEncoder(config->encoder); + drmModeFreeConnector(config->connector); +} + +/** + * kmstest_set_connector_dpms: + * @fd: DRM fd + * @connector: libdrm connector + * @mode: DRM DPMS value + * + * This function sets the DPMS setting of @connector to @mode. + */ +void kmstest_set_connector_dpms(int fd, drmModeConnector *connector, int mode) +{ + int i, dpms = 0; + bool found_it = false; + + for (i = 0; i < connector->count_props; i++) { + struct drm_mode_get_property prop; + + prop.prop_id = connector->props[i]; + prop.count_values = 0; + prop.count_enum_blobs = 0; + if (drmIoctl(fd, DRM_IOCTL_MODE_GETPROPERTY, &prop)) + continue; + + if (strcmp(prop.name, "DPMS")) + continue; + + dpms = prop.prop_id; + found_it = true; + break; + } + igt_assert_f(found_it, "DPMS property not found on %d\n", + connector->connector_id); + + igt_assert(drmModeConnectorSetProperty(fd, connector->connector_id, + dpms, mode) == 0); +} + +/** + * kmstest_get_property: + * @drm_fd: drm file descriptor + * @object_id: object whose properties we're going to get + * @object_type: type of obj_id (DRM_MODE_OBJECT_*) + * @name: name of the property we're going to get + * @prop_id: if not NULL, returns the property id + * @value: if not NULL, returns the property value + * @prop: if not NULL, returns the property, and the caller will have to free + * it manually. + * + * Finds a property with the given name on the given object. + * + * Returns: true in case we found something. + */ +bool +kmstest_get_property(int drm_fd, uint32_t object_id, uint32_t object_type, + const char *name, uint32_t *prop_id /* out */, + uint64_t *value /* out */, + drmModePropertyPtr *prop /* out */) +{ + drmModeObjectPropertiesPtr proplist; + drmModePropertyPtr _prop; + bool found = false; + int i; + + proplist = drmModeObjectGetProperties(drm_fd, object_id, object_type); + for (i = 0; i < proplist->count_props; i++) { + _prop = drmModeGetProperty(drm_fd, proplist->props[i]); + if (!_prop) + continue; + + if (strcmp(_prop->name, name) == 0) { + found = true; + if (prop_id) + *prop_id = proplist->props[i]; + if (value) + *value = proplist->prop_values[i]; + if (prop) + *prop = _prop; + else + drmModeFreeProperty(_prop); + + break; + } + drmModeFreeProperty(_prop); + } + + drmModeFreeObjectProperties(proplist); + return found; +} + +/** + * kmstest_edid_add_3d: + * @edid: an existing valid edid block + * @length: length of @edid + * @new_edid_ptr: pointer to where the new edid will be placed + * @new_length: pointer to the size of the new edid + * + * Makes a copy of an existing edid block and adds an extension indicating + * stereo 3D capabilities. + */ +void kmstest_edid_add_3d(const unsigned char *edid, size_t length, + unsigned char *new_edid_ptr[], size_t *new_length) +{ + unsigned char *new_edid; + int n_extensions; + char sum = 0; + int pos; + int i; + char cea_header_len = 4, video_block_len = 6, vsdb_block_len = 11; + + igt_assert(new_edid_ptr != NULL && new_length != NULL); + + *new_length = length + 128; + + new_edid = calloc(*new_length, sizeof(char)); + memcpy(new_edid, edid, length); + *new_edid_ptr = new_edid; + + n_extensions = new_edid[126]; + n_extensions++; + new_edid[126] = n_extensions; + + /* recompute checksum */ + for (i = 0; i < 127; i++) { + sum = sum + new_edid[i]; + } + new_edid[127] = 256 - sum; + + /* add a cea-861 extension block */ + pos = length; + new_edid[pos++] = 0x2; + new_edid[pos++] = 0x3; + new_edid[pos++] = cea_header_len + video_block_len + vsdb_block_len; + new_edid[pos++] = 0x0; + + /* video block (id | length) */ + new_edid[pos++] = 2 << 5 | (video_block_len - 1); + new_edid[pos++] = 32 | 0x80; /* 1080p @ 24Hz | (native)*/ + new_edid[pos++] = 5; /* 1080i @ 60Hz */ + new_edid[pos++] = 20; /* 1080i @ 50Hz */ + new_edid[pos++] = 4; /* 720p @ 60Hz*/ + new_edid[pos++] = 19; /* 720p @ 50Hz*/ + + /* vsdb block ( id | length ) */ + new_edid[pos++] = 3 << 5 | (vsdb_block_len - 1); + /* registration id */ + new_edid[pos++] = 0x3; + new_edid[pos++] = 0xc; + new_edid[pos++] = 0x0; + /* source physical address */ + new_edid[pos++] = 0x10; + new_edid[pos++] = 0x00; + /* Supports_AI ... etc */ + new_edid[pos++] = 0x00; + /* Max TMDS Clock */ + new_edid[pos++] = 0x00; + /* Latency present, HDMI Video Present */ + new_edid[pos++] = 0x20; + /* HDMI Video */ + new_edid[pos++] = 0x80; + new_edid[pos++] = 0x00; + + /* checksum */ + sum = 0; + for (i = 0; i < 127; i++) { + sum = sum + new_edid[length + i]; + } + new_edid[length + 127] = 256 - sum; +} + +/** + * kmstest_unset_all_crtcs: + * @drm_fd: the DRM fd + * @resources: libdrm resources pointer + * + * Disables all the screens. + */ +void kmstest_unset_all_crtcs(int drm_fd, drmModeResPtr resources) +{ + int i, rc; + + for (i = 0; i < resources->count_crtcs; i++) { + rc = drmModeSetCrtc(drm_fd, resources->crtcs[i], -1, 0, 0, NULL, + 0, NULL); + igt_assert(rc == 0); + } +} + +/* + * A small modeset API + */ + +#define LOG_SPACES " " +#define LOG_N_SPACES (sizeof(LOG_SPACES) - 1) + +#define LOG_INDENT(d, section) \ + do { \ + igt_display_log(d, "%s {\n", section); \ + igt_display_log_shift(d, 1); \ + } while (0) +#define LOG_UNINDENT(d) \ + do { \ + igt_display_log_shift(d, -1); \ + igt_display_log(d, "}\n"); \ + } while (0) +#define LOG(d, fmt, ...) igt_display_log(d, fmt, ## __VA_ARGS__) + +static void __attribute__((format(printf, 2, 3))) +igt_display_log(igt_display_t *display, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + igt_debug("display: "); + for (i = 0; i < display->log_shift; i++) + igt_debug("%s", LOG_SPACES); + igt_vlog(IGT_LOG_DOMAIN, IGT_LOG_DEBUG, fmt, args); + va_end(args); +} + +static void igt_display_log_shift(igt_display_t *display, int shift) +{ + display->log_shift += shift; + igt_assert(display->log_shift >= 0); +} + +static void igt_output_refresh(igt_output_t *output) +{ + igt_display_t *display = output->display; + bool ret; + unsigned long crtc_idx_mask; + + /* we mask out the pipes already in use */ + crtc_idx_mask = output->pending_crtc_idx_mask & ~display->pipes_in_use; + + if (output->valid) + kmstest_free_connector_config(&output->config); + + ret = kmstest_get_connector_config(display->drm_fd, + output->id, + crtc_idx_mask, + &output->config); + if (ret) + output->valid = true; + else + output->valid = false; + + if (!output->valid) + return; + + if (!output->name) { + drmModeConnector *c = output->config.connector; + + asprintf(&output->name, "%s-%d", + kmstest_connector_type_str(c->connector_type), + c->connector_type_id); + } + + LOG(display, "%s: Selecting pipe %s\n", output->name, + kmstest_pipe_name(output->config.pipe)); + + display->pipes_in_use |= 1 << output->config.pipe; +} + +static bool +get_plane_property(int drm_fd, uint32_t plane_id, const char *name, + uint32_t *prop_id /* out */, uint64_t *value /* out */, + drmModePropertyPtr *prop /* out */) +{ + return kmstest_get_property(drm_fd, plane_id, DRM_MODE_OBJECT_PLANE, + name, prop_id, value, prop); +} + +static void +igt_plane_set_property(igt_plane_t *plane, uint32_t prop_id, uint64_t value) +{ + igt_pipe_t *pipe = plane->pipe; + igt_display_t *display = pipe->display; + + drmModeObjectSetProperty(display->drm_fd, plane->drm_plane->plane_id, + DRM_MODE_OBJECT_PLANE, prop_id, value); +} + +/* + * Walk a plane's property list to determine its type. If we don't + * find a type property, then the kernel doesn't support universal + * planes and we know the plane is an overlay/sprite. + */ +static int get_drm_plane_type(int drm_fd, uint32_t plane_id) +{ + uint64_t value; + bool has_prop; + + has_prop = get_plane_property(drm_fd, plane_id, "type", + NULL /* prop_id */, &value, NULL); + if (has_prop) + return (int)value; + + return DRM_PLANE_TYPE_OVERLAY; +} + +void igt_display_init(igt_display_t *display, int drm_fd) +{ + drmModeRes *resources; + drmModePlaneRes *plane_resources; + int i; + + memset(display, 0, sizeof(igt_display_t)); + + LOG_INDENT(display, "init"); + + display->drm_fd = drm_fd; + + resources = drmModeGetResources(display->drm_fd); + igt_assert(resources); + + /* + * We cache the number of pipes, that number is a physical limit of the + * hardware and cannot change of time (for now, at least). + */ + display->n_pipes = resources->count_crtcs; + + drmSetClientCap(drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); + plane_resources = drmModeGetPlaneResources(display->drm_fd); + igt_assert(plane_resources); + + for (i = 0; i < display->n_pipes; i++) { + igt_pipe_t *pipe = &display->pipes[i]; + igt_plane_t *plane; + int p = IGT_PLANE_2; + int j, type; + + pipe->display = display; + pipe->pipe = i; + + /* add the planes that can be used with that pipe */ + for (j = 0; j < plane_resources->count_planes; j++) { + drmModePlane *drm_plane; + uint64_t prop_value; + + drm_plane = drmModeGetPlane(display->drm_fd, + plane_resources->planes[j]); + igt_assert(drm_plane); + + if (!(drm_plane->possible_crtcs & (1 << i))) { + drmModeFreePlane(drm_plane); + continue; + } + + type = get_drm_plane_type(display->drm_fd, + plane_resources->planes[j]); + switch (type) { + case DRM_PLANE_TYPE_PRIMARY: + plane = &pipe->planes[IGT_PLANE_PRIMARY]; + plane->is_primary = 1; + plane->index = IGT_PLANE_PRIMARY; + display->has_universal_planes = 1; + break; + case DRM_PLANE_TYPE_CURSOR: + /* + * Cursor should be the highest index in our + * internal list, but we don't know what that + * is yet. Just stick it in the last slot + * for now and we'll move it later, if + * necessary. + */ + plane = &pipe->planes[IGT_PLANE_CURSOR]; + plane->is_cursor = 1; + plane->index = IGT_PLANE_CURSOR; + display->has_universal_planes = 1; + break; + default: + plane = &pipe->planes[p]; + plane->index = p++; + break; + } + + plane->pipe = pipe; + plane->drm_plane = drm_plane; + + get_plane_property(display->drm_fd, drm_plane->plane_id, + "rotation", + &plane->rotation_property, + &prop_value, + NULL); + plane->rotation = (igt_rotation_t)prop_value; + } + + if (display->has_universal_planes) { + /* + * If we have universal planes, we should have both + * primary and cursor planes setup now. + */ + igt_assert(pipe->planes[IGT_PLANE_PRIMARY].drm_plane && + pipe->planes[IGT_PLANE_CURSOR].drm_plane); + + /* + * Cursor was put in the last slot. If we have 0 or + * only 1 sprite, that's the wrong slot and we need to + * move it down. + */ + if (p != IGT_PLANE_CURSOR) { + pipe->planes[p] = pipe->planes[IGT_PLANE_CURSOR]; + pipe->planes[p].index = p; + memset(&pipe->planes[IGT_PLANE_CURSOR], 0, + sizeof *plane); + } + } else { + /* + * No universal plane support. Add drm_plane-less + * primary and cursor planes. + */ + plane = &pipe->planes[IGT_PLANE_PRIMARY]; + plane->pipe = pipe; + plane->index = IGT_PLANE_PRIMARY; + plane->is_primary = true; + + plane = &pipe->planes[p]; + plane->pipe = pipe; + plane->index = p; + plane->is_cursor = true; + } + + /* planes = 1 primary, (p-1) sprites, 1 cursor */ + pipe->n_planes = p + 1; + + /* make sure we don't overflow the plane array */ + igt_assert(pipe->n_planes <= IGT_MAX_PLANES); + } + + /* + * The number of connectors is set, so we just initialize the outputs + * array in _init(). This may change when we need dynamic connectors + * (say DisplayPort MST). + */ + display->n_outputs = resources->count_connectors; + display->outputs = calloc(display->n_outputs, sizeof(igt_output_t)); + igt_assert(display->outputs); + + for (i = 0; i < display->n_outputs; i++) { + igt_output_t *output = &display->outputs[i]; + + /* + * We're free to select any pipe to drive that output until + * a constraint is set with igt_output_set_pipe(). + */ + output->pending_crtc_idx_mask = -1UL; + output->id = resources->connectors[i]; + output->display = display; + + igt_output_refresh(output); + } + + drmModeFreePlaneResources(plane_resources); + drmModeFreeResources(resources); + + LOG_UNINDENT(display); +} + +int igt_display_get_n_pipes(igt_display_t *display) +{ + return display->n_pipes; +} + +static void igt_pipe_fini(igt_pipe_t *pipe) +{ + int i; + + for (i = 0; i < pipe->n_planes; i++) { + igt_plane_t *plane = &pipe->planes[i]; + + if (plane->drm_plane) { + drmModeFreePlane(plane->drm_plane); + plane->drm_plane = NULL; + } + } +} + +static void igt_output_fini(igt_output_t *output) +{ + if (output->valid) + kmstest_free_connector_config(&output->config); + free(output->name); +} + +void igt_display_fini(igt_display_t *display) +{ + int i; + + for (i = 0; i < display->n_pipes; i++) + igt_pipe_fini(&display->pipes[i]); + + for (i = 0; i < display->n_outputs; i++) + igt_output_fini(&display->outputs[i]); + free(display->outputs); + display->outputs = NULL; +} + +static void igt_display_refresh(igt_display_t *display) +{ + int i, j; + + display->pipes_in_use = 0; + + /* Check that two outputs aren't trying to use the same pipe */ + for (i = 0; i < display->n_outputs; i++) { + igt_output_t *a = &display->outputs[i]; + + if (a->pending_crtc_idx_mask == -1UL) + continue; + + for (j = 0; j < display->n_outputs; j++) { + igt_output_t *b = &display->outputs[j]; + + if (i == j) + continue; + + if (b->pending_crtc_idx_mask == -1UL) + continue; + + igt_assert_f(a->pending_crtc_idx_mask != + b->pending_crtc_idx_mask, + "%s and %s are both trying to use pipe %s\n", + igt_output_name(a), igt_output_name(b), + kmstest_pipe_name(ffs(a->pending_crtc_idx_mask) - 1)); + } + } + + /* + * The pipe allocation has to be done in two phases: + * - first, try to satisfy the outputs where a pipe has been specified + * - then, allocate the outputs with PIPE_ANY + */ + for (i = 0; i < display->n_outputs; i++) { + igt_output_t *output = &display->outputs[i]; + + if (output->pending_crtc_idx_mask == -1UL) + continue; + + igt_output_refresh(output); + } + for (i = 0; i < display->n_outputs; i++) { + igt_output_t *output = &display->outputs[i]; + + if (output->pending_crtc_idx_mask != -1UL) + continue; + + igt_output_refresh(output); + } +} + +static igt_pipe_t *igt_output_get_driving_pipe(igt_output_t *output) +{ + igt_display_t *display = output->display; + enum pipe pipe; + + if (output->pending_crtc_idx_mask == -1UL) { + /* + * The user hasn't specified a pipe to use, take the one + * configured by the last refresh() + */ + pipe = output->config.pipe; + } else { + /* + * Otherwise, return the pending pipe (ie the pipe that should + * drive this output after the commit() + */ + pipe = ffs(output->pending_crtc_idx_mask) - 1; + } + + igt_assert(pipe >= 0 && pipe < display->n_pipes); + + return &display->pipes[pipe]; +} + +static igt_plane_t *igt_pipe_get_plane(igt_pipe_t *pipe, enum igt_plane plane) +{ + int idx; + + /* Cursor plane is always the highest index */ + if (plane == IGT_PLANE_CURSOR) + idx = pipe->n_planes - 1; + else { + igt_assert_f(plane >= 0 && plane < (pipe->n_planes), + "plane=%d\n", plane); + idx = plane; + } + + return &pipe->planes[idx]; +} + +static uint32_t igt_plane_get_fb_id(igt_plane_t *plane) +{ + if (plane->fb) + return plane->fb->fb_id; + else + return 0; +} + +static uint32_t igt_plane_get_fb_gem_handle(igt_plane_t *plane) +{ + if (plane->fb) + return plane->fb->gem_handle; + else + return 0; +} + +#define CHECK_RETURN(r, fail) { \ + if (r && !fail) \ + return r; \ + igt_assert(r == 0); \ +} + +/* + * Commit position and fb changes to a DRM plane via the SetPlane ioctl; if the + * DRM call to program the plane fails, we'll either fail immediately (for + * tests that expect the commit to succeed) or return the failure code (for + * tests that expect a specific error code). + */ +static int igt_drm_plane_commit(igt_plane_t *plane, + igt_output_t *output, + bool fail_on_error) +{ + igt_display_t *display = output->display; + uint32_t fb_id, crtc_id; + int ret; + + igt_assert(plane->drm_plane); + + /* it's an error to try an unsupported feature */ + igt_assert(igt_plane_supports_rotation(plane) || + !plane->rotation_changed); + + fb_id = igt_plane_get_fb_id(plane); + crtc_id = output->config.crtc->crtc_id; + + if (plane->fb_changed && fb_id == 0) { + LOG(display, + "%s: SetPlane pipe %s, plane %d, disabling\n", + igt_output_name(output), + kmstest_pipe_name(output->config.pipe), + plane->index); + + ret = drmModeSetPlane(display->drm_fd, + plane->drm_plane->plane_id, + crtc_id, + fb_id, + 0, /* flags */ + 0, 0, /* crtc_x, crtc_y */ + 0, 0, /* crtc_w, crtc_h */ + IGT_FIXED(0,0), /* src_x */ + IGT_FIXED(0,0), /* src_y */ + IGT_FIXED(0,0), /* src_w */ + IGT_FIXED(0,0) /* src_h */); + + CHECK_RETURN(ret, fail_on_error); + } else if (plane->fb_changed || plane->position_changed) { + LOG(display, + "%s: SetPlane %s.%d, fb %u, position (%d, %d)\n", + igt_output_name(output), + kmstest_pipe_name(output->config.pipe), + plane->index, + fb_id, + plane->crtc_x, plane->crtc_y); + + ret = drmModeSetPlane(display->drm_fd, + plane->drm_plane->plane_id, + crtc_id, + fb_id, + 0, /* flags */ + plane->crtc_x, plane->crtc_y, + plane->crtc_w, plane->crtc_h, + IGT_FIXED(0,0), /* src_x */ + IGT_FIXED(0,0), /* src_y */ + IGT_FIXED(plane->fb->width,0), /* src_w */ + IGT_FIXED(plane->fb->height,0) /* src_h */); + + CHECK_RETURN(ret, fail_on_error); + } + + plane->fb_changed = false; + plane->position_changed = false; + + if (plane->rotation_changed) { + igt_plane_set_property(plane, plane->rotation_property, + plane->rotation); + + plane->rotation_changed = false; + } + + return 0; +} + +/* + * Commit position and fb changes to a cursor via legacy ioctl's. If commit + * fails, we'll either fail immediately (for tests that expect the commit to + * succeed) or return the failure code (for tests that expect a specific error + * code). + */ +static int igt_cursor_commit_legacy(igt_plane_t *cursor, + igt_output_t *output, + bool fail_on_error) +{ + igt_display_t *display = output->display; + uint32_t crtc_id = output->config.crtc->crtc_id; + int ret; + + if (cursor->fb_changed) { + uint32_t gem_handle = igt_plane_get_fb_gem_handle(cursor); + + if (gem_handle) { + LOG(display, + "%s: SetCursor pipe %s, fb %u %dx%d\n", + igt_output_name(output), + kmstest_pipe_name(output->config.pipe), + gem_handle, + cursor->crtc_w, cursor->crtc_h); + + ret = drmModeSetCursor(display->drm_fd, crtc_id, + gem_handle, + cursor->crtc_w, + cursor->crtc_h); + } else { + LOG(display, + "%s: SetCursor pipe %s, disabling\n", + igt_output_name(output), + kmstest_pipe_name(output->config.pipe)); + + ret = drmModeSetCursor(display->drm_fd, crtc_id, + 0, 0, 0); + } + + CHECK_RETURN(ret, fail_on_error); + + cursor->fb_changed = false; + } + + if (cursor->position_changed) { + int x = cursor->crtc_x; + int y = cursor->crtc_y; + + LOG(display, + "%s: MoveCursor pipe %s, (%d, %d)\n", + igt_output_name(output), + kmstest_pipe_name(output->config.pipe), + x, y); + + ret = drmModeMoveCursor(display->drm_fd, crtc_id, x, y); + CHECK_RETURN(ret, fail_on_error); + + cursor->position_changed = false; + } + + return 0; +} + +/* + * Commit position and fb changes to a primary plane via the legacy interface + * (setmode). + */ +static int igt_primary_plane_commit_legacy(igt_plane_t *primary, + igt_output_t *output, + bool fail_on_error) +{ + struct igt_display *display = primary->pipe->display; + drmModeModeInfo *mode; + uint32_t fb_id, crtc_id; + int ret; + + /* Primary planes can't be windowed when using a legacy commit */ + igt_assert((primary->crtc_x == 0 && primary->crtc_y == 0)); + + /* nor rotated */ + igt_assert(!primary->rotation_changed); + + if (!primary->fb_changed && !primary->position_changed && + !primary->panning_changed) + return 0; + + crtc_id = output->config.crtc->crtc_id; + fb_id = igt_plane_get_fb_id(primary); + if (fb_id) + mode = igt_output_get_mode(output); + else + mode = NULL; + + if (fb_id) { + LOG(display, + "%s: SetCrtc pipe %s, fb %u, panning (%d, %d), " + "mode %dx%d\n", + igt_output_name(output), + kmstest_pipe_name(output->config.pipe), + fb_id, + primary->pan_x, primary->pan_y, + mode->hdisplay, mode->vdisplay); + + ret = drmModeSetCrtc(display->drm_fd, + crtc_id, + fb_id, + primary->pan_x, primary->pan_y, + &output->id, + 1, + mode); + } else { + LOG(display, + "%s: SetCrtc pipe %s, disabling\n", + igt_output_name(output), + kmstest_pipe_name(output->config.pipe)); + + ret = drmModeSetCrtc(display->drm_fd, + crtc_id, + fb_id, + 0, 0, /* x, y */ + NULL, /* connectors */ + 0, /* n_connectors */ + NULL /* mode */); + } + + CHECK_RETURN(ret, fail_on_error); + + primary->pipe->enabled = (fb_id != 0); + primary->fb_changed = false; + primary->position_changed = false; + primary->panning_changed = false; + + return 0; +} + + +/* + * Commit position and fb changes to a plane. The value of @s will determine + * which API is used to do the programming. + */ +static int igt_plane_commit(igt_plane_t *plane, + igt_output_t *output, + enum igt_commit_style s, + bool fail_on_error) +{ + if (plane->is_cursor && s == COMMIT_LEGACY) { + return igt_cursor_commit_legacy(plane, output, fail_on_error); + } else if (plane->is_primary && s == COMMIT_LEGACY) { + return igt_primary_plane_commit_legacy(plane, output, + fail_on_error); + } else { + return igt_drm_plane_commit(plane, output, fail_on_error); + } +} + +/* + * Commit all plane changes to an output. Note that if @s is COMMIT_LEGACY, + * enabling/disabling the primary plane will also enable/disable the CRTC. + * + * If @fail_on_error is true, any failure to commit plane state will lead + * to subtest failure in the specific function where the failure occurs. + * Otherwise, the first error code encountered will be returned and no + * further programming will take place, which may result in some changes + * taking effect and others not taking effect. + */ +static int igt_output_commit(igt_output_t *output, + enum igt_commit_style s, + bool fail_on_error) +{ + igt_display_t *display = output->display; + igt_pipe_t *pipe; + int i; + int ret; + bool need_wait_for_vblank = false; + + pipe = igt_output_get_driving_pipe(output); + + for (i = 0; i < pipe->n_planes; i++) { + igt_plane_t *plane = &pipe->planes[i]; + + if (plane->fb_changed || plane->position_changed) + need_wait_for_vblank = true; + + ret = igt_plane_commit(plane, output, s, fail_on_error); + CHECK_RETURN(ret, fail_on_error); + } + + /* + * If the crtc is enabled, wait until the next vblank before returning + * if we made changes to any of the planes. + */ + if (need_wait_for_vblank && pipe->enabled) { + igt_wait_for_vblank(display->drm_fd, pipe->pipe); + } + + return 0; +} + +/* + * Commit all plane changes across all outputs of the display. + * + * If @fail_on_error is true, any failure to commit plane state will lead + * to subtest failure in the specific function where the failure occurs. + * Otherwise, the first error code encountered will be returned and no + * further programming will take place, which may result in some changes + * taking effect and others not taking effect. + */ +static int do_display_commit(igt_display_t *display, + enum igt_commit_style s, + bool fail_on_error) +{ + int i, ret; + + LOG_INDENT(display, "commit"); + + igt_display_refresh(display); + + for (i = 0; i < display->n_outputs; i++) { + igt_output_t *output = &display->outputs[i]; + + if (!output->valid) + continue; + + ret = igt_output_commit(output, s, fail_on_error); + CHECK_RETURN(ret, fail_on_error); + } + + LOG_UNINDENT(display); + + igt_debug_wait_for_keypress("modeset"); + + return 0; +} + +/** + * igt_display_commit2: + * @display: DRM device handle + * @s: Commit style + * + * Commits framebuffer and positioning changes to all planes of each display + * pipe, using a specific API to perform the programming. This function should + * be used to exercise a specific driver programming API; igt_display_commit + * should be used instead if the API used is unimportant to the test being run. + * + * This function should only be used to commit changes that are expected to + * succeed, since any failure during the commit process will cause the IGT + * subtest to fail. To commit changes that are expected to fail, use + * @igt_try_display_commit2 instead. + * + * Returns: 0 upon success. This function will never return upon failure + * since igt_fail() at lower levels will longjmp out of it. + */ +int igt_display_commit2(igt_display_t *display, + enum igt_commit_style s) +{ + do_display_commit(display, s, true); + + return 0; +} + +/** + * igt_display_try_commit2: + * @display: DRM device handle + * @s: Commit style + * + * Attempts to commit framebuffer and positioning changes to all planes of each + * display pipe. This function should be used to commit changes that are + * expected to fail, so that the error code can be checked for correctness. + * For changes that are expected to succeed, use @igt_display_commit instead. + * + * Note that in non-atomic commit styles, no display programming will be + * performed after the first failure is encountered, so only some of the + * operations requested by a test may have been completed. Tests that catch + * errors returned by this function should take care to restore the display to + * a sane state after a failure is detected. + * + * Returns: 0 upon success, otherwise the error code of the first error + * encountered. + */ +int igt_display_try_commit2(igt_display_t *display, enum igt_commit_style s) +{ + return do_display_commit(display, s, false); +} + +/** + * igt_display_commit: + * @display: DRM device handle + * + * Commits framebuffer and positioning changes to all planes of each display + * pipe. + * + * Returns: 0 upon success. This function will never return upon failure + * since igt_fail() at lower levels will longjmp out of it. + */ +int igt_display_commit(igt_display_t *display) +{ + return igt_display_commit2(display, COMMIT_LEGACY); +} + +const char *igt_output_name(igt_output_t *output) +{ + return output->name; +} + +drmModeModeInfo *igt_output_get_mode(igt_output_t *output) +{ + return &output->config.default_mode; +} + +void igt_output_set_pipe(igt_output_t *output, enum pipe pipe) +{ + igt_display_t *display = output->display; + + if (pipe == PIPE_ANY) { + LOG(display, "%s: set_pipe(any)\n", igt_output_name(output)); + output->pending_crtc_idx_mask = -1UL; + } else { + LOG(display, "%s: set_pipe(%s)\n", igt_output_name(output), + kmstest_pipe_name(pipe)); + output->pending_crtc_idx_mask = 1 << pipe; + } +} + +igt_plane_t *igt_output_get_plane(igt_output_t *output, enum igt_plane plane) +{ + igt_pipe_t *pipe; + + pipe = igt_output_get_driving_pipe(output); + return igt_pipe_get_plane(pipe, plane); +} + +void igt_plane_set_fb(igt_plane_t *plane, struct igt_fb *fb) +{ + igt_pipe_t *pipe = plane->pipe; + igt_display_t *display = pipe->display; + + LOG(display, "%s.%d: plane_set_fb(%d)\n", kmstest_pipe_name(pipe->pipe), + plane->index, fb ? fb->fb_id : 0); + + plane->fb = fb; + /* hack to keep tests working that don't call igt_plane_set_size() */ + if (fb) { + plane->crtc_w = fb->width; + plane->crtc_h = fb->height; + } else { + plane->crtc_w = 0; + plane->crtc_h = 0; + } + + plane->fb_changed = true; +} + +void igt_plane_set_position(igt_plane_t *plane, int x, int y) +{ + igt_pipe_t *pipe = plane->pipe; + igt_display_t *display = pipe->display; + + LOG(display, "%s.%d: plane_set_position(%d,%d)\n", + kmstest_pipe_name(pipe->pipe), plane->index, x, y); + + plane->crtc_x = x; + plane->crtc_y = y; + + plane->position_changed = true; +} + +void igt_plane_set_size(igt_plane_t *plane, int w, int h) +{ + igt_pipe_t *pipe = plane->pipe; + igt_display_t *display = pipe->display; + + LOG(display, "%s.%d: plane_set_size(%d,%d)\n", + kmstest_pipe_name(pipe->pipe), plane->index, w, h); + + plane->crtc_w = w; + plane->crtc_h = h; + + /* + * must be fb_changed so that legacy cursors call + * drmModeSetCursor() instead of drmModeMoveCursor() + */ + plane->fb_changed = true; +} + +void igt_plane_set_panning(igt_plane_t *plane, int x, int y) +{ + igt_pipe_t *pipe = plane->pipe; + igt_display_t *display = pipe->display; + + LOG(display, "%s.%d: plane_set_panning(%d,%d)\n", + kmstest_pipe_name(pipe->pipe), + plane->index, x, y); + + plane->pan_x = x; + plane->pan_y = y; + + plane->panning_changed = true; +} + +static const char *rotation_name(igt_rotation_t rotation) +{ + switch (rotation) { + case IGT_ROTATION_0: + return "0°"; + case IGT_ROTATION_90: + return "90°"; + case IGT_ROTATION_180: + return "180°"; + case IGT_ROTATION_270: + return "270°"; + default: + igt_assert(0); + } +} + +void igt_plane_set_rotation(igt_plane_t *plane, igt_rotation_t rotation) +{ + igt_pipe_t *pipe = plane->pipe; + igt_display_t *display = pipe->display; + + LOG(display, "%s.%d: plane_set_rotation(%s)\n", + kmstest_pipe_name(pipe->pipe), + plane->index, rotation_name(rotation)); + + plane->rotation = rotation; + + plane->rotation_changed = true; +} + +void igt_wait_for_vblank(int drm_fd, enum pipe pipe) +{ + drmVBlank wait_vbl; + + memset(&wait_vbl, 0, sizeof(wait_vbl)); + + wait_vbl.request.type = pipe << DRM_VBLANK_HIGH_CRTC_SHIFT | + DRM_VBLANK_RELATIVE; + wait_vbl.request.sequence = 1; + + igt_assert(drmWaitVBlank(drm_fd, &wait_vbl) == 0); +} + +/** + * igt_enable_connectors: + * + * Force connectors to be enabled where this is known to work well. Use + * #igt_reset_connectors to revert the changes. + * + * An exit handler is installed to ensure connectors are reset when the test + * exits. + */ +void igt_enable_connectors(void) +{ + drmModeRes *res; + drmModeConnector *c; + int drm_fd; + + drm_fd = drm_open_any(); + + res = drmModeGetResources(drm_fd); + + for (int i = 0; i < res->count_connectors; i++) { + + c = drmModeGetConnector(drm_fd, res->connectors[i]); + + /* don't attempt to force connectors that are already connected + */ + if (c->connection == DRM_MODE_CONNECTED) + continue; + + /* just enable VGA for now */ + if (c->connector_type == DRM_MODE_CONNECTOR_VGA) { + if (!kmstest_force_connector(drm_fd, c, FORCE_CONNECTOR_ON)) + igt_info("Unable to force state on %s-%d\n", + kmstest_connector_type_str(c->connector_type), + c->connector_type_id); + } + + drmModeFreeConnector(c); + } + close(drm_fd); +} + +/** + * igt_reset_connectors: + * + * Remove any forced state from the connectors. + */ +void igt_reset_connectors(void) +{ + char **tmp; + + /* reset the connectors stored in forced_connectors, avoiding any + * functions that are not safe to call in signal handlers */ + + for (tmp = forced_connectors; *tmp; tmp++) { + int fd = igt_debugfs_open(*tmp, O_WRONLY | O_TRUNC); + igt_assert(write(fd, "unspecified", 11) == 11); + close(fd); + } +} diff --git a/lib/igt_kms.h b/lib/igt_kms.h new file mode 100644 index 0000000..a1483a4 --- /dev/null +++ b/lib/igt_kms.h @@ -0,0 +1,280 @@ +/* + * Copyright © 2013 Intel Corporation + * + * 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: + * Daniel Vetter + * Damien Lespiau + */ + +#ifndef __IGT_KMS_H__ +#define __IGT_KMS_H__ + +#include +#include +#include + +#include + +#include "igt_fb.h" + +/* Low-level helpers with kmstest_ prefix */ + +enum pipe { + PIPE_A = 0, + PIPE_B, + PIPE_C, + I915_MAX_PIPES +}; +const char *kmstest_pipe_name(enum pipe pipe); + +/* We namespace this enum to not conflict with the Android i915_drm.h */ +enum igt_plane { + IGT_PLANE_1 = 0, + IGT_PLANE_PRIMARY = IGT_PLANE_1, + IGT_PLANE_2, + IGT_PLANE_3, + IGT_PLANE_CURSOR, +}; + +const char *kmstest_plane_name(enum igt_plane plane); + +enum port { + PORT_A = 0, + PORT_B, + PORT_C, + PORT_D, + PORT_E, + I915_MAX_PORTS +}; + +/** + * kmstest_port_name: + * @port: display plane + * + * Returns: String representing @port, e.g. "A". + */ +#define kmstest_port_name(port) ((port) + 'A') + +/** + * kmstest_encoder_type_str: + * @type: DRM_MODE_ENCODER_* enumeration value + * + * Returns: A string representing the drm encoder @type. + */ +const char *kmstest_encoder_type_str(int type); + +/** + * kmstest_connector_status_str: + * @status: DRM_MODE_* connector status value + * + * Returns: A string representing the drm connector status @status. + */ +const char *kmstest_connector_status_str(int status); + +/** + * kmstest_connector_type_str: + * @type: DRM_MODE_CONNECTOR_* enumeration value + * + * Returns: A string representing the drm connector @type. + */ +const char *kmstest_connector_type_str(int type); + +void kmstest_dump_mode(drmModeModeInfo *mode); + +int kmstest_get_pipe_from_crtc_id(int fd, int crtc_id); +void kmstest_set_vt_graphics_mode(void); +void kmstest_restore_vt_mode(void); + +struct kmstest_connector_config { + drmModeCrtc *crtc; + drmModeConnector *connector; + drmModeEncoder *encoder; + drmModeModeInfo default_mode; + int crtc_idx; + int pipe; +}; + +/** + * kmstest_force_connector_state: + * @FORCE_CONNECTOR_UNSPECIFIED: Unspecified + * @FORCE_CONNECTOR_ON: On + * @FORCE_CONNECTOR_DIGITAL: Digital + * @FORCE_CONNECTOR_OFF: Off + */ +enum kmstest_force_connector_state { + FORCE_CONNECTOR_UNSPECIFIED, + FORCE_CONNECTOR_ON, + FORCE_CONNECTOR_DIGITAL, + FORCE_CONNECTOR_OFF +}; + +bool kmstest_force_connector(int fd, drmModeConnector *connector, + enum kmstest_force_connector_state state); +void kmstest_edid_add_3d(const unsigned char *edid, size_t length, unsigned char *new_edid_ptr[], size_t *new_length); +void kmstest_force_edid(int drm_fd, drmModeConnector *connector, + const unsigned char *edid, size_t length); + +bool kmstest_get_connector_default_mode(int drm_fd, drmModeConnector *connector, + drmModeModeInfo *mode); +bool kmstest_get_connector_config(int drm_fd, uint32_t connector_id, + unsigned long crtc_idx_mask, + struct kmstest_connector_config *config); +void kmstest_free_connector_config(struct kmstest_connector_config *config); + +void kmstest_set_connector_dpms(int fd, drmModeConnector *connector, int mode); +bool kmstest_get_property(int drm_fd, uint32_t object_id, uint32_t object_type, + const char *name, uint32_t *prop_id, uint64_t *value, + drmModePropertyPtr *prop); +void kmstest_unset_all_crtcs(int drm_fd, drmModeResPtr resources); + +/* + * A small modeset API + */ + +/* High-level kms api with igt_ prefix */ +enum igt_commit_style { + COMMIT_LEGACY = 0, + COMMIT_UNIVERSAL, + /* We'll add atomic here eventually. */ +}; + +typedef struct igt_display igt_display_t; +typedef struct igt_pipe igt_pipe_t; +typedef uint32_t igt_fixed_t; /* 16.16 fixed point */ + +typedef enum { + /* this maps to the kernel API */ + IGT_ROTATION_0 = 1 << 0, + IGT_ROTATION_90 = 1 << 1, + IGT_ROTATION_180 = 1 << 2, + IGT_ROTATION_270 = 1 << 3, +} igt_rotation_t; + +typedef struct { + /*< private >*/ + igt_pipe_t *pipe; + int index; + /* capabilities */ + unsigned int is_primary : 1; + unsigned int is_cursor : 1; + /* state tracking */ + unsigned int fb_changed : 1; + unsigned int position_changed : 1; + unsigned int panning_changed : 1; + unsigned int rotation_changed : 1; + /* + * drm_plane can be NULL for primary and cursor planes (when not + * using the atomic modeset API) + */ + drmModePlane *drm_plane; + struct igt_fb *fb; + + uint32_t rotation_property; + + /* position within pipe_src_w x pipe_src_h */ + int crtc_x, crtc_y; + /* size within pipe_src_w x pipe_src_h */ + int crtc_w, crtc_h; + /* panning offset within the fb */ + unsigned int pan_x, pan_y; + igt_rotation_t rotation; +} igt_plane_t; + +struct igt_pipe { + igt_display_t *display; + enum pipe pipe; + bool enabled; +#define IGT_MAX_PLANES 4 + int n_planes; + igt_plane_t planes[IGT_MAX_PLANES]; +}; + +typedef struct { + /*< private >*/ + igt_display_t *display; + uint32_t id; /* KMS id */ + struct kmstest_connector_config config; + char *name; + bool valid; + unsigned long pending_crtc_idx_mask; +} igt_output_t; + +struct igt_display { + int drm_fd; + int log_shift; + int n_pipes; + int n_outputs; + unsigned long pipes_in_use; + igt_output_t *outputs; + igt_pipe_t pipes[I915_MAX_PIPES]; + bool has_universal_planes; +}; + +void igt_display_init(igt_display_t *display, int drm_fd); +void igt_display_fini(igt_display_t *display); +int igt_display_commit2(igt_display_t *display, enum igt_commit_style s); +int igt_display_commit(igt_display_t *display); +int igt_display_try_commit2(igt_display_t *display, enum igt_commit_style s); +int igt_display_get_n_pipes(igt_display_t *display); + +const char *igt_output_name(igt_output_t *output); +drmModeModeInfo *igt_output_get_mode(igt_output_t *output); +void igt_output_set_pipe(igt_output_t *output, enum pipe pipe); +igt_plane_t *igt_output_get_plane(igt_output_t *output, enum igt_plane plane); + +static inline bool igt_plane_supports_rotation(igt_plane_t *plane) +{ + return plane->rotation_property != 0; +} + +void igt_plane_set_fb(igt_plane_t *plane, struct igt_fb *fb); +void igt_plane_set_position(igt_plane_t *plane, int x, int y); +void igt_plane_set_size(igt_plane_t *plane, int w, int h); +void igt_plane_set_panning(igt_plane_t *plane, int x, int y); +void igt_plane_set_rotation(igt_plane_t *plane, igt_rotation_t rotation); + +void igt_wait_for_vblank(int drm_fd, enum pipe pipe); + +#define for_each_connected_output(display, output) \ + for (int i__ = 0; i__ < (display)->n_outputs; i__++) \ + if ((output = &(display)->outputs[i__]), output->valid) + +#define for_each_pipe(display, pipe) \ + for (pipe = 0; pipe < igt_display_get_n_pipes(display); pipe++) \ + +/* + * Can be used with igt_output_set_pipe() to mean we don't care about the pipe + * that should drive this output + */ +#define PIPE_ANY (-1) + +#define IGT_FIXED(i,f) ((i) << 16 | (f)) + +void igt_enable_connectors(void); +void igt_reset_connectors(void); + +#define EDID_LENGTH 128 +const unsigned char* igt_kms_get_base_edid(void); + + +#endif /* __IGT_KMS_H__ */ + diff --git a/lib/intel_batchbuffer.c b/lib/intel_batchbuffer.c new file mode 100644 index 0000000..c70f6d8 --- /dev/null +++ b/lib/intel_batchbuffer.c @@ -0,0 +1,532 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas. + * 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, 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 TUNGSTEN GRAPHICS AND/OR ITS 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. + * + **************************************************************************/ + +#include +#include +#include +#include +#include + +#include "drm.h" +#include "drmtest.h" +#include "intel_batchbuffer.h" +#include "intel_bufmgr.h" +#include "intel_chipset.h" +#include "intel_reg.h" +#include "rendercopy.h" +#include "media_fill.h" +#include + +/** + * SECTION:intel_batchbuffer + * @short_description: Batchbuffer and blitter support + * @title: intel batchbuffer + * @include: intel_batchbuffer.h + * + * This library provides some basic support for batchbuffers and using the + * blitter engine based upon libdrm. A new batchbuffer is allocated with + * intel_batchbuffer_alloc() and for simple blitter commands submitted with + * intel_batchbuffer_flush(). + * + * It also provides some convenient macros to easily emit commands into + * batchbuffers. All those macros presume that a pointer to a #intel_batchbuffer + * structure called batch is in scope. The basic macros are #BEGIN_BATCH, + * #OUT_BATCH, #OUT_RELOC and #ADVANCE_BATCH. + * + * Note that this library's header pulls in the [i-g-t core](intel-gpu-tools-i-g-t-core.html) + * library as a dependency. + */ + +/** + * intel_batchbuffer_reset: + * @batch: batchbuffer object + * + * Resets @batch by allocating a new gem buffer object as backing storage. + */ +void +intel_batchbuffer_reset(struct intel_batchbuffer *batch) +{ + if (batch->bo != NULL) { + drm_intel_bo_unreference(batch->bo); + batch->bo = NULL; + } + + batch->bo = drm_intel_bo_alloc(batch->bufmgr, "batchbuffer", + BATCH_SZ, 4096); + + memset(batch->buffer, 0, sizeof(batch->buffer)); + batch->ctx = NULL; + + batch->ptr = batch->buffer; + batch->end = NULL; +} + +/** + * intel_batchbuffer_alloc: + * @bufmgr: libdrm buffer manager + * @devid: pci device id of the drm device + * + * Allocates a new batchbuffer object. @devid must be supplied since libdrm + * doesn't expose it directly. + * + * Returns: The allocated and initialized batchbuffer object. + */ +struct intel_batchbuffer * +intel_batchbuffer_alloc(drm_intel_bufmgr *bufmgr, uint32_t devid) +{ + struct intel_batchbuffer *batch = calloc(sizeof(*batch), 1); + + batch->bufmgr = bufmgr; + batch->devid = devid; + batch->gen = intel_gen(devid); + intel_batchbuffer_reset(batch); + + return batch; +} + +/** + * intel_batchbuffer_free: + * @batch: batchbuffer object + * + * Releases all resource of the batchbuffer object @batch. + */ +void +intel_batchbuffer_free(struct intel_batchbuffer *batch) +{ + drm_intel_bo_unreference(batch->bo); + batch->bo = NULL; + free(batch); +} + +#define CMD_POLY_STIPPLE_OFFSET 0x7906 + +static unsigned int +flush_on_ring_common(struct intel_batchbuffer *batch, int ring) +{ + unsigned int used = batch->ptr - batch->buffer; + + if (used == 0) + return 0; + + if (IS_GEN5(batch->devid)) { + /* emit gen5 w/a without batch space checks - we reserve that + * already. */ + *(uint32_t *) (batch->ptr) = CMD_POLY_STIPPLE_OFFSET << 16; + batch->ptr += 4; + *(uint32_t *) (batch->ptr) = 0; + batch->ptr += 4; + } + + /* Round batchbuffer usage to 2 DWORDs. */ + if ((used & 4) == 0) { + *(uint32_t *) (batch->ptr) = 0; /* noop */ + batch->ptr += 4; + } + + /* Mark the end of the buffer. */ + *(uint32_t *)(batch->ptr) = MI_BATCH_BUFFER_END; /* noop */ + batch->ptr += 4; + return batch->ptr - batch->buffer; +} + +/** + * intel_batchbuffer_flush_on_ring: + * @batch: batchbuffer object + * @ring: execbuf ring flag + * + * Submits the batch for execution on @ring. + */ +void +intel_batchbuffer_flush_on_ring(struct intel_batchbuffer *batch, int ring) +{ + unsigned int used = flush_on_ring_common(batch, ring); + drm_intel_context *ctx; + + if (used == 0) + return; + + do_or_die(drm_intel_bo_subdata(batch->bo, 0, used, batch->buffer)); + + batch->ptr = NULL; + + /* XXX bad kernel API */ + ctx = batch->ctx; + if (ring != I915_EXEC_RENDER) + ctx = NULL; + do_or_die(drm_intel_gem_bo_context_exec(batch->bo, ctx, used, ring)); + + intel_batchbuffer_reset(batch); +} + +void +intel_batchbuffer_set_context(struct intel_batchbuffer *batch, + drm_intel_context *context) +{ + batch->ctx = context; +} + +/** + * intel_batchbuffer_flush_with_context: + * @batch: batchbuffer object + * @context: libdrm hardware context object + * + * Submits the batch for execution on the render engine with the supplied + * hardware context. + */ +void +intel_batchbuffer_flush_with_context(struct intel_batchbuffer *batch, + drm_intel_context *context) +{ + int ret; + unsigned int used = flush_on_ring_common(batch, I915_EXEC_RENDER); + + if (used == 0) + return; + + ret = drm_intel_bo_subdata(batch->bo, 0, used, batch->buffer); + igt_assert(ret == 0); + + batch->ptr = NULL; + + ret = drm_intel_gem_bo_context_exec(batch->bo, context, used, + I915_EXEC_RENDER); + igt_assert(ret == 0); + + intel_batchbuffer_reset(batch); +} + +/** + * intel_batchbuffer_flush: + * @batch: batchbuffer object + * + * Submits the batch for execution on the blitter engine, selecting the right + * ring depending upon the hardware platform. + */ +void +intel_batchbuffer_flush(struct intel_batchbuffer *batch) +{ + int ring = 0; + if (HAS_BLT_RING(batch->devid)) + ring = I915_EXEC_BLT; + intel_batchbuffer_flush_on_ring(batch, ring); +} + + +/** + * intel_batchbuffer_emit_reloc: + * @batch: batchbuffer object + * @buffer: relocation target libdrm buffer object + * @delta: delta value to add to @buffer's gpu address + * @read_domains: gem domain bits for the relocation + * @write_domain: gem domain bit for the relocation + * @fenced: whether this gpu access requires fences + * + * Emits both a libdrm relocation entry pointing at @buffer and the pre-computed + * DWORD of @batch's presumed gpu address plus the supplied @delta into @batch. + * + * Note that @fenced is only relevant if @buffer is actually tiled. + * + * This is the only way buffers get added to the validate list. + */ +void +intel_batchbuffer_emit_reloc(struct intel_batchbuffer *batch, + drm_intel_bo *buffer, uint64_t delta, + uint32_t read_domains, uint32_t write_domain, + int fenced) +{ + uint64_t offset; + int ret; + + if (batch->ptr - batch->buffer > BATCH_SZ) + igt_info("bad relocation ptr %p map %p offset %d size %d\n", + batch->ptr, batch->buffer, + (int)(batch->ptr - batch->buffer), BATCH_SZ); + + if (fenced) + ret = drm_intel_bo_emit_reloc_fence(batch->bo, batch->ptr - batch->buffer, + buffer, delta, + read_domains, write_domain); + else + ret = drm_intel_bo_emit_reloc(batch->bo, batch->ptr - batch->buffer, + buffer, delta, + read_domains, write_domain); + + offset = buffer->offset64; + offset += delta; + intel_batchbuffer_emit_dword(batch, offset); + if (batch->gen >= 8) + intel_batchbuffer_emit_dword(batch, offset >> 32); + igt_assert(ret == 0); +} + +/** + * intel_batchbuffer_data: + * @batch: batchbuffer object + * @data: pointer to the data to write into the batchbuffer + * @bytes: number of bytes to write into the batchbuffer + * + * This transfers the given @data into the batchbuffer. Note that the length + * must be DWORD aligned, i.e. multiples of 32bits. + */ +void +intel_batchbuffer_data(struct intel_batchbuffer *batch, + const void *data, unsigned int bytes) +{ + igt_assert((bytes & 3) == 0); + intel_batchbuffer_require_space(batch, bytes); + memcpy(batch->ptr, data, bytes); + batch->ptr += bytes; +} + +/** + * intel_blt_copy: + * @batch: batchbuffer object + * @src_bo: source libdrm buffer object + * @src_x1: source pixel x-coordination + * @src_y1: source pixel y-coordination + * @src_pitch: @src_bo's pitch in bytes + * @dst_bo: destination libdrm buffer object + * @dst_x1: destination pixel x-coordination + * @dst_y1: destination pixel y-coordination + * @dst_pitch: @dst_bo's pitch in bytes + * @width: width of the copied rectangle + * @height: height of the copied rectangle + * @bpp: bits per pixel + * + * This emits a 2D copy operation using blitter commands into the supplied batch + * buffer object. + */ +void +intel_blt_copy(struct intel_batchbuffer *batch, + drm_intel_bo *src_bo, int src_x1, int src_y1, int src_pitch, + drm_intel_bo *dst_bo, int dst_x1, int dst_y1, int dst_pitch, + int width, int height, int bpp) +{ + const int gen = batch->gen; + uint32_t src_tiling, dst_tiling, swizzle; + uint32_t cmd_bits = 0; + uint32_t br13_bits; + +#define CHECK_RANGE(x) ((x) >= 0 && (x) < (1 << 15)) + igt_assert(CHECK_RANGE(src_x1) && CHECK_RANGE(src_y1) && + CHECK_RANGE(dst_x1) && CHECK_RANGE(dst_y1) && + CHECK_RANGE(width) && CHECK_RANGE(height) && + CHECK_RANGE(src_x1 + width) && CHECK_RANGE(src_y1 + height) + && CHECK_RANGE(dst_x1 + width) && CHECK_RANGE(dst_y1 + + height) && + CHECK_RANGE(src_pitch) && CHECK_RANGE(dst_pitch)); +#undef CHECK_RANGE + igt_assert(bpp*(src_x1 + width) <= 8*src_pitch); + igt_assert(bpp*(dst_x1 + width) <= 8*dst_pitch); + igt_assert(src_pitch * (src_y1 + height) <= src_bo->size); + igt_assert(dst_pitch * (dst_y1 + height) <= dst_bo->size); + + drm_intel_bo_get_tiling(src_bo, &src_tiling, &swizzle); + drm_intel_bo_get_tiling(dst_bo, &dst_tiling, &swizzle); + + if (gen >= 4 && src_tiling != I915_TILING_NONE) { + src_pitch /= 4; + cmd_bits |= XY_SRC_COPY_BLT_SRC_TILED; + } + + if (gen >= 4 && dst_tiling != I915_TILING_NONE) { + dst_pitch /= 4; + cmd_bits |= XY_SRC_COPY_BLT_DST_TILED; + } + + br13_bits = 0; + switch (bpp) { + case 8: + break; + case 16: /* supporting only RGB565, not ARGB1555 */ + br13_bits |= 1 << 24; + break; + case 32: + br13_bits |= 3 << 24; + cmd_bits |= XY_SRC_COPY_BLT_WRITE_ALPHA | + XY_SRC_COPY_BLT_WRITE_RGB; + break; + default: + igt_fail(1); + } + + BLIT_COPY_BATCH_START(cmd_bits); + OUT_BATCH((br13_bits) | + (0xcc << 16) | /* copy ROP */ + dst_pitch); + OUT_BATCH((dst_y1 << 16) | dst_x1); /* dst x1,y1 */ + OUT_BATCH(((dst_y1 + height) << 16) | (dst_x1 + width)); /* dst x2,y2 */ + OUT_RELOC_FENCED(dst_bo, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER, 0); + OUT_BATCH((src_y1 << 16) | src_x1); /* src x1,y1 */ + OUT_BATCH(src_pitch); + OUT_RELOC_FENCED(src_bo, I915_GEM_DOMAIN_RENDER, 0, 0); + ADVANCE_BATCH(); + +#define CMD_POLY_STIPPLE_OFFSET 0x7906 + if (gen == 5) { + BEGIN_BATCH(2, 0); + OUT_BATCH(CMD_POLY_STIPPLE_OFFSET << 16); + OUT_BATCH(0); + ADVANCE_BATCH(); + } + + if (gen >= 6 && src_bo == dst_bo) { + BEGIN_BATCH(3, 0); + OUT_BATCH(XY_SETUP_CLIP_BLT_CMD); + OUT_BATCH(0); + OUT_BATCH(0); + ADVANCE_BATCH(); + } + + intel_batchbuffer_flush(batch); +} + +/** + * intel_copy_bo: + * @batch: batchbuffer object + * @src_bo: source libdrm buffer object + * @dst_bo: destination libdrm buffer object + * @size: size of the copy range in bytes + * + * This emits a copy operation using blitter commands into the supplied batch + * buffer object. A total of @size bytes from the start of @src_bo is copied + * over to @dst_bo. Note that @size must be page-aligned. + */ +void +intel_copy_bo(struct intel_batchbuffer *batch, + drm_intel_bo *dst_bo, drm_intel_bo *src_bo, + long int size) +{ + igt_assert(size % 4096 == 0); + + intel_blt_copy(batch, + src_bo, 0, 0, 4096, + dst_bo, 0, 0, 4096, + 4096/4, size/4096, 32); +} + +/** + * igt_buf_width: + * @buf: the i-g-t buffer object + * + * Computes the widht in 32-bit pixels of the given buffer. + * + * Returns: + * The width of the buffer. + */ +unsigned igt_buf_width(struct igt_buf *buf) +{ + return buf->stride/sizeof(uint32_t); +} + +/** + * igt_buf_height: + * @buf: the i-g-t buffer object + * + * Computes the height in 32-bit pixels of the given buffer. + * + * Returns: + * The height of the buffer. + */ +unsigned igt_buf_height(struct igt_buf *buf) +{ + return buf->size/buf->stride; +} + +/** + * igt_get_render_copyfunc: + * @devid: pci device id + * + * Returns: + * + * The platform-specific render copy function pointer for the device + * specified with @devid. Will return NULL when no render copy function is + * implemented. + */ +igt_render_copyfunc_t igt_get_render_copyfunc(int devid) +{ + igt_render_copyfunc_t copy = NULL; + + if (IS_GEN2(devid)) + copy = gen2_render_copyfunc; + else if (IS_GEN3(devid)) + copy = gen3_render_copyfunc; + else if (IS_GEN6(devid)) + copy = gen6_render_copyfunc; + else if (IS_GEN7(devid)) + copy = gen7_render_copyfunc; + else if (IS_GEN8(devid)) + copy = gen8_render_copyfunc; + else if (IS_GEN9(devid)) + copy = gen9_render_copyfunc; + + return copy; +} + +/** + * igt_get_media_fillfunc: + * @devid: pci device id + * + * Returns: + * + * The platform-specific media fill function pointer for the device specified + * with @devid. Will return NULL when no media fill function is implemented. + */ +igt_fillfunc_t igt_get_media_fillfunc(int devid) +{ + igt_fillfunc_t fill = NULL; + + if (IS_GEN9(devid)) + fill = gen9_media_fillfunc; + else if (IS_BROADWELL(devid)) + fill = gen8_media_fillfunc; + else if (IS_GEN7(devid)) + fill = gen7_media_fillfunc; + else if (IS_CHERRYVIEW(devid)) + fill = gen8lp_media_fillfunc; + + return fill; +} + +/** + * igt_get_gpgpu_fillfunc: + * @devid: pci device id + * + * Returns: + * + * The platform-specific gpgpu fill function pointer for the device specified + * with @devid. Will return NULL when no gpgpu fill function is implemented. + */ +igt_fillfunc_t igt_get_gpgpu_fillfunc(int devid) +{ + igt_fillfunc_t fill = NULL; + + if (IS_GEN7(devid)) + fill = gen7_gpgpu_fillfunc; + + return fill; +} diff --git a/lib/intel_batchbuffer.h b/lib/intel_batchbuffer.h new file mode 100644 index 0000000..12f7be1 --- /dev/null +++ b/lib/intel_batchbuffer.h @@ -0,0 +1,268 @@ +#ifndef INTEL_BATCHBUFFER_H +#define INTEL_BATCHBUFFER_H + +#include +#include +#include "igt_core.h" +#include "intel_reg.h" + +#define BATCH_SZ 4096 +#define BATCH_RESERVED 16 + +struct intel_batchbuffer { + drm_intel_bufmgr *bufmgr; + uint32_t devid; + int gen; + + drm_intel_context *ctx; + drm_intel_bo *bo; + + uint8_t buffer[BATCH_SZ]; + uint8_t *ptr, *end; + uint8_t *state; +}; + +struct intel_batchbuffer *intel_batchbuffer_alloc(drm_intel_bufmgr *bufmgr, + uint32_t devid); + +void intel_batchbuffer_set_context(struct intel_batchbuffer *batch, + drm_intel_context *ctx); + + +void intel_batchbuffer_free(struct intel_batchbuffer *batch); + + +void intel_batchbuffer_flush(struct intel_batchbuffer *batch); +void intel_batchbuffer_flush_on_ring(struct intel_batchbuffer *batch, int ring); +void intel_batchbuffer_flush_with_context(struct intel_batchbuffer *batch, + drm_intel_context *context); + +void intel_batchbuffer_reset(struct intel_batchbuffer *batch); + +void intel_batchbuffer_data(struct intel_batchbuffer *batch, + const void *data, unsigned int bytes); + +void intel_batchbuffer_emit_reloc(struct intel_batchbuffer *batch, + drm_intel_bo *buffer, + uint64_t delta, + uint32_t read_domains, + uint32_t write_domain, + int fenced); + +/* Inline functions - might actually be better off with these + * non-inlined. Certainly better off switching all command packets to + * be passed as structs rather than dwords, but that's a little bit of + * work... + */ +#pragma GCC diagnostic ignored "-Winline" +static inline unsigned int +intel_batchbuffer_space(struct intel_batchbuffer *batch) +{ + return (BATCH_SZ - BATCH_RESERVED) - (batch->ptr - batch->buffer); +} + + +static inline void +intel_batchbuffer_emit_dword(struct intel_batchbuffer *batch, uint32_t dword) +{ + igt_assert(intel_batchbuffer_space(batch) >= 4); + *(uint32_t *) (batch->ptr) = dword; + batch->ptr += 4; +} + +static inline void +intel_batchbuffer_require_space(struct intel_batchbuffer *batch, + unsigned int sz) +{ + igt_assert(sz < BATCH_SZ - BATCH_RESERVED); + if (intel_batchbuffer_space(batch) < sz) + intel_batchbuffer_flush(batch); +} + +/** + * BEGIN_BATCH: + * @n: number of DWORDS to emit + * @r: number of RELOCS to emit + * + * Prepares a batch to emit @n DWORDS, flushing it if there's not enough space + * available. + * + * This macro needs a pointer to an #intel_batchbuffer structure called batch in + * scope. + */ +#define BEGIN_BATCH(n, r) do { \ + int __n = (n); \ + igt_assert(batch->end == NULL); \ + if (batch->gen >= 8) __n += r; \ + __n *= 4; \ + intel_batchbuffer_require_space(batch, __n); \ + batch->end = batch->ptr + __n; \ +} while (0) + +/** + * OUT_BATCH: + * @d: DWORD to emit + * + * Emits @d into a batch. + * + * This macro needs a pointer to an #intel_batchbuffer structure called batch in + * scope. + */ +#define OUT_BATCH(d) intel_batchbuffer_emit_dword(batch, d) + +/** + * OUT_RELOC_FENCED: + * @buf: relocation target libdrm buffer object + * @read_domains: gem domain bits for the relocation + * @write_domain: gem domain bit for the relocation + * @delta: delta value to add to @buffer's gpu address + * + * Emits a fenced relocation into a batch. + * + * This macro needs a pointer to an #intel_batchbuffer structure called batch in + * scope. + */ +#define OUT_RELOC_FENCED(buf, read_domains, write_domain, delta) do { \ + igt_assert((delta) >= 0); \ + intel_batchbuffer_emit_reloc(batch, buf, delta, \ + read_domains, write_domain, 1); \ +} while (0) + +/** + * OUT_RELOC: + * @buf: relocation target libdrm buffer object + * @read_domains: gem domain bits for the relocation + * @write_domain: gem domain bit for the relocation + * @delta: delta value to add to @buffer's gpu address + * + * Emits a normal, unfenced relocation into a batch. + * + * This macro needs a pointer to an #intel_batchbuffer structure called batch in + * scope. + */ +#define OUT_RELOC(buf, read_domains, write_domain, delta) do { \ + igt_assert((delta) >= 0); \ + intel_batchbuffer_emit_reloc(batch, buf, delta, \ + read_domains, write_domain, 0); \ +} while (0) + +/** + * ADVANCE_BATCH: + * + * Completes the batch command emission sequence started with #BEGIN_BATCH. + * + * This macro needs a pointer to an #intel_batchbuffer structure called batch in + * scope. + */ +#define ADVANCE_BATCH() do { \ + igt_assert(batch->ptr == batch->end); \ + batch->end = NULL; \ +} while(0) + +#define BLIT_COPY_BATCH_START(flags) do { \ + BEGIN_BATCH(8, 2); \ + OUT_BATCH(XY_SRC_COPY_BLT_CMD | \ + XY_SRC_COPY_BLT_WRITE_ALPHA | \ + XY_SRC_COPY_BLT_WRITE_RGB | \ + (flags) | \ + (6 + 2*(batch->gen >= 8))); \ +} while(0) + +#define COLOR_BLIT_COPY_BATCH_START(flags) do { \ + BEGIN_BATCH(6, 1); \ + OUT_BATCH(XY_COLOR_BLT_CMD_NOLEN | \ + COLOR_BLT_WRITE_ALPHA | \ + XY_COLOR_BLT_WRITE_RGB | \ + (4 + (batch->gen >= 8))); \ +} while(0) + +void +intel_blt_copy(struct intel_batchbuffer *batch, + drm_intel_bo *src_bo, int src_x1, int src_y1, int src_pitch, + drm_intel_bo *dst_bo, int dst_x1, int dst_y1, int dst_pitch, + int width, int height, int bpp); +void intel_copy_bo(struct intel_batchbuffer *batch, + drm_intel_bo *dst_bo, drm_intel_bo *src_bo, + long int size); + +/** + * igt_buf: + * @bo: underlying libdrm buffer object + * @stride: stride of the buffer + * @tiling: tiling mode bits + * @data: pointer to the memory mapping of the buffer + * @size: size of the buffer object + * + * This is a i-g-t buffer object wrapper structure which augments the baseline + * libdrm buffer object with suitable data needed by the render copy and the + * fill functions. + */ +struct igt_buf { + drm_intel_bo *bo; + uint32_t stride; + uint32_t tiling; + uint32_t *data; + uint32_t size; + /*< private >*/ + unsigned num_tiles; +}; + +unsigned igt_buf_width(struct igt_buf *buf); +unsigned igt_buf_height(struct igt_buf *buf); + +/** + * igt_render_copyfunc_t: + * @batch: batchbuffer object + * @context: libdrm hardware context to use + * @src: source i-g-t buffer object + * @src_x: source pixel x-coordination + * @src_y: source pixel y-coordination + * @width: width of the copied rectangle + * @height: height of the copied rectangle + * @dst: destination i-g-t buffer object + * @dst_x: destination pixel x-coordination + * @dst_y: destination pixel y-coordination + * + * This is the type of the per-platform render copy functions. The + * platform-specific implementation can be obtained by calling + * igt_get_render_copyfunc(). + * + * A render copy function will emit a batchbuffer to the kernel which executes + * the specified blit copy operation using the render engine. @context is + * optional and can be NULL. + */ +typedef void (*igt_render_copyfunc_t)(struct intel_batchbuffer *batch, + drm_intel_context *context, + struct igt_buf *src, unsigned src_x, unsigned src_y, + unsigned width, unsigned height, + struct igt_buf *dst, unsigned dst_x, unsigned dst_y); + +igt_render_copyfunc_t igt_get_render_copyfunc(int devid); + +/** + * igt_fillfunc_t: + * @batch: batchbuffer object + * @dst: destination i-g-t buffer object + * @x: destination pixel x-coordination + * @y: destination pixel y-coordination + * @width: width of the filled rectangle + * @height: height of the filled rectangle + * @color: fill color to use + * + * This is the type of the per-platform fill functions using media + * or gpgpu pipeline. The platform-specific implementation can be obtained + * by calling igt_get_media_fillfunc() or igt_get_gpgpu_fillfunc(). + * + * A fill function will emit a batchbuffer to the kernel which executes + * the specified blit fill operation using the media/gpgpu engine. + */ +typedef void (*igt_fillfunc_t)(struct intel_batchbuffer *batch, + struct igt_buf *dst, + unsigned x, unsigned y, + unsigned width, unsigned height, + uint8_t color); + +igt_fillfunc_t igt_get_media_fillfunc(int devid); +igt_fillfunc_t igt_get_gpgpu_fillfunc(int devid); + +#endif diff --git a/lib/intel_chipset.c b/lib/intel_chipset.c new file mode 100644 index 0000000..3b3e03b --- /dev/null +++ b/lib/intel_chipset.c @@ -0,0 +1,206 @@ +/* + * Copyright © 2008 Intel Corporation + * + * 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: + * Eric Anholt + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "i915_drm.h" + +#include "intel_chipset.h" +#include "igt_core.h" + +/** + * SECTION:intel_chipset + * @short_description: Feature macros and chipset helpers + * @title: intel chipset + * @include: intel_chipset.h + * + * This library mostly provides feature macros which use raw pci device ids. It + * also provides a few more helper functions to handle pci devices, chipset + * detection and related issues. + */ + +/** + * intel_pch: + * + * Global variable to keep track of the pch type. Can either be set manually or + * detected at runtime with intel_check_pch(). + */ +enum pch_type intel_pch; + +/** + * intel_get_pci_device: + * + * Looks up the main graphics pci device using libpciaccess. + * + * Returns: + * The pci_device, exits the program on any failures. + */ +struct pci_device * +intel_get_pci_device(void) +{ + struct pci_device *pci_dev; + int error; + + error = pci_system_init(); + igt_fail_on_f(error != 0, + "Couldn't initialize PCI system\n"); + + /* Grab the graphics card. Try the canonical slot first, then + * walk the entire PCI bus for a matching device. */ + pci_dev = pci_device_find_by_slot(0, 0, 2, 0); + if (pci_dev == NULL || pci_dev->vendor_id != 0x8086) { + struct pci_device_iterator *iter; + struct pci_id_match match; + + match.vendor_id = 0x8086; /* Intel */ + match.device_id = PCI_MATCH_ANY; + match.subvendor_id = PCI_MATCH_ANY; + match.subdevice_id = PCI_MATCH_ANY; + + match.device_class = 0x3 << 16; + match.device_class_mask = 0xff << 16; + + match.match_data = 0; + + iter = pci_id_match_iterator_create(&match); + pci_dev = pci_device_next(iter); + pci_iterator_destroy(iter); + } + if (pci_dev == NULL) + errx(1, "Couldn't find graphics card"); + + error = pci_device_probe(pci_dev); + igt_fail_on_f(error != 0, + "Couldn't probe graphics card\n"); + + if (pci_dev->vendor_id != 0x8086) + errx(1, "Graphics card is non-intel"); + + return pci_dev; +} + +extern uint16_t __drm_device_id; + +/** + * intel_get_drm_devid: + * @fd: open i915 drm file descriptor + * + * Queries the kernel for the pci device id corresponding to the drm file + * descriptor. + * + * Returns: + * The devid, exits the program on any failures. + */ +uint32_t +intel_get_drm_devid(int fd) +{ + const char *override; + + igt_assert(__drm_device_id); + + override = getenv("INTEL_DEVID_OVERRIDE"); + if (override) + return strtol(override, NULL, 0); + else + return __drm_device_id; +} + +/** + * intel_gen: + * @devid: pci device id + * + * Computes the Intel GFX generation for the give device id. + * + * Returns: + * The GFX generation on successful lookup, -1 on failure. + */ +int intel_gen(uint32_t devid) +{ + if (IS_GEN2(devid)) + return 2; + if (IS_GEN3(devid)) + return 3; + if (IS_GEN4(devid)) + return 4; + if (IS_GEN5(devid)) + return 5; + if (IS_GEN6(devid)) + return 6; + if (IS_GEN7(devid)) + return 7; + if (IS_GEN8(devid)) + return 8; + if (IS_GEN9(devid)) + return 9; + + return -1; +} + +/** + * intel_check_pch: + * + * Detects the PCH chipset type of the running systems and fills in the results + * into the global #intel_pch varaible. + */ +void +intel_check_pch(void) +{ + struct pci_device *pch_dev; + + pch_dev = pci_device_find_by_slot(0, 0, 31, 0); + if (pch_dev == NULL) + return; + + if (pch_dev->vendor_id != 0x8086) + return; + + switch (pch_dev->device_id & 0xff00) { + case 0x3b00: + intel_pch = PCH_IBX; + break; + case 0x1c00: + case 0x1e00: + intel_pch = PCH_CPT; + break; + case 0x8c00: + case 0x9c00: + intel_pch = PCH_LPT; + break; + default: + intel_pch = PCH_NONE; + return; + } +} diff --git a/lib/intel_chipset.h b/lib/intel_chipset.h new file mode 100644 index 0000000..e3ce603 --- /dev/null +++ b/lib/intel_chipset.h @@ -0,0 +1,444 @@ +/* + * Copyright © 2007 Intel Corporation + * + * 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: + * Eric Anholt + * + */ + +#ifndef _INTEL_CHIPSET_H +#define _INTEL_CHIPSET_H + +#include + +struct pci_device *intel_get_pci_device(void); +uint32_t intel_get_drm_devid(int fd); +int intel_gen(uint32_t devid); + +extern enum pch_type intel_pch; + +enum pch_type { + PCH_NONE, + PCH_IBX, + PCH_CPT, + PCH_LPT, +}; + +void intel_check_pch(void); + +#define HAS_IBX (intel_pch == PCH_IBX) +#define HAS_CPT (intel_pch == PCH_CPT) +#define HAS_LPT (intel_pch == PCH_LPT) + +/* Exclude chipset #defines, they just add noise */ +#ifndef __GTK_DOC_IGNORE__ + +#define PCI_CHIP_I810 0x7121 +#define PCI_CHIP_I810_DC100 0x7123 +#define PCI_CHIP_I810_E 0x7125 +#define PCI_CHIP_I815 0x1132 + +#define PCI_CHIP_I830_M 0x3577 +#define PCI_CHIP_845_G 0x2562 +#define PCI_CHIP_I855_GM 0x3582 +#define PCI_CHIP_I865_G 0x2572 + +#define PCI_CHIP_I915_G 0x2582 +#define PCI_CHIP_E7221_G 0x258A +#define PCI_CHIP_I915_GM 0x2592 +#define PCI_CHIP_I945_G 0x2772 +#define PCI_CHIP_I945_GM 0x27A2 +#define PCI_CHIP_I945_GME 0x27AE + +#define PCI_CHIP_Q35_G 0x29B2 +#define PCI_CHIP_G33_G 0x29C2 +#define PCI_CHIP_Q33_G 0x29D2 + +#define PCI_CHIP_IGD_GM 0xA011 +#define PCI_CHIP_IGD_G 0xA001 + +#define IS_IGDGM(devid) ((devid) == PCI_CHIP_IGD_GM) +#define IS_IGDG(devid) ((devid) == PCI_CHIP_IGD_G) +#define IS_IGD(devid) (IS_IGDG(devid) || IS_IGDGM(devid)) + +#define PCI_CHIP_I965_G 0x29A2 +#define PCI_CHIP_I965_Q 0x2992 +#define PCI_CHIP_I965_G_1 0x2982 +#define PCI_CHIP_I946_GZ 0x2972 +#define PCI_CHIP_I965_GM 0x2A02 +#define PCI_CHIP_I965_GME 0x2A12 + +#define PCI_CHIP_GM45_GM 0x2A42 + +#define PCI_CHIP_IGD_E_G 0x2E02 +#define PCI_CHIP_Q45_G 0x2E12 +#define PCI_CHIP_G45_G 0x2E22 +#define PCI_CHIP_G41_G 0x2E32 + +#define PCI_CHIP_ILD_G 0x0042 +#define PCI_CHIP_ILM_G 0x0046 + +#define PCI_CHIP_SANDYBRIDGE_GT1 0x0102 /* desktop */ +#define PCI_CHIP_SANDYBRIDGE_GT2 0x0112 +#define PCI_CHIP_SANDYBRIDGE_GT2_PLUS 0x0122 +#define PCI_CHIP_SANDYBRIDGE_M_GT1 0x0106 /* mobile */ +#define PCI_CHIP_SANDYBRIDGE_M_GT2 0x0116 +#define PCI_CHIP_SANDYBRIDGE_M_GT2_PLUS 0x0126 +#define PCI_CHIP_SANDYBRIDGE_S 0x010A /* server */ + +#define PCI_CHIP_IVYBRIDGE_GT1 0x0152 /* desktop */ +#define PCI_CHIP_IVYBRIDGE_GT2 0x0162 +#define PCI_CHIP_IVYBRIDGE_M_GT1 0x0156 /* mobile */ +#define PCI_CHIP_IVYBRIDGE_M_GT2 0x0166 +#define PCI_CHIP_IVYBRIDGE_S 0x015a /* server */ +#define PCI_CHIP_IVYBRIDGE_S_GT2 0x016a /* server */ + +#define PCI_CHIP_HASWELL_GT1 0x0402 /* Desktop */ +#define PCI_CHIP_HASWELL_GT2 0x0412 +#define PCI_CHIP_HASWELL_GT3 0x0422 +#define PCI_CHIP_HASWELL_M_GT1 0x0406 /* Mobile */ +#define PCI_CHIP_HASWELL_M_GT2 0x0416 +#define PCI_CHIP_HASWELL_M_GT3 0x0426 +#define PCI_CHIP_HASWELL_S_GT1 0x040A /* Server */ +#define PCI_CHIP_HASWELL_S_GT2 0x041A +#define PCI_CHIP_HASWELL_S_GT3 0x042A +#define PCI_CHIP_HASWELL_B_GT1 0x040B /* Reserved */ +#define PCI_CHIP_HASWELL_B_GT2 0x041B +#define PCI_CHIP_HASWELL_B_GT3 0x042B +#define PCI_CHIP_HASWELL_E_GT1 0x040E /* Reserved */ +#define PCI_CHIP_HASWELL_E_GT2 0x041E +#define PCI_CHIP_HASWELL_E_GT3 0x042E +#define PCI_CHIP_HASWELL_SDV_GT1 0x0C02 /* Desktop */ +#define PCI_CHIP_HASWELL_SDV_GT2 0x0C12 +#define PCI_CHIP_HASWELL_SDV_GT3 0x0C22 +#define PCI_CHIP_HASWELL_SDV_M_GT1 0x0C06 /* Mobile */ +#define PCI_CHIP_HASWELL_SDV_M_GT2 0x0C16 +#define PCI_CHIP_HASWELL_SDV_M_GT3 0x0C26 +#define PCI_CHIP_HASWELL_SDV_S_GT1 0x0C0A /* Server */ +#define PCI_CHIP_HASWELL_SDV_S_GT2 0x0C1A +#define PCI_CHIP_HASWELL_SDV_S_GT3 0x0C2A +#define PCI_CHIP_HASWELL_SDV_B_GT1 0x0C0B /* Reserved */ +#define PCI_CHIP_HASWELL_SDV_B_GT2 0x0C1B +#define PCI_CHIP_HASWELL_SDV_B_GT3 0x0C2B +#define PCI_CHIP_HASWELL_SDV_E_GT1 0x0C0E /* Reserved */ +#define PCI_CHIP_HASWELL_SDV_E_GT2 0x0C1E +#define PCI_CHIP_HASWELL_SDV_E_GT3 0x0C2E +#define PCI_CHIP_HASWELL_ULT_GT1 0x0A02 /* Desktop */ +#define PCI_CHIP_HASWELL_ULT_GT2 0x0A12 +#define PCI_CHIP_HASWELL_ULT_GT3 0x0A22 +#define PCI_CHIP_HASWELL_ULT_M_GT1 0x0A06 /* Mobile */ +#define PCI_CHIP_HASWELL_ULT_M_GT2 0x0A16 +#define PCI_CHIP_HASWELL_ULT_M_GT3 0x0A26 +#define PCI_CHIP_HASWELL_ULT_S_GT1 0x0A0A /* Server */ +#define PCI_CHIP_HASWELL_ULT_S_GT2 0x0A1A +#define PCI_CHIP_HASWELL_ULT_S_GT3 0x0A2A +#define PCI_CHIP_HASWELL_ULT_B_GT1 0x0A0B /* Reserved */ +#define PCI_CHIP_HASWELL_ULT_B_GT2 0x0A1B +#define PCI_CHIP_HASWELL_ULT_B_GT3 0x0A2B +#define PCI_CHIP_HASWELL_ULT_E_GT1 0x0A0E /* Reserved */ +#define PCI_CHIP_HASWELL_ULT_E_GT2 0x0A1E +#define PCI_CHIP_HASWELL_ULT_E_GT3 0x0A2E +#define PCI_CHIP_HASWELL_CRW_GT1 0x0D02 /* Desktop */ +#define PCI_CHIP_HASWELL_CRW_GT2 0x0D12 +#define PCI_CHIP_HASWELL_CRW_GT3 0x0D22 +#define PCI_CHIP_HASWELL_CRW_M_GT1 0x0D06 /* Mobile */ +#define PCI_CHIP_HASWELL_CRW_M_GT2 0x0D16 +#define PCI_CHIP_HASWELL_CRW_M_GT3 0x0D26 +#define PCI_CHIP_HASWELL_CRW_S_GT1 0x0D0A /* Server */ +#define PCI_CHIP_HASWELL_CRW_S_GT2 0x0D1A +#define PCI_CHIP_HASWELL_CRW_S_GT3 0x0D2A +#define PCI_CHIP_HASWELL_CRW_B_GT1 0x0D0B /* Reserved */ +#define PCI_CHIP_HASWELL_CRW_B_GT2 0x0D1B +#define PCI_CHIP_HASWELL_CRW_B_GT3 0x0D2B +#define PCI_CHIP_HASWELL_CRW_E_GT1 0x0D0E /* Reserved */ +#define PCI_CHIP_HASWELL_CRW_E_GT2 0x0D1E +#define PCI_CHIP_HASWELL_CRW_E_GT3 0x0D2E +#define BDW_SPARE 0x2 +#define BDW_ULT 0x6 +#define BDW_HALO 0xb +#define BDW_SERVER 0xa +#define BDW_WORKSTATION 0xd +#define BDW_ULX 0xe + +#define PCI_CHIP_VALLEYVIEW_PO 0x0f30 /* VLV PO board */ +#define PCI_CHIP_VALLEYVIEW_1 0x0f31 +#define PCI_CHIP_VALLEYVIEW_2 0x0f32 +#define PCI_CHIP_VALLEYVIEW_3 0x0f33 + +#define PCI_CHIP_CHERRYVIEW_0 0x22b0 +#define PCI_CHIP_CHERRYVIEW_1 0x22b1 +#define PCI_CHIP_CHERRYVIEW_2 0x22b2 +#define PCI_CHIP_CHERRYVIEW_3 0x22b3 + +#define PCI_CHIP_SKYLAKE_ULT_GT2 0x1916 +#define PCI_CHIP_SKYLAKE_ULT_GT1 0x1906 +#define PCI_CHIP_SKYLAKE_ULT_GT3 0x1926 +#define PCI_CHIP_SKYLAKE_ULT_GT2F 0x1921 +#define PCI_CHIP_SKYLAKE_ULX_GT1 0x190E +#define PCI_CHIP_SKYLAKE_ULX_GT2 0x191E +#define PCI_CHIP_SKYLAKE_DT_GT2 0x1912 +#define PCI_CHIP_SKYLAKE_DT_GT1 0x1902 +#define PCI_CHIP_SKYLAKE_HALO_GT2 0x191B +#define PCI_CHIP_SKYLAKE_HALO_GT3 0x192B +#define PCI_CHIP_SKYLAKE_HALO_GT1 0x190B +#define PCI_CHIP_SKYLAKE_SRV_GT2 0x191A +#define PCI_CHIP_SKYLAKE_SRV_GT3 0x192A +#define PCI_CHIP_SKYLAKE_SRV_GT1 0x190A +#define PCI_CHIP_SKYLAKE_WKS_GT2 0x191D + +#endif /* __GTK_DOC_IGNORE__ */ + +#define IS_MOBILE(devid) ((devid) == PCI_CHIP_I855_GM || \ + (devid) == PCI_CHIP_I915_GM || \ + (devid) == PCI_CHIP_I945_GM || \ + (devid) == PCI_CHIP_I945_GME || \ + (devid) == PCI_CHIP_I965_GM || \ + (devid) == PCI_CHIP_I965_GME || \ + (devid) == PCI_CHIP_GM45_GM || IS_IGD(devid) || \ + (devid) == PCI_CHIP_IVYBRIDGE_M_GT1 || \ + (devid) == PCI_CHIP_IVYBRIDGE_M_GT2) + +#define IS_G45(devid) ((devid) == PCI_CHIP_IGD_E_G || \ + (devid) == PCI_CHIP_Q45_G || \ + (devid) == PCI_CHIP_G45_G || \ + (devid) == PCI_CHIP_G41_G) +#define IS_GM45(devid) ((devid) == PCI_CHIP_GM45_GM) +#define IS_G4X(devid) (IS_G45(devid) || IS_GM45(devid)) + +#define IS_ILD(devid) ((devid) == PCI_CHIP_ILD_G) +#define IS_ILM(devid) ((devid) == PCI_CHIP_ILM_G) + +#define IS_915(devid) ((devid) == PCI_CHIP_I915_G || \ + (devid) == PCI_CHIP_E7221_G || \ + (devid) == PCI_CHIP_I915_GM) + +#define IS_945GM(devid) ((devid) == PCI_CHIP_I945_GM || \ + (devid) == PCI_CHIP_I945_GME) + +#define IS_945(devid) ((devid) == PCI_CHIP_I945_G || \ + (devid) == PCI_CHIP_I945_GM || \ + (devid) == PCI_CHIP_I945_GME || \ + IS_G33(devid)) + +#define IS_G33(devid) ((devid) == PCI_CHIP_G33_G || \ + (devid) == PCI_CHIP_Q33_G || \ + (devid) == PCI_CHIP_Q35_G || IS_IGD(devid)) + +#define IS_GEN2(devid) ((devid) == PCI_CHIP_I830_M || \ + (devid) == PCI_CHIP_845_G || \ + (devid) == PCI_CHIP_I855_GM || \ + (devid) == PCI_CHIP_I865_G) + +#define IS_GEN3(devid) (IS_945(devid) || IS_915(devid)) + +#define IS_GEN4(devid) ((devid) == PCI_CHIP_I965_G || \ + (devid) == PCI_CHIP_I965_Q || \ + (devid) == PCI_CHIP_I965_G_1 || \ + (devid) == PCI_CHIP_I965_GM || \ + (devid) == PCI_CHIP_I965_GME || \ + (devid) == PCI_CHIP_I946_GZ || \ + IS_G4X(devid)) + +#define IS_GEN5(devid) (IS_ILD(devid) || IS_ILM(devid)) + +#define IS_GEN6(devid) ((devid) == PCI_CHIP_SANDYBRIDGE_GT1 || \ + (devid) == PCI_CHIP_SANDYBRIDGE_GT2 || \ + (devid) == PCI_CHIP_SANDYBRIDGE_GT2_PLUS || \ + (devid) == PCI_CHIP_SANDYBRIDGE_M_GT1 || \ + (devid) == PCI_CHIP_SANDYBRIDGE_M_GT2 || \ + (devid) == PCI_CHIP_SANDYBRIDGE_M_GT2_PLUS || \ + (devid) == PCI_CHIP_SANDYBRIDGE_S) + +#define IS_GEN7(devid) (IS_IVYBRIDGE(devid) || \ + IS_HASWELL(devid) || \ + IS_VALLEYVIEW(devid)) + +#define IS_IVYBRIDGE(devid) ((devid) == PCI_CHIP_IVYBRIDGE_GT1 || \ + (devid) == PCI_CHIP_IVYBRIDGE_GT2 || \ + (devid) == PCI_CHIP_IVYBRIDGE_M_GT1 || \ + (devid) == PCI_CHIP_IVYBRIDGE_M_GT2 || \ + (devid) == PCI_CHIP_IVYBRIDGE_S || \ + (devid) == PCI_CHIP_IVYBRIDGE_S_GT2) + +#define IS_VALLEYVIEW(devid) ((devid) == PCI_CHIP_VALLEYVIEW_PO || \ + (devid) == PCI_CHIP_VALLEYVIEW_1 || \ + (devid) == PCI_CHIP_VALLEYVIEW_2 || \ + (devid) == PCI_CHIP_VALLEYVIEW_3) + +#define IS_HSW_GT1(devid) ((devid) == PCI_CHIP_HASWELL_GT1 || \ + (devid) == PCI_CHIP_HASWELL_M_GT1 || \ + (devid) == PCI_CHIP_HASWELL_S_GT1 || \ + (devid) == PCI_CHIP_HASWELL_B_GT1 || \ + (devid) == PCI_CHIP_HASWELL_E_GT1 || \ + (devid) == PCI_CHIP_HASWELL_SDV_GT1 || \ + (devid) == PCI_CHIP_HASWELL_SDV_M_GT1 || \ + (devid) == PCI_CHIP_HASWELL_SDV_S_GT1 || \ + (devid) == PCI_CHIP_HASWELL_SDV_B_GT1 || \ + (devid) == PCI_CHIP_HASWELL_SDV_E_GT1 || \ + (devid) == PCI_CHIP_HASWELL_ULT_GT1 || \ + (devid) == PCI_CHIP_HASWELL_ULT_M_GT1 || \ + (devid) == PCI_CHIP_HASWELL_ULT_S_GT1 || \ + (devid) == PCI_CHIP_HASWELL_ULT_B_GT1 || \ + (devid) == PCI_CHIP_HASWELL_ULT_E_GT1 || \ + (devid) == PCI_CHIP_HASWELL_CRW_GT1 || \ + (devid) == PCI_CHIP_HASWELL_CRW_M_GT1 || \ + (devid) == PCI_CHIP_HASWELL_CRW_S_GT1 || \ + (devid) == PCI_CHIP_HASWELL_CRW_B_GT1 || \ + (devid) == PCI_CHIP_HASWELL_CRW_E_GT1) +#define IS_HSW_GT2(devid) ((devid) == PCI_CHIP_HASWELL_GT2 || \ + (devid) == PCI_CHIP_HASWELL_M_GT2 || \ + (devid) == PCI_CHIP_HASWELL_S_GT2 || \ + (devid) == PCI_CHIP_HASWELL_B_GT2 || \ + (devid) == PCI_CHIP_HASWELL_E_GT2 || \ + (devid) == PCI_CHIP_HASWELL_SDV_GT2 || \ + (devid) == PCI_CHIP_HASWELL_SDV_M_GT2 || \ + (devid) == PCI_CHIP_HASWELL_SDV_S_GT2 || \ + (devid) == PCI_CHIP_HASWELL_SDV_B_GT2 || \ + (devid) == PCI_CHIP_HASWELL_SDV_E_GT2 || \ + (devid) == PCI_CHIP_HASWELL_ULT_GT2 || \ + (devid) == PCI_CHIP_HASWELL_ULT_M_GT2 || \ + (devid) == PCI_CHIP_HASWELL_ULT_S_GT2 || \ + (devid) == PCI_CHIP_HASWELL_ULT_B_GT2 || \ + (devid) == PCI_CHIP_HASWELL_ULT_E_GT2 || \ + (devid) == PCI_CHIP_HASWELL_CRW_GT2 || \ + (devid) == PCI_CHIP_HASWELL_CRW_M_GT2 || \ + (devid) == PCI_CHIP_HASWELL_CRW_S_GT2 || \ + (devid) == PCI_CHIP_HASWELL_CRW_B_GT2 || \ + (devid) == PCI_CHIP_HASWELL_CRW_E_GT2) +#define IS_HSW_GT3(devid) ((devid) == PCI_CHIP_HASWELL_GT3 || \ + (devid) == PCI_CHIP_HASWELL_M_GT3 || \ + (devid) == PCI_CHIP_HASWELL_S_GT3 || \ + (devid) == PCI_CHIP_HASWELL_B_GT3 || \ + (devid) == PCI_CHIP_HASWELL_E_GT3 || \ + (devid) == PCI_CHIP_HASWELL_SDV_GT3 || \ + (devid) == PCI_CHIP_HASWELL_SDV_M_GT3 || \ + (devid) == PCI_CHIP_HASWELL_SDV_S_GT3 || \ + (devid) == PCI_CHIP_HASWELL_SDV_B_GT3 || \ + (devid) == PCI_CHIP_HASWELL_SDV_E_GT3 || \ + (devid) == PCI_CHIP_HASWELL_ULT_GT3 || \ + (devid) == PCI_CHIP_HASWELL_ULT_M_GT3 || \ + (devid) == PCI_CHIP_HASWELL_ULT_S_GT3 || \ + (devid) == PCI_CHIP_HASWELL_ULT_B_GT3 || \ + (devid) == PCI_CHIP_HASWELL_ULT_E_GT3 || \ + (devid) == PCI_CHIP_HASWELL_CRW_GT3 || \ + (devid) == PCI_CHIP_HASWELL_CRW_M_GT3 || \ + (devid) == PCI_CHIP_HASWELL_CRW_S_GT3 || \ + (devid) == PCI_CHIP_HASWELL_CRW_B_GT3 || \ + (devid) == PCI_CHIP_HASWELL_CRW_E_GT3) + +#define IS_HASWELL(devid) (IS_HSW_GT1(devid) || \ + IS_HSW_GT2(devid) || \ + IS_HSW_GT3(devid)) + +#define IS_BROADWELL(devid) ((((devid) & 0xff00) != 0x1600) ? 0 : \ + ((((devid) & 0x00f0) >> 4) > 3) ? 0 : \ + (((devid) & 0x000f) == BDW_SPARE) ? 1 : \ + (((devid) & 0x000f) == BDW_ULT) ? 1 : \ + (((devid) & 0x000f) == BDW_HALO) ? 1 : \ + (((devid) & 0x000f) == BDW_SERVER) ? 1 : \ + (((devid) & 0x000f) == BDW_WORKSTATION) ? 1 : \ + (((devid) & 0x000f) == BDW_ULX) ? 1 : 0) + +#define IS_CHERRYVIEW(devid) ((devid) == PCI_CHIP_CHERRYVIEW_0 || \ + (devid) == PCI_CHIP_CHERRYVIEW_1 || \ + (devid) == PCI_CHIP_CHERRYVIEW_2 || \ + (devid) == PCI_CHIP_CHERRYVIEW_3) + +#define IS_GEN8(devid) (IS_BROADWELL(devid) || \ + IS_CHERRYVIEW(devid)) + +#define IS_SKL_GT1(devid) ((devid) == PCI_CHIP_SKYLAKE_ULT_GT1 || \ + (devid) == PCI_CHIP_SKYLAKE_ULX_GT1 || \ + (devid) == PCI_CHIP_SKYLAKE_DT_GT1 || \ + (devid) == PCI_CHIP_SKYLAKE_HALO_GT1 || \ + (devid) == PCI_CHIP_SKYLAKE_SRV_GT1) + +#define IS_SKL_GT2(devid) ((devid) == PCI_CHIP_SKYLAKE_ULT_GT2 || \ + (devid) == PCI_CHIP_SKYLAKE_ULT_GT2F || \ + (devid) == PCI_CHIP_SKYLAKE_ULX_GT2 || \ + (devid) == PCI_CHIP_SKYLAKE_DT_GT2 || \ + (devid) == PCI_CHIP_SKYLAKE_HALO_GT2 || \ + (devid) == PCI_CHIP_SKYLAKE_SRV_GT2 || \ + (devid) == PCI_CHIP_SKYLAKE_WKS_GT2) + +#define IS_SKL_GT3(devid) ((devid) == PCI_CHIP_SKYLAKE_ULT_GT3 || \ + (devid) == PCI_CHIP_SKYLAKE_HALO_GT3 || \ + (devid) == PCI_CHIP_SKYLAKE_SRV_GT3) + +#define IS_SKYLAKE(devid) (IS_SKL_GT1(devid) || \ + IS_SKL_GT2(devid) || \ + IS_SKL_GT3(devid)) + +#define IS_GEN9(devid) IS_SKYLAKE(devid) + +#define IS_965(devid) (IS_GEN4(devid) || \ + IS_GEN5(devid) || \ + IS_GEN6(devid) || \ + IS_GEN7(devid) || \ + IS_GEN8(devid) || \ + IS_GEN9(devid)) + +#define IS_9XX(devid) (IS_GEN3(devid) || \ + IS_GEN4(devid) || \ + IS_GEN5(devid) || \ + IS_GEN6(devid) || \ + IS_GEN7(devid) || \ + IS_GEN8(devid) || \ + IS_GEN9(devid)) + +#define IS_INTEL(devid) (IS_GEN2(devid) || \ + IS_GEN3(devid) || \ + IS_GEN4(devid) || \ + IS_GEN5(devid) || \ + IS_GEN6(devid) || \ + IS_GEN7(devid) || \ + IS_GEN8(devid) || \ + IS_GEN9(devid)) + +#define HAS_PCH_SPLIT(devid) (IS_GEN5(devid) || \ + IS_GEN6(devid) || \ + IS_IVYBRIDGE(devid) || IS_HASWELL(devid) || \ + IS_GEN8(devid) || \ + IS_GEN9(devid)) + +#define HAS_BLT_RING(devid) (IS_GEN6(devid) || \ + IS_GEN7(devid) || \ + IS_GEN8(devid) || \ + IS_GEN9(devid)) + +#define HAS_BSD_RING(devid) (IS_GEN5(devid) || \ + IS_GEN6(devid) || \ + IS_GEN7(devid) || \ + IS_GEN8(devid) || \ + IS_GEN9(devid)) + +#define IS_BROADWATER(devid) ((devid) == PCI_CHIP_I946_GZ || \ + (devid) == PCI_CHIP_I965_G_1 || \ + (devid) == PCI_CHIP_I965_Q || \ + (devid) == PCI_CHIP_I965_G) + +#define IS_CRESTLINE(devid) ((devid) == PCI_CHIP_I965_GM || \ + (devid) == PCI_CHIP_I965_GME) + +#define HAS_VEBOX_RING(devid) (IS_HASWELL(devid)) + +#endif /* _INTEL_CHIPSET_H */ diff --git a/lib/intel_io.h b/lib/intel_io.h new file mode 100644 index 0000000..8293353 --- /dev/null +++ b/lib/intel_io.h @@ -0,0 +1,85 @@ +/* + * Copyright © 2009 Intel Corporation + * + * 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: + * Eric Anholt + * + */ + +#ifndef INTEL_GPU_TOOLS_H +#define INTEL_GPU_TOOLS_H + +#include +#include + +/* register access helpers from intel_mmio.c */ +extern void *mmio; +void intel_mmio_use_pci_bar(struct pci_device *pci_dev); +void intel_mmio_use_dump_file(char *file); + +int intel_register_access_init(struct pci_device *pci_dev, int safe); +void intel_register_access_fini(void); +uint32_t intel_register_read(uint32_t reg); +void intel_register_write(uint32_t reg, uint32_t val); +int intel_register_access_needs_fakewake(void); + +uint32_t INREG(uint32_t reg); +void OUTREG(uint32_t reg, uint32_t val); + +/* sideband access functions from intel_iosf.c */ +uint32_t intel_dpio_reg_read(uint32_t reg, int phy); +void intel_dpio_reg_write(uint32_t reg, uint32_t val, int phy); +uint32_t intel_flisdsi_reg_read(uint32_t reg); +void intel_flisdsi_reg_write(uint32_t reg, uint32_t val); +uint32_t intel_iosf_sb_read(uint32_t port, uint32_t reg); +void intel_iosf_sb_write(uint32_t port, uint32_t reg, uint32_t val); + +int intel_punit_read(uint8_t addr, uint32_t *val); +int intel_punit_write(uint8_t addr, uint32_t val); +int intel_nc_read(uint8_t addr, uint32_t *val); +int intel_nc_write(uint8_t addr, uint32_t val); + +/* register maps from intel_reg_map.c */ +#ifndef __GTK_DOC_IGNORE__ + +#define INTEL_RANGE_RSVD (0<<0) /* Shouldn't be read or written */ +#define INTEL_RANGE_READ (1<<0) +#define INTEL_RANGE_WRITE (1<<1) +#define INTEL_RANGE_RW (INTEL_RANGE_READ | INTEL_RANGE_WRITE) +#define INTEL_RANGE_END (1<<31) + +struct intel_register_range { + uint32_t base; + uint32_t size; + uint32_t flags; +}; + +struct intel_register_map { + struct intel_register_range *map; + uint32_t top; + uint32_t alignment_mask; +}; +struct intel_register_map intel_get_register_map(uint32_t devid); +struct intel_register_range *intel_get_register_range(struct intel_register_map map, uint32_t offset, uint32_t mode); +#endif /* __GTK_DOC_IGNORE__ */ + +#endif /* INTEL_GPU_TOOLS_H */ diff --git a/lib/intel_reg.h b/lib/intel_reg.h new file mode 100644 index 0000000..ade1c0c --- /dev/null +++ b/lib/intel_reg.h @@ -0,0 +1,3600 @@ +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/i810/i810_reg.h,v 1.13 2003/02/06 04:18:04 dawes Exp $ */ +/************************************************************************** + +Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas. +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, 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 PRECISION INSIGHT AND/OR ITS 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. + +**************************************************************************/ + +/* @file + * Register names and fields for Intel graphics. + */ + +/* + * Authors: + * Keith Whitwell + * Eric Anholt + * + * based on the i740 driver by + * Kevin E. Martin + * + * + */ + +#ifndef _I810_REG_H +#define _I810_REG_H + +/* I/O register offsets + */ +#define SRX 0x3C4 /* p208 */ +#define GRX 0x3CE /* p213 */ +#define ARX 0x3C0 /* p224 */ + +/* VGA Color Palette Registers */ +#define DACMASK 0x3C6 /* p232 */ +#define DACSTATE 0x3C7 /* p232 */ +#define DACRX 0x3C7 /* p233 */ +#define DACWX 0x3C8 /* p233 */ +#define DACDATA 0x3C9 /* p233 */ + +/* CRT Controller Registers (CRX) */ +#define START_ADDR_HI 0x0C /* p246 */ +#define START_ADDR_LO 0x0D /* p247 */ +#define VERT_SYNC_END 0x11 /* p249 */ +#define EXT_VERT_TOTAL 0x30 /* p257 */ +#define EXT_VERT_DISPLAY 0x31 /* p258 */ +#define EXT_VERT_SYNC_START 0x32 /* p259 */ +#define EXT_VERT_BLANK_START 0x33 /* p260 */ +#define EXT_HORIZ_TOTAL 0x35 /* p261 */ +#define EXT_HORIZ_BLANK 0x39 /* p261 */ +#define EXT_START_ADDR 0x40 /* p262 */ +#define EXT_START_ADDR_ENABLE 0x80 +#define EXT_OFFSET 0x41 /* p263 */ +#define EXT_START_ADDR_HI 0x42 /* p263 */ +#define INTERLACE_CNTL 0x70 /* p264 */ +#define INTERLACE_ENABLE 0x80 +#define INTERLACE_DISABLE 0x00 + +/* Miscellaneous Output Register + */ +#define MSR_R 0x3CC /* p207 */ +#define MSR_W 0x3C2 /* p207 */ +#define IO_ADDR_SELECT 0x01 + +#define MDA_BASE 0x3B0 /* p207 */ +#define CGA_BASE 0x3D0 /* p207 */ + +/* CR80 - IO Control, p264 + */ +#define IO_CTNL 0x80 +#define EXTENDED_ATTR_CNTL 0x02 +#define EXTENDED_CRTC_CNTL 0x01 + +/* GR10 - Address mapping, p221 + */ +#define ADDRESS_MAPPING 0x10 +#define PAGE_TO_LOCAL_MEM_ENABLE 0x10 +#define GTT_MEM_MAP_ENABLE 0x08 +#define PACKED_MODE_ENABLE 0x04 +#define LINEAR_MODE_ENABLE 0x02 +#define PAGE_MAPPING_ENABLE 0x01 + +#define HOTKEY_VBIOS_SWITCH_BLOCK 0x80 +#define HOTKEY_SWITCH 0x20 +#define HOTKEY_TOGGLE 0x10 + +/* Blitter control, p378 + */ +#define BITBLT_CNTL 0x7000c +#define COLEXP_MODE 0x30 +#define COLEXP_8BPP 0x00 +#define COLEXP_16BPP 0x10 +#define COLEXP_24BPP 0x20 +#define COLEXP_RESERVED 0x30 +#define BITBLT_STATUS 0x01 + +#define CHDECMISC 0x10111 +#define DCC 0x10200 +#define C0DRB0 0x10200 +#define C0DRB1 0x10202 +#define C0DRB2 0x10204 +#define C0DRB3 0x10206 +#define C0DRA01 0x10208 +#define C0DRA23 0x1020a +#define C1DRB0 0x10600 +#define C1DRB1 0x10602 +#define C1DRB2 0x10604 +#define C1DRB3 0x10606 +#define C1DRA01 0x10608 +#define C1DRA23 0x1060a + +/* p375. + */ +#define DISPLAY_CNTL 0x70008 +#define VGA_WRAP_MODE 0x02 +#define VGA_WRAP_AT_256KB 0x00 +#define VGA_NO_WRAP 0x02 +#define GUI_MODE 0x01 +#define STANDARD_VGA_MODE 0x00 +#define HIRES_MODE 0x01 + +/* p375 + */ +#define PIXPIPE_CONFIG_0 0x70009 +#define DAC_8_BIT 0x80 +#define DAC_6_BIT 0x00 +#define HW_CURSOR_ENABLE 0x10 +#define EXTENDED_PALETTE 0x01 + +/* p375 + */ +#define PIXPIPE_CONFIG_1 0x7000a +#define DISPLAY_COLOR_MODE 0x0F +#define DISPLAY_VGA_MODE 0x00 +#define DISPLAY_8BPP_MODE 0x02 +#define DISPLAY_15BPP_MODE 0x04 +#define DISPLAY_16BPP_MODE 0x05 +#define DISPLAY_24BPP_MODE 0x06 +#define DISPLAY_32BPP_MODE 0x07 + +/* p375 + */ +#define PIXPIPE_CONFIG_2 0x7000b +#define DISPLAY_GAMMA_ENABLE 0x08 +#define DISPLAY_GAMMA_DISABLE 0x00 +#define OVERLAY_GAMMA_ENABLE 0x04 +#define OVERLAY_GAMMA_DISABLE 0x00 + + +/* p380 + */ +#define DISPLAY_BASE 0x70020 +#define DISPLAY_BASE_MASK 0x03fffffc + + +/* Cursor control registers, pp383-384 + */ +/* Desktop (845G, 865G) */ +#define CURSOR_CONTROL 0x70080 +#define CURSOR_ENABLE 0x80000000 +#define CURSOR_GAMMA_ENABLE 0x40000000 +#define CURSOR_STRIDE_MASK 0x30000000 +#define CURSOR_FORMAT_SHIFT 24 +#define CURSOR_FORMAT_MASK (0x07 << CURSOR_FORMAT_SHIFT) +#define CURSOR_FORMAT_2C (0x00 << CURSOR_FORMAT_SHIFT) +#define CURSOR_FORMAT_3C (0x01 << CURSOR_FORMAT_SHIFT) +#define CURSOR_FORMAT_4C (0x02 << CURSOR_FORMAT_SHIFT) +#define CURSOR_FORMAT_ARGB (0x04 << CURSOR_FORMAT_SHIFT) +#define CURSOR_FORMAT_XRGB (0x05 << CURSOR_FORMAT_SHIFT) + +/* Mobile and i810 */ +#define CURSOR_A_CONTROL CURSOR_CONTROL +#define CURSOR_ORIGIN_SCREEN 0x00 /* i810 only */ +#define CURSOR_ORIGIN_DISPLAY 0x1 /* i810 only */ +#define CURSOR_MODE 0x27 +#define CURSOR_MODE_DISABLE 0x00 +#define CURSOR_MODE_32_4C_AX 0x01 /* i810 only */ +#define CURSOR_MODE_64_3C 0x04 +#define CURSOR_MODE_64_4C_AX 0x05 +#define CURSOR_MODE_64_4C 0x06 +#define CURSOR_MODE_64_32B_AX 0x07 +#define CURSOR_MODE_64_ARGB_AX (0x20 | CURSOR_MODE_64_32B_AX) +#define MCURSOR_PIPE_SELECT (1 << 28) +#define MCURSOR_PIPE_A 0x00 +#define MCURSOR_PIPE_B (1 << 28) +#define MCURSOR_GAMMA_ENABLE (1 << 26) +#define MCURSOR_MEM_TYPE_LOCAL (1 << 25) + + +#define CURSOR_BASEADDR 0x70084 +#define CURSOR_A_BASE CURSOR_BASEADDR +#define CURSOR_BASEADDR_MASK 0x1FFFFF00 +#define CURSOR_A_POSITION 0x70088 +#define CURSOR_POS_SIGN 0x8000 +#define CURSOR_POS_MASK 0x007FF +#define CURSOR_X_SHIFT 0 +#define CURSOR_Y_SHIFT 16 +#define CURSOR_X_LO 0x70088 +#define CURSOR_X_HI 0x70089 +#define CURSOR_X_POS 0x00 +#define CURSOR_X_NEG 0x80 +#define CURSOR_Y_LO 0x7008A +#define CURSOR_Y_HI 0x7008B +#define CURSOR_Y_POS 0x00 +#define CURSOR_Y_NEG 0x80 + +#define CURSOR_A_PALETTE0 0x70090 +#define CURSOR_A_PALETTE1 0x70094 +#define CURSOR_A_PALETTE2 0x70098 +#define CURSOR_A_PALETTE3 0x7009C + +#define CURSOR_SIZE 0x700A0 +#define CURSOR_SIZE_MASK 0x3FF +#define CURSOR_SIZE_HSHIFT 0 +#define CURSOR_SIZE_VSHIFT 12 + +#define CURSOR_B_CONTROL 0x700C0 +#define CURSOR_B_BASE 0x700C4 +#define CURSOR_B_POSITION 0x700C8 +#define CURSOR_B_PALETTE0 0x700D0 +#define CURSOR_B_PALETTE1 0x700D4 +#define CURSOR_B_PALETTE2 0x700D8 +#define CURSOR_B_PALETTE3 0x700DC + + +/* Similar registers exist in Device 0 on the i810 (pp55-65), but I'm + * not sure they refer to local (graphics) memory. + * + * These details are for the local memory control registers, + * (pp301-310). The test machines are not equiped with local memory, + * so nothing is tested. Only a single row seems to be supported. + */ +#define DRAM_ROW_TYPE 0x3000 +#define DRAM_ROW_0 0x01 +#define DRAM_ROW_0_SDRAM 0x01 +#define DRAM_ROW_0_EMPTY 0x00 +#define DRAM_ROW_CNTL_LO 0x3001 +#define DRAM_PAGE_MODE_CTRL 0x10 +#define DRAM_RAS_TO_CAS_OVRIDE 0x08 +#define DRAM_CAS_LATENCY 0x04 +#define DRAM_RAS_TIMING 0x02 +#define DRAM_RAS_PRECHARGE 0x01 +#define DRAM_ROW_CNTL_HI 0x3002 +#define DRAM_REFRESH_RATE 0x18 +#define DRAM_REFRESH_DISABLE 0x00 +#define DRAM_REFRESH_60HZ 0x08 +#define DRAM_REFRESH_FAST_TEST 0x10 +#define DRAM_REFRESH_RESERVED 0x18 +#define DRAM_SMS 0x07 +#define DRAM_SMS_NORMAL 0x00 +#define DRAM_SMS_NOP_ENABLE 0x01 +#define DRAM_SMS_ABPCE 0x02 +#define DRAM_SMS_MRCE 0x03 +#define DRAM_SMS_CBRCE 0x04 + +/* p307 + */ +#define DPMS_SYNC_SELECT 0x5002 +#define VSYNC_CNTL 0x08 +#define VSYNC_ON 0x00 +#define VSYNC_OFF 0x08 +#define HSYNC_CNTL 0x02 +#define HSYNC_ON 0x00 +#define HSYNC_OFF 0x02 + +#define GPIOA 0x5010 +#define GPIOB 0x5014 +#define GPIOC 0x5018 +#define GPIOD 0x501c +#define GPIOE 0x5020 +#define GPIOF 0x5024 +#define GPIOG 0x5028 +#define GPIOH 0x502c +# define GPIO_CLOCK_DIR_MASK (1 << 0) +# define GPIO_CLOCK_DIR_IN (0 << 1) +# define GPIO_CLOCK_DIR_OUT (1 << 1) +# define GPIO_CLOCK_VAL_MASK (1 << 2) +# define GPIO_CLOCK_VAL_OUT (1 << 3) +# define GPIO_CLOCK_VAL_IN (1 << 4) +# define GPIO_CLOCK_PULLUP_DISABLE (1 << 5) +# define GPIO_DATA_DIR_MASK (1 << 8) +# define GPIO_DATA_DIR_IN (0 << 9) +# define GPIO_DATA_DIR_OUT (1 << 9) +# define GPIO_DATA_VAL_MASK (1 << 10) +# define GPIO_DATA_VAL_OUT (1 << 11) +# define GPIO_DATA_VAL_IN (1 << 12) +# define GPIO_DATA_PULLUP_DISABLE (1 << 13) + +/* GMBus registers for hardware-assisted (non-bitbanging) I2C access */ +#define GMBUS0 0x5100 +#define GMBUS1 0x5104 +#define GMBUS2 0x5108 +#define GMBUS3 0x510c +#define GMBUS4 0x5110 +#define GMBUS5 0x5120 + +/* p317, 319 + */ +#define VCLK2_VCO_M 0x6008 /* treat as 16 bit? (includes msbs) */ +#define VCLK2_VCO_N 0x600a +#define VCLK2_VCO_DIV_SEL 0x6012 + +#define VCLK_DIVISOR_VGA0 0x6000 +#define VCLK_DIVISOR_VGA1 0x6004 +#define VCLK_POST_DIV 0x6010 +/* Selects a post divisor of 4 instead of 2. */ +# define VGA1_PD_P2_DIV_4 (1 << 15) +/* Overrides the p2 post divisor field */ +# define VGA1_PD_P1_DIV_2 (1 << 13) +# define VGA1_PD_P1_SHIFT 8 +/* P1 value is 2 greater than this field */ +# define VGA1_PD_P1_MASK (0x1f << 8) +/* Selects a post divisor of 4 instead of 2. */ +# define VGA0_PD_P2_DIV_4 (1 << 7) +/* Overrides the p2 post divisor field */ +# define VGA0_PD_P1_DIV_2 (1 << 5) +# define VGA0_PD_P1_SHIFT 0 +/* P1 value is 2 greater than this field */ +# define VGA0_PD_P1_MASK (0x1f << 0) + +#define POST_DIV_SELECT 0x70 +#define POST_DIV_1 0x00 +#define POST_DIV_2 0x10 +#define POST_DIV_4 0x20 +#define POST_DIV_8 0x30 +#define POST_DIV_16 0x40 +#define POST_DIV_32 0x50 +#define VCO_LOOP_DIV_BY_4M 0x00 +#define VCO_LOOP_DIV_BY_16M 0x04 + + +/* Instruction Parser Mode Register + * - p281 + * - 2 new bits. + */ +#define INST_PM 0x20c0 +#define AGP_SYNC_PACKET_FLUSH_ENABLE 0x20 /* reserved */ +#define SYNC_PACKET_FLUSH_ENABLE 0x10 +#define TWO_D_INST_DISABLE 0x08 +#define THREE_D_INST_DISABLE 0x04 +#define STATE_VAR_UPDATE_DISABLE 0x02 +#define PAL_STIP_DISABLE 0x01 +#define GEN6_GLOBAL_DEBUG_ENABLE 0x10 + + +#define MEMMODE 0x20dc + + +/* Instruction parser error register. p279 + */ +#define IPEIR 0x2088 +#define IPEHR 0x208C + +#define INSTDONE 0x2090 +#define NOP_ID 0x2094 + +#define SCPD0 0x209c /* debug */ +#define INST_PS 0x20c4 +#define IPEIR_I965 0x2064 /* i965 */ +#define IPEHR_I965 0x2068 /* i965 */ +#define INSTDONE_I965 0x206c +#define GEN6_INSTDONE_1 0x206c +#define INST_PS_I965 0x2070 + +/* Current active ring head address: + */ +#define ACTHD_I965 0x2074 +#define ACTHD 0x20C8 + +/* Current primary/secondary DMA fetch addresses: + */ +#define DMA_FADD_P 0x2078 +#define DMA_FADD_S 0x20d4 +#define INSTDONE_1 0x207c +#define GEN6_INSTDONE_2 0x207c + +#define CACHE_MODE_0 0x2120 +#define CACHE_MODE_1 0x2124 +#define MI_MODE 0x209c +#define MI_DISPLAY_POWER_DOWN 0x20e0 +#define MI_ARB_STATE 0x20e4 +#define MI_RDRET_STATE 0x20fc + +/* Start addresses for each of the primary rings: + */ +#define PR0_STR 0x20f0 +#define PR1_STR 0x20f4 +#define PR2_STR 0x20f8 + +#define WIZ_CTL 0x7c00 +#define WIZ_CTL_SINGLE_SUBSPAN (1<<6) +#define WIZ_CTL_IGNORE_STALLS (1<<5) + +#define SVG_WORK_CTL 0x7408 + +#define TS_CTL 0x7e00 +#define TS_MUX_ERR_CODE (0<<8) +#define TS_MUX_URB_0 (1<<8) +#define TS_MUX_DISPATCH_ID_0 (10<<8) +#define TS_MUX_ERR_CODE_VALID (15<<8) +#define TS_MUX_TID_0 (16<<8) +#define TS_MUX_EUID_0 (18<<8) +#define TS_MUX_FFID_0 (22<<8) +#define TS_MUX_EOT (26<<8) +#define TS_MUX_SIDEBAND_0 (27<<8) +#define TS_SNAP_ALL_CHILD (1<<2) +#define TS_SNAP_ALL_ROOT (1<<1) +#define TS_SNAP_ENABLE (1<<0) + +#define TS_DEBUG_DATA 0x7e0c + +#define TD_CTL 0x8000 +#define TD_CTL2 0x8004 + + +#define ECOSKPD 0x21d0 +#define EXCC 0x2028 + +/* I965 debug regs: + */ +#define IA_VERTICES_COUNT_QW 0x2310 +#define IA_PRIMITIVES_COUNT_QW 0x2318 +#define VS_INVOCATION_COUNT_QW 0x2320 +#define GS_INVOCATION_COUNT_QW 0x2328 +#define GS_PRIMITIVES_COUNT_QW 0x2330 +#define CL_INVOCATION_COUNT_QW 0x2338 +#define CL_PRIMITIVES_COUNT_QW 0x2340 +#define PS_INVOCATION_COUNT_QW 0x2348 +#define PS_DEPTH_COUNT_QW 0x2350 +#define TIMESTAMP_QW 0x2358 +#define CLKCMP_QW 0x2360 + + + + + + +/* General error reporting regs, p296 + */ +#define EIR 0x20B0 +#define EMR 0x20B4 +#define ESR 0x20B8 +# define ERR_VERTEX_MAX (1 << 5) /* lpt/cst */ +# define ERR_PGTBL_ERROR (1 << 4) +# define ERR_DISPLAY_OVERLAY_UNDERRUN (1 << 3) +# define ERR_MAIN_MEMORY_REFRESH (1 << 1) +# define ERR_INSTRUCTION_ERROR (1 << 0) + + +/* Interrupt Control Registers + * - new bits for i810 + * - new register hwstam (mask) + */ +#define HWS_PGA 0x2080 +#define PWRCTXA 0x2088 /* 965GM+ only */ +#define PWRCTX_EN (1<<0) +#define HWSTAM 0x2098 /* p290 */ +#define IER 0x20a0 /* p291 */ +#define IIR 0x20a4 /* p292 */ +#define IMR 0x20a8 /* p293 */ +#define ISR 0x20ac /* p294 */ +#define HW_ERROR 0x8000 +#define SYNC_STATUS_TOGGLE 0x1000 +#define DPY_0_FLIP_PENDING 0x0800 +#define DPY_1_FLIP_PENDING 0x0400 /* not implemented on i810 */ +#define OVL_0_FLIP_PENDING 0x0200 +#define OVL_1_FLIP_PENDING 0x0100 /* not implemented on i810 */ +#define DPY_0_VBLANK 0x0080 +#define DPY_0_EVENT 0x0040 +#define DPY_1_VBLANK 0x0020 /* not implemented on i810 */ +#define DPY_1_EVENT 0x0010 /* not implemented on i810 */ +#define HOST_PORT_EVENT 0x0008 /* */ +#define CAPTURE_EVENT 0x0004 /* */ +#define USER_DEFINED 0x0002 +#define BREAKPOINT 0x0001 + + +#define INTR_RESERVED (0x6000 | \ + DPY_1_FLIP_PENDING | \ + OVL_1_FLIP_PENDING | \ + DPY_1_VBLANK | \ + DPY_1_EVENT | \ + HOST_PORT_EVENT | \ + CAPTURE_EVENT ) + +/* FIFO Watermark and Burst Length Control Register + * + * - different offset and contents on i810 (p299) (fewer bits per field) + * - some overlay fields added + * - what does it all mean? + */ +#define FWATER_BLC 0x20d8 +#define FWATER_BLC2 0x20dc +#define MM_BURST_LENGTH 0x00700000 +#define MM_FIFO_WATERMARK 0x0001F000 +#define LM_BURST_LENGTH 0x00000700 +#define LM_FIFO_WATERMARK 0x0000001F + + +/* Fence/Tiling ranges [0..7] + */ +#define FENCE 0x2000 +#define FENCE_NR 8 + +#define FENCE_NEW 0x3000 +#define FENCE_NEW_NR 16 + +#define FENCE_LINEAR 0 +#define FENCE_XMAJOR 1 +#define FENCE_YMAJOR 2 + +#define I915G_FENCE_START_MASK 0x0ff00000 + +#define I830_FENCE_START_MASK 0x07f80000 + +#define FENCE_START_MASK 0x03F80000 +#define FENCE_X_MAJOR 0x00000000 +#define FENCE_Y_MAJOR 0x00001000 +#define FENCE_SIZE_MASK 0x00000700 +#define FENCE_SIZE_512K 0x00000000 +#define FENCE_SIZE_1M 0x00000100 +#define FENCE_SIZE_2M 0x00000200 +#define FENCE_SIZE_4M 0x00000300 +#define FENCE_SIZE_8M 0x00000400 +#define FENCE_SIZE_16M 0x00000500 +#define FENCE_SIZE_32M 0x00000600 +#define FENCE_SIZE_64M 0x00000700 +#define I915G_FENCE_SIZE_1M 0x00000000 +#define I915G_FENCE_SIZE_2M 0x00000100 +#define I915G_FENCE_SIZE_4M 0x00000200 +#define I915G_FENCE_SIZE_8M 0x00000300 +#define I915G_FENCE_SIZE_16M 0x00000400 +#define I915G_FENCE_SIZE_32M 0x00000500 +#define I915G_FENCE_SIZE_64M 0x00000600 +#define I915G_FENCE_SIZE_128M 0x00000700 +#define I965_FENCE_X_MAJOR 0x00000000 +#define I965_FENCE_Y_MAJOR 0x00000002 +#define FENCE_PITCH_1 0x00000000 +#define FENCE_PITCH_2 0x00000010 +#define FENCE_PITCH_4 0x00000020 +#define FENCE_PITCH_8 0x00000030 +#define FENCE_PITCH_16 0x00000040 +#define FENCE_PITCH_32 0x00000050 +#define FENCE_PITCH_64 0x00000060 +#define FENCE_VALID 0x00000001 + +#define FENCE_REG_SANDYBRIDGE_0 0x100000 + +/* Registers to control page table, p274 + */ +#define PGETBL_CTL 0x2020 +#define PGETBL_ADDR_MASK 0xFFFFF000 +#define PGETBL_ENABLE_MASK 0x00000001 +#define PGETBL_ENABLED 0x00000001 +/* Added in 965G, this field has the actual size of the global GTT */ +#define PGETBL_SIZE_MASK 0x0000000e +#define PGETBL_SIZE_512KB (0 << 1) +#define PGETBL_SIZE_256KB (1 << 1) +#define PGETBL_SIZE_128KB (2 << 1) +#define PGETBL_SIZE_1MB (3 << 1) +#define PGETBL_SIZE_2MB (4 << 1) +#define PGETBL_SIZE_1_5MB (5 << 1) +#define G33_PGETBL_SIZE_MASK (3 << 8) +#define G33_PGETBL_SIZE_1M (1 << 8) +#define G33_PGETBL_SIZE_2M (2 << 8) + +#define I830_PTE_BASE 0x10000 +#define PTE_ADDRESS_MASK 0xfffff000 +#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */ +#define PTE_MAPPING_TYPE_UNCACHED (0 << 1) +#define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */ +#define PTE_MAPPING_TYPE_CACHED (3 << 1) +#define PTE_MAPPING_TYPE_MASK (3 << 1) +#define PTE_VALID (1 << 0) + +/* @defgroup PGE_ERR + * @{ + */ +/* Page table debug register for i845 */ +#define PGE_ERR 0x2024 +#define PGE_ERR_ADDR_MASK 0xFFFFF000 +#define PGE_ERR_ID_MASK 0x00000038 +#define PGE_ERR_CAPTURE 0x00000000 +#define PGE_ERR_OVERLAY 0x00000008 +#define PGE_ERR_DISPLAY 0x00000010 +#define PGE_ERR_HOST 0x00000018 +#define PGE_ERR_RENDER 0x00000020 +#define PGE_ERR_BLITTER 0x00000028 +#define PGE_ERR_MAPPING 0x00000030 +#define PGE_ERR_CMD_PARSER 0x00000038 +#define PGE_ERR_TYPE_MASK 0x00000007 +#define PGE_ERR_INV_TABLE 0x00000000 +#define PGE_ERR_INV_PTE 0x00000001 +#define PGE_ERR_MIXED_TYPES 0x00000002 +#define PGE_ERR_PAGE_MISS 0x00000003 +#define PGE_ERR_ILLEGAL_TRX 0x00000004 +#define PGE_ERR_LOCAL_MEM 0x00000005 +#define PGE_ERR_TILED 0x00000006 +/* @} */ + +/* @defgroup PGTBL_ER + * @{ + */ +/* Page table debug register for i945 */ +# define PGTBL_ER 0x2024 +# define PGTBL_ERR_MT_TILING (1 << 27) +# define PGTBL_ERR_MT_GTT_PTE (1 << 26) +# define PGTBL_ERR_LC_TILING (1 << 25) +# define PGTBL_ERR_LC_GTT_PTE (1 << 24) +# define PGTBL_ERR_BIN_VERTEXDATA_GTT_PTE (1 << 23) +# define PGTBL_ERR_BIN_INSTRUCTION_GTT_PTE (1 << 22) +# define PGTBL_ERR_CS_VERTEXDATA_GTT_PTE (1 << 21) +# define PGTBL_ERR_CS_INSTRUCTION_GTT_PTE (1 << 20) +# define PGTBL_ERR_CS_GTT (1 << 19) +# define PGTBL_ERR_OVERLAY_TILING (1 << 18) +# define PGTBL_ERR_OVERLAY_GTT_PTE (1 << 16) +# define PGTBL_ERR_DISPC_TILING (1 << 14) +# define PGTBL_ERR_DISPC_GTT_PTE (1 << 12) +# define PGTBL_ERR_DISPB_TILING (1 << 10) +# define PGTBL_ERR_DISPB_GTT_PTE (1 << 8) +# define PGTBL_ERR_DISPA_TILING (1 << 6) +# define PGTBL_ERR_DISPA_GTT_PTE (1 << 4) +# define PGTBL_ERR_HOST_PTE_DATA (1 << 1) +# define PGTBL_ERR_HOST_GTT_PTE (1 << 0) +/* @} */ + +/* Ring buffer registers, p277, overview p19 + */ +#define LP_RING 0x2030 +#define HP_RING 0x2040 + +#define RING_TAIL 0x00 +#define TAIL_ADDR 0x000FFFF8 +#define I830_TAIL_MASK 0x001FFFF8 + +#define RING_HEAD 0x04 +#define HEAD_WRAP_COUNT 0xFFE00000 +#define HEAD_WRAP_ONE 0x00200000 +#define HEAD_ADDR 0x001FFFFC +#define I830_HEAD_MASK 0x001FFFFC + +#define RING_START 0x08 +#define START_ADDR 0x03FFFFF8 +#define I830_RING_START_MASK 0xFFFFF000 + +#define RING_LEN 0x0C +#define RING_NR_PAGES 0x001FF000 +#define I830_RING_NR_PAGES 0x001FF000 +#define RING_REPORT_MASK 0x00000006 +#define RING_REPORT_64K 0x00000002 +#define RING_REPORT_128K 0x00000004 +#define RING_NO_REPORT 0x00000000 +#define RING_VALID_MASK 0x00000001 +#define RING_VALID 0x00000001 +#define RING_INVALID 0x00000000 + + + +/* BitBlt Instructions + * + * There are many more masks & ranges yet to add. + */ +#define BR00_BITBLT_CLIENT 0x40000000 +#define BR00_OP_COLOR_BLT 0x10000000 +#define BR00_OP_SRC_COPY_BLT 0x10C00000 +#define BR00_OP_FULL_BLT 0x11400000 +#define BR00_OP_MONO_SRC_BLT 0x11800000 +#define BR00_OP_MONO_SRC_COPY_BLT 0x11000000 +#define BR00_OP_MONO_PAT_BLT 0x11C00000 +#define BR00_OP_MONO_SRC_COPY_IMMEDIATE_BLT (0x61 << 22) +#define BR00_OP_TEXT_IMMEDIATE_BLT 0xc000000 + + +#define BR00_TPCY_DISABLE 0x00000000 +#define BR00_TPCY_ENABLE 0x00000010 + +#define BR00_TPCY_ROP 0x00000000 +#define BR00_TPCY_NO_ROP 0x00000020 +#define BR00_TPCY_EQ 0x00000000 +#define BR00_TPCY_NOT_EQ 0x00000040 + +#define BR00_PAT_MSB_FIRST 0x00000000 /* ? */ + +#define BR00_PAT_VERT_ALIGN 0x000000e0 + +#define BR00_LENGTH 0x0000000F + +#define BR09_DEST_ADDR 0x03FFFFFF + +#define BR11_SOURCE_PITCH 0x00003FFF + +#define BR12_SOURCE_ADDR 0x03FFFFFF + +#define BR13_SOLID_PATTERN 0x80000000 +#define BR13_RIGHT_TO_LEFT 0x40000000 +#define BR13_LEFT_TO_RIGHT 0x00000000 +#define BR13_MONO_TRANSPCY 0x20000000 +#define BR13_MONO_PATN_TRANS 0x10000000 +#define BR13_USE_DYN_DEPTH 0x04000000 +#define BR13_DYN_8BPP 0x00000000 +#define BR13_DYN_16BPP 0x01000000 +#define BR13_DYN_24BPP 0x02000000 +#define BR13_ROP_MASK 0x00FF0000 +#define BR13_DEST_PITCH 0x0000FFFF +#define BR13_PITCH_SIGN_BIT 0x00008000 + +#define BR14_DEST_HEIGHT 0xFFFF0000 +#define BR14_DEST_WIDTH 0x0000FFFF + +#define BR15_PATTERN_ADDR 0x03FFFFFF + +#define BR16_SOLID_PAT_COLOR 0x00FFFFFF +#define BR16_BACKGND_PAT_CLR 0x00FFFFFF + +#define BR17_FGND_PAT_CLR 0x00FFFFFF + +#define BR18_SRC_BGND_CLR 0x00FFFFFF +#define BR19_SRC_FGND_CLR 0x00FFFFFF + + +/* Instruction parser instructions + */ + +#define INST_PARSER_CLIENT 0x00000000 +#define INST_OP_FLUSH 0x02000000 +#define INST_FLUSH_MAP_CACHE 0x00000001 + + +#define GFX_OP_USER_INTERRUPT ((0<<29)|(2<<23)) + + +/* Registers in the i810 host-pci bridge pci config space which affect + * the i810 graphics operations. + */ +#define SMRAM_MISCC 0x70 +#define GMS 0x000000c0 +#define GMS_DISABLE 0x00000000 +#define GMS_ENABLE_BARE 0x00000040 +#define GMS_ENABLE_512K 0x00000080 +#define GMS_ENABLE_1M 0x000000c0 +#define USMM 0x00000030 +#define USMM_DISABLE 0x00000000 +#define USMM_TSEG_ZERO 0x00000010 +#define USMM_TSEG_512K 0x00000020 +#define USMM_TSEG_1M 0x00000030 +#define GFX_MEM_WIN_SIZE 0x00010000 +#define GFX_MEM_WIN_32M 0x00010000 +#define GFX_MEM_WIN_64M 0x00000000 + +/* Overkill? I don't know. Need to figure out top of mem to make the + * SMRAM calculations come out. Linux seems to have problems + * detecting it all on its own, so this seems a reasonable double + * check to any user supplied 'mem=...' boot param. + * + * ... unfortunately this reg doesn't work according to spec on the + * test hardware. + */ +#define WHTCFG_PAMR_DRP 0x50 +#define SYS_DRAM_ROW_0_SHIFT 16 +#define SYS_DRAM_ROW_1_SHIFT 20 +#define DRAM_MASK 0x0f +#define DRAM_VALUE_0 0 +#define DRAM_VALUE_1 8 +/* No 2 value defined */ +#define DRAM_VALUE_3 16 +#define DRAM_VALUE_4 16 +#define DRAM_VALUE_5 24 +#define DRAM_VALUE_6 32 +#define DRAM_VALUE_7 32 +#define DRAM_VALUE_8 48 +#define DRAM_VALUE_9 64 +#define DRAM_VALUE_A 64 +#define DRAM_VALUE_B 96 +#define DRAM_VALUE_C 128 +#define DRAM_VALUE_D 128 +#define DRAM_VALUE_E 192 +#define DRAM_VALUE_F 256 /* nice one, geezer */ +#define LM_FREQ_MASK 0x10 +#define LM_FREQ_133 0x10 +#define LM_FREQ_100 0x00 + + + + +/* These are 3d state registers, but the state is invarient, so we let + * the X server handle it: + */ + + + +/* GFXRENDERSTATE_COLOR_CHROMA_KEY, p135 + */ +#define GFX_OP_COLOR_CHROMA_KEY ((0x3<<29)|(0x1d<<24)|(0x2<<16)|0x1) +#define CC1_UPDATE_KILL_WRITE (1<<28) +#define CC1_ENABLE_KILL_WRITE (1<<27) +#define CC1_DISABLE_KILL_WRITE 0 +#define CC1_UPDATE_COLOR_IDX (1<<26) +#define CC1_UPDATE_CHROMA_LOW (1<<25) +#define CC1_UPDATE_CHROMA_HI (1<<24) +#define CC1_CHROMA_LOW_MASK ((1<<24)-1) +#define CC2_COLOR_IDX_SHIFT 24 +#define CC2_COLOR_IDX_MASK (0xff<<24) +#define CC2_CHROMA_HI_MASK ((1<<24)-1) + + +#define GFX_CMD_CONTEXT_SEL ((0<<29)|(0x5<<23)) +#define CS_UPDATE_LOAD (1<<17) +#define CS_UPDATE_USE (1<<16) +#define CS_UPDATE_LOAD (1<<17) +#define CS_LOAD_CTX0 0 +#define CS_LOAD_CTX1 (1<<8) +#define CS_USE_CTX0 0 +#define CS_USE_CTX1 (1<<0) + +/* I810 LCD/TV registers */ +#define LCD_TV_HTOTAL 0x60000 +#define LCD_TV_C 0x60018 +#define LCD_TV_OVRACT 0x6001C + +#define LCD_TV_ENABLE (1 << 31) +#define LCD_TV_VGAMOD (1 << 28) + +/* I830 CRTC registers */ +#define HTOTAL_A 0x60000 +#define HBLANK_A 0x60004 +#define HSYNC_A 0x60008 +#define VTOTAL_A 0x6000c +#define VBLANK_A 0x60010 +#define VSYNC_A 0x60014 +#define PIPEASRC 0x6001c +#define BCLRPAT_A 0x60020 +#define VSYNCSHIFT_A 0x60028 + +#define HTOTAL_B 0x61000 +#define HBLANK_B 0x61004 +#define HSYNC_B 0x61008 +#define VTOTAL_B 0x6100c +#define VBLANK_B 0x61010 +#define VSYNC_B 0x61014 +#define PIPEBSRC 0x6101c +#define BCLRPAT_B 0x61020 +#define VSYNCSHIFT_B 0x61028 + +#define HTOTAL_C 0x62000 +#define HBLANK_C 0x62004 +#define HSYNC_C 0x62008 +#define VTOTAL_C 0x6200c +#define VBLANK_C 0x62010 +#define VSYNC_C 0x62014 +#define PIPECSRC 0x6201c +#define BCLRPAT_C 0x62020 +#define VSYNCSHIFT_C 0x62028 + +#define HTOTAL_EDP 0x6F000 +#define HBLANK_EDP 0x6F004 +#define HSYNC_EDP 0x6F008 +#define VTOTAL_EDP 0x6F00c +#define VBLANK_EDP 0x6F010 +#define VSYNC_EDP 0x6F014 +#define VSYNCSHIFT_EDP 0x6F028 + +#define PP_STATUS 0x61200 +# define PP_ON (1 << 31) +/* + * Indicates that all dependencies of the panel are on: + * + * - PLL enabled + * - pipe enabled + * - LVDS/DVOB/DVOC on + */ +# define PP_READY (1 << 30) +# define PP_SEQUENCE_NONE (0 << 28) +# define PP_SEQUENCE_ON (1 << 28) +# define PP_SEQUENCE_OFF (2 << 28) +# define PP_SEQUENCE_MASK 0x30000000 + +#define PP_CONTROL 0x61204 +# define POWER_DOWN_ON_RESET (1 << 1) +# define POWER_TARGET_ON (1 << 0) + +#define PP_ON_DELAYS 0x61208 +#define PP_OFF_DELAYS 0x6120c +#define PP_DIVISOR 0x61210 + +#define PFIT_CONTROL 0x61230 +# define PFIT_ENABLE (1 << 31) +/* Pre-965 */ +# define VERT_INTERP_DISABLE (0 << 10) +# define VERT_INTERP_BILINEAR (1 << 10) +# define VERT_INTERP_MASK (3 << 10) +# define VERT_AUTO_SCALE (1 << 9) +# define HORIZ_INTERP_DISABLE (0 << 6) +# define HORIZ_INTERP_BILINEAR (1 << 6) +# define HORIZ_INTERP_MASK (3 << 6) +# define HORIZ_AUTO_SCALE (1 << 5) +# define PANEL_8TO6_DITHER_ENABLE (1 << 3) +/* 965+ */ +# define PFIT_PIPE_MASK (3 << 29) +# define PFIT_PIPE_SHIFT 29 +# define PFIT_SCALING_MODE_MASK (7 << 26) +# define PFIT_SCALING_AUTO (0 << 26) +# define PFIT_SCALING_PROGRAMMED (1 << 26) +# define PFIT_SCALING_PILLAR (2 << 26) +# define PFIT_SCALING_LETTER (3 << 26) +# define PFIT_FILTER_SELECT_MASK (3 << 24) +# define PFIT_FILTER_FUZZY (0 << 24) +# define PFIT_FILTER_CRISP (1 << 24) +# define PFIT_FILTER_MEDIAN (2 << 24) + +#define PFIT_PGM_RATIOS 0x61234 +/* Pre-965 */ +# define PFIT_VERT_SCALE_SHIFT 20 +# define PFIT_VERT_SCALE_MASK 0xfff00000 +# define PFIT_HORIZ_SCALE_SHIFT 4 +# define PFIT_HORIZ_SCALE_MASK 0x0000fff0 +/* 965+ */ +# define PFIT_VERT_SCALE_SHIFT_965 16 +# define PFIT_VERT_SCALE_MASK_965 0x1fff0000 +# define PFIT_HORIZ_SCALE_SHIFT_965 0 +# define PFIT_HORIZ_SCALE_MASK_965 0x00001fff + +#define DPLL_A 0x06014 +#define DPLL_B 0x06018 +# define DPLL_VCO_ENABLE (1 << 31) +# define DPLL_DVO_HIGH_SPEED (1 << 30) +# define DPLL_SYNCLOCK_ENABLE (1 << 29) +# define DPLL_VGA_MODE_DIS (1 << 28) +# define DPLLB_MODE_DAC_SERIAL (1 << 26) /* i915 */ +# define DPLLB_MODE_LVDS (2 << 26) /* i915 */ +# define DPLL_MODE_MASK (3 << 26) +# define DPLL_DAC_SERIAL_P2_CLOCK_DIV_10 (0 << 24) /* i915 */ +# define DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 (1 << 24) /* i915 */ +# define DPLLB_LVDS_P2_CLOCK_DIV_14 (0 << 24) /* i915 */ +# define DPLLB_LVDS_P2_CLOCK_DIV_7 (1 << 24) /* i915 */ +# define DPLL_P2_CLOCK_DIV_MASK 0x03000000 /* i915 */ +# define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */ +# define DPLL_FPA01_P1_POST_DIV_MASK_IGD 0x00ff8000 /* IGD */ +/* + * The i830 generation, in DAC/serial mode, defines p1 as two plus this + * bitfield, or just 2 if PLL_P1_DIVIDE_BY_TWO is set. + */ +# define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000 +/* + * The i830 generation, in LVDS mode, defines P1 as the bit number set within + * this field (only one bit may be set). + */ +# define DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS 0x003f0000 +# define DPLL_FPA01_P1_POST_DIV_SHIFT 16 +# define DPLL_FPA01_P1_POST_DIV_SHIFT_IGD 15 +# define PLL_P2_DIVIDE_BY_4 (1 << 23) /* i830, required in DVO non-gang */ +# define PLL_P1_DIVIDE_BY_TWO (1 << 21) /* i830 */ +# define PLL_REF_INPUT_DREFCLK (0 << 13) +# define PLL_REF_INPUT_TVCLKINA (1 << 13) /* i830 */ +# define PLL_REF_INPUT_SUPER_SSC (1 << 13) /* Ironlake: 120M SSC */ +# define PLL_REF_INPUT_TVCLKINBC (2 << 13) /* SDVO TVCLKIN */ +# define PLLB_REF_INPUT_SPREADSPECTRUMIN (3 << 13) +# define PLL_REF_INPUT_MASK (3 << 13) +# define PLL_REF_INPUT_DMICLK (5 << 13) /* Ironlake: DMI refclk */ +# define PLL_LOAD_PULSE_PHASE_SHIFT 9 +/* + * Parallel to Serial Load Pulse phase selection. + * Selects the phase for the 10X DPLL clock for the PCIe + * digital display port. The range is 4 to 13; 10 or more + * is just a flip delay. The default is 6 + */ +# define PLL_LOAD_PULSE_PHASE_MASK (0xf << PLL_LOAD_PULSE_PHASE_SHIFT) +# define DISPLAY_RATE_SELECT_FPA1 (1 << 8) +/* Ironlake */ +# define PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT 9 +# define PLL_REF_SDVO_HDMI_MULTIPLIER_MASK (7 << 9) +# define PLL_REF_SDVO_HDMI_MULTIPLIER(x) (((x)-1)<< PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT) +# define DPLL_FPA1_P1_POST_DIV_SHIFT 0 +# define DPLL_FPA1_P1_POST_DIV_MASK 0xff + +/* + * SDVO multiplier for 945G/GM. Not used on 965. + * + * \sa DPLL_MD_UDI_MULTIPLIER_MASK + */ +# define SDVO_MULTIPLIER_MASK 0x000000ff +# define SDVO_MULTIPLIER_SHIFT_HIRES 4 +# define SDVO_MULTIPLIER_SHIFT_VGA 0 + +/* @defgroup DPLL_MD + * @{ + */ +/* Pipe A SDVO/UDI clock multiplier/divider register for G965. */ +#define DPLL_A_MD 0x0601c +/* Pipe B SDVO/UDI clock multiplier/divider register for G965. */ +#define DPLL_B_MD 0x06020 +/* + * UDI pixel divider, controlling how many pixels are stuffed into a packet. + * + * Value is pixels minus 1. Must be set to 1 pixel for SDVO. + */ +# define DPLL_MD_UDI_DIVIDER_MASK 0x3f000000 +# define DPLL_MD_UDI_DIVIDER_SHIFT 24 +/* UDI pixel divider for VGA, same as DPLL_MD_UDI_DIVIDER_MASK. */ +# define DPLL_MD_VGA_UDI_DIVIDER_MASK 0x003f0000 +# define DPLL_MD_VGA_UDI_DIVIDER_SHIFT 16 +/* + * SDVO/UDI pixel multiplier. + * + * SDVO requires that the bus clock rate be between 1 and 2 Ghz, and the bus + * clock rate is 10 times the DPLL clock. At low resolution/refresh rate + * modes, the bus rate would be below the limits, so SDVO allows for stuffing + * dummy bytes in the datastream at an increased clock rate, with both sides of + * the link knowing how many bytes are fill. + * + * So, for a mode with a dotclock of 65Mhz, we would want to double the clock + * rate to 130Mhz to get a bus rate of 1.30Ghz. The DPLL clock rate would be + * set to 130Mhz, and the SDVO multiplier set to 2x in this register and + * through an SDVO command. + * + * This register field has values of multiplication factor minus 1, with + * a maximum multiplier of 5 for SDVO. + */ +# define DPLL_MD_UDI_MULTIPLIER_MASK 0x00003f00 +# define DPLL_MD_UDI_MULTIPLIER_SHIFT 8 +/* SDVO/UDI pixel multiplier for VGA, same as DPLL_MD_UDI_MULTIPLIER_MASK. + * This best be set to the default value (3) or the CRT won't work. No, + * I don't entirely understand what this does... + */ +# define DPLL_MD_VGA_UDI_MULTIPLIER_MASK 0x0000003f +# define DPLL_MD_VGA_UDI_MULTIPLIER_SHIFT 0 +/* @} */ + +#define DPLL_TEST 0x606c +# define DPLLB_TEST_SDVO_DIV_1 (0 << 22) +# define DPLLB_TEST_SDVO_DIV_2 (1 << 22) +# define DPLLB_TEST_SDVO_DIV_4 (2 << 22) +# define DPLLB_TEST_SDVO_DIV_MASK (3 << 22) +# define DPLLB_TEST_N_BYPASS (1 << 19) +# define DPLLB_TEST_M_BYPASS (1 << 18) +# define DPLLB_INPUT_BUFFER_ENABLE (1 << 16) +# define DPLLA_TEST_N_BYPASS (1 << 3) +# define DPLLA_TEST_M_BYPASS (1 << 2) +# define DPLLA_INPUT_BUFFER_ENABLE (1 << 0) + +#define D_STATE 0x6104 +#define DSPCLK_GATE_D 0x6200 +# define DPUNIT_B_CLOCK_GATE_DISABLE (1 << 30) /* 965 */ +# define VSUNIT_CLOCK_GATE_DISABLE (1 << 29) /* 965 */ +# define VRHUNIT_CLOCK_GATE_DISABLE (1 << 28) /* 965 */ +# define VRDUNIT_CLOCK_GATE_DISABLE (1 << 27) /* 965 */ +# define AUDUNIT_CLOCK_GATE_DISABLE (1 << 26) /* 965 */ +# define DPUNIT_A_CLOCK_GATE_DISABLE (1 << 25) /* 965 */ +# define DPCUNIT_CLOCK_GATE_DISABLE (1 << 24) /* 965 */ +# define TVRUNIT_CLOCK_GATE_DISABLE (1 << 23) /* 915-945 */ +# define TVCUNIT_CLOCK_GATE_DISABLE (1 << 22) /* 915-945 */ +# define TVFUNIT_CLOCK_GATE_DISABLE (1 << 21) /* 915-945 */ +# define TVEUNIT_CLOCK_GATE_DISABLE (1 << 20) /* 915-945 */ +# define DVSUNIT_CLOCK_GATE_DISABLE (1 << 19) /* 915-945 */ +# define DSSUNIT_CLOCK_GATE_DISABLE (1 << 18) /* 915-945 */ +# define DDBUNIT_CLOCK_GATE_DISABLE (1 << 17) /* 915-945 */ +# define DPRUNIT_CLOCK_GATE_DISABLE (1 << 16) /* 915-945 */ +# define DPFUNIT_CLOCK_GATE_DISABLE (1 << 15) /* 915-945 */ +# define DPBMUNIT_CLOCK_GATE_DISABLE (1 << 14) /* 915-945 */ +# define DPLSUNIT_CLOCK_GATE_DISABLE (1 << 13) /* 915-945 */ +# define DPLUNIT_CLOCK_GATE_DISABLE (1 << 12) /* 915-945 */ +# define DPOUNIT_CLOCK_GATE_DISABLE (1 << 11) +# define DPBUNIT_CLOCK_GATE_DISABLE (1 << 10) +# define DCUNIT_CLOCK_GATE_DISABLE (1 << 9) +# define DPUNIT_CLOCK_GATE_DISABLE (1 << 8) +# define VRUNIT_CLOCK_GATE_DISABLE (1 << 7) /* 915+: reserved */ +# define OVHUNIT_CLOCK_GATE_DISABLE (1 << 6) /* 830-865 */ +# define DPIOUNIT_CLOCK_GATE_DISABLE (1 << 6) /* 915-945 */ +# define OVFUNIT_CLOCK_GATE_DISABLE (1 << 5) +# define OVBUNIT_CLOCK_GATE_DISABLE (1 << 4) +/* + * This bit must be set on the 830 to prevent hangs when turning off the + * overlay scaler. + */ +# define OVRUNIT_CLOCK_GATE_DISABLE (1 << 3) +# define OVCUNIT_CLOCK_GATE_DISABLE (1 << 2) +# define OVUUNIT_CLOCK_GATE_DISABLE (1 << 1) +# define ZVUNIT_CLOCK_GATE_DISABLE (1 << 0) /* 830 */ +# define OVLUNIT_CLOCK_GATE_DISABLE (1 << 0) /* 845,865 */ + +#define RENCLK_GATE_D1 0x6204 +# define BLITTER_CLOCK_GATE_DISABLE (1 << 13) /* 945GM only */ +# define MPEG_CLOCK_GATE_DISABLE (1 << 12) /* 945GM only */ +# define PC_FE_CLOCK_GATE_DISABLE (1 << 11) +# define PC_BE_CLOCK_GATE_DISABLE (1 << 10) +# define WINDOWER_CLOCK_GATE_DISABLE (1 << 9) +# define INTERPOLATOR_CLOCK_GATE_DISABLE (1 << 8) +# define COLOR_CALCULATOR_CLOCK_GATE_DISABLE (1 << 7) +# define MOTION_COMP_CLOCK_GATE_DISABLE (1 << 6) +# define MAG_CLOCK_GATE_DISABLE (1 << 5) +/* This bit must be unset on 855,865 */ +# define MECI_CLOCK_GATE_DISABLE (1 << 4) +# define DCMP_CLOCK_GATE_DISABLE (1 << 3) +# define MEC_CLOCK_GATE_DISABLE (1 << 2) +# define MECO_CLOCK_GATE_DISABLE (1 << 1) +/* This bit must be set on 855,865. */ +# define SV_CLOCK_GATE_DISABLE (1 << 0) +# define I915_MPEG_CLOCK_GATE_DISABLE (1 << 16) +# define I915_VLD_IP_PR_CLOCK_GATE_DISABLE (1 << 15) +# define I915_MOTION_COMP_CLOCK_GATE_DISABLE (1 << 14) +# define I915_BD_BF_CLOCK_GATE_DISABLE (1 << 13) +# define I915_SF_SE_CLOCK_GATE_DISABLE (1 << 12) +# define I915_WM_CLOCK_GATE_DISABLE (1 << 11) +# define I915_IZ_CLOCK_GATE_DISABLE (1 << 10) +# define I915_PI_CLOCK_GATE_DISABLE (1 << 9) +# define I915_DI_CLOCK_GATE_DISABLE (1 << 8) +# define I915_SH_SV_CLOCK_GATE_DISABLE (1 << 7) +# define I915_PL_DG_QC_FT_CLOCK_GATE_DISABLE (1 << 6) +# define I915_SC_CLOCK_GATE_DISABLE (1 << 5) +# define I915_FL_CLOCK_GATE_DISABLE (1 << 4) +# define I915_DM_CLOCK_GATE_DISABLE (1 << 3) +# define I915_PS_CLOCK_GATE_DISABLE (1 << 2) +# define I915_CC_CLOCK_GATE_DISABLE (1 << 1) +# define I915_BY_CLOCK_GATE_DISABLE (1 << 0) + +# define I965_RCZ_CLOCK_GATE_DISABLE (1 << 30) +/* This bit must always be set on 965G/965GM */ +# define I965_RCC_CLOCK_GATE_DISABLE (1 << 29) +# define I965_RCPB_CLOCK_GATE_DISABLE (1 << 28) +# define I965_DAP_CLOCK_GATE_DISABLE (1 << 27) +# define I965_ROC_CLOCK_GATE_DISABLE (1 << 26) +# define I965_GW_CLOCK_GATE_DISABLE (1 << 25) +# define I965_TD_CLOCK_GATE_DISABLE (1 << 24) +/* This bit must always be set on 965G */ +# define I965_ISC_CLOCK_GATE_DISABLE (1 << 23) +# define I965_IC_CLOCK_GATE_DISABLE (1 << 22) +# define I965_EU_CLOCK_GATE_DISABLE (1 << 21) +# define I965_IF_CLOCK_GATE_DISABLE (1 << 20) +# define I965_TC_CLOCK_GATE_DISABLE (1 << 19) +# define I965_SO_CLOCK_GATE_DISABLE (1 << 17) +# define I965_FBC_CLOCK_GATE_DISABLE (1 << 16) +# define I965_MARI_CLOCK_GATE_DISABLE (1 << 15) +# define I965_MASF_CLOCK_GATE_DISABLE (1 << 14) +# define I965_MAWB_CLOCK_GATE_DISABLE (1 << 13) +# define I965_EM_CLOCK_GATE_DISABLE (1 << 12) +# define I965_UC_CLOCK_GATE_DISABLE (1 << 11) +# define I965_SI_CLOCK_GATE_DISABLE (1 << 6) +# define I965_MT_CLOCK_GATE_DISABLE (1 << 5) +# define I965_PL_CLOCK_GATE_DISABLE (1 << 4) +# define I965_DG_CLOCK_GATE_DISABLE (1 << 3) +# define I965_QC_CLOCK_GATE_DISABLE (1 << 2) +# define I965_FT_CLOCK_GATE_DISABLE (1 << 1) +# define I965_DM_CLOCK_GATE_DISABLE (1 << 0) + +#define RENCLK_GATE_D2 0x6208 +#define VF_UNIT_CLOCK_GATE_DISABLE (1 << 9) +#define GS_UNIT_CLOCK_GATE_DISABLE (1 << 7) +#define CL_UNIT_CLOCK_GATE_DISABLE (1 << 6) +#define RAMCLK_GATE_D 0x6210 /* CRL only */ +#define DEUC 0x6214 /* CRL only */ + +/* + * This is a PCI config space register to manipulate backlight brightness + * It is used when the BLM_LEGACY_MODE is turned on. When enabled, the first + * byte of this config register sets brightness within the range from + * 0 to 0xff + */ +#define LEGACY_BACKLIGHT_BRIGHTNESS 0xf4 + +#define BLC_PWM_CTL 0x61254 +#define BACKLIGHT_MODULATION_FREQ_SHIFT (17) +#define BACKLIGHT_MODULATION_FREQ_SHIFT2 (16) +/* + * This is the most significant 15 bits of the number of backlight cycles in a + * complete cycle of the modulated backlight control. + * + * The actual value is this field multiplied by two. + */ +#define BACKLIGHT_MODULATION_FREQ_MASK (0x7fff << 17) +#define BACKLIGHT_MODULATION_FREQ_MASK2 (0xffff << 16) +#define BLM_LEGACY_MODE (1 << 16) + +/* + * This is the number of cycles out of the backlight modulation cycle for which + * the backlight is on. + * + * This field must be no greater than the number of cycles in the complete + * backlight modulation cycle. + */ +#define BACKLIGHT_DUTY_CYCLE_SHIFT (0) +#define BACKLIGHT_DUTY_CYCLE_MASK (0xffff) + +/* On 965+ backlight control is in another register */ +#define BLC_PWM_CTL2 0x61250 +#define BLM_LEGACY_MODE2 (1 << 30) + +#define BLM_CTL 0x61260 +#define BLM_THRESHOLD_0 0x61270 +#define BLM_THRESHOLD_1 0x61274 +#define BLM_THRESHOLD_2 0x61278 +#define BLM_THRESHOLD_3 0x6127c +#define BLM_THRESHOLD_4 0x61280 +#define BLM_THRESHOLD_5 0x61284 + +#define BLM_ACCUMULATOR_0 0x61290 +#define BLM_ACCUMULATOR_1 0x61294 +#define BLM_ACCUMULATOR_2 0x61298 +#define BLM_ACCUMULATOR_3 0x6129c +#define BLM_ACCUMULATOR_4 0x612a0 +#define BLM_ACCUMULATOR_5 0x612a4 + +#define FPA0 0x06040 +#define FPA1 0x06044 +#define FPB0 0x06048 +#define FPB1 0x0604c +# define FP_N_DIV_MASK 0x003f0000 +# define FP_N_IGD_DIV_MASK 0x00ff0000 +# define FP_N_DIV_SHIFT 16 +# define FP_M1_DIV_MASK 0x00003f00 +# define FP_M1_DIV_SHIFT 8 +# define FP_M2_DIV_MASK 0x0000003f +# define FP_M2_IGD_DIV_MASK 0x000000ff +# define FP_M2_DIV_SHIFT 0 + +#define PORT_HOTPLUG_EN 0x61110 +# define HDMIB_HOTPLUG_INT_EN (1 << 29) +# define HDMIC_HOTPLUG_INT_EN (1 << 28) +# define HDMID_HOTPLUG_INT_EN (1 << 27) +# define SDVOB_HOTPLUG_INT_EN (1 << 26) +# define SDVOC_HOTPLUG_INT_EN (1 << 25) +# define TV_HOTPLUG_INT_EN (1 << 18) +# define CRT_HOTPLUG_INT_EN (1 << 9) +# define CRT_HOTPLUG_ACTIVATION_PERIOD_32 (0 << 8) +/* must use period 64 on GM45 according to docs */ +# define CRT_HOTPLUG_ACTIVATION_PERIOD_64 (1 << 8) +# define CRT_HOTPLUG_DAC_ON_TIME_2M (0 << 7) +# define CRT_HOTPLUG_DAC_ON_TIME_4M (1 << 7) +# define CRT_HOTPLUG_VOLTAGE_COMPARE_40 (0 << 5) +# define CRT_HOTPLUG_VOLTAGE_COMPARE_50 (1 << 5) +# define CRT_HOTPLUG_VOLTAGE_COMPARE_60 (2 << 5) +# define CRT_HOTPLUG_VOLTAGE_COMPARE_70 (3 << 5) +# define CRT_HOTPLUG_VOLTAGE_COMPARE_MASK (3 << 5) +# define CRT_HOTPLUG_DETECT_DELAY_1G (0 << 4) +# define CRT_HOTPLUG_DETECT_DELAY_2G (1 << 4) +# define CRT_HOTPLUG_FORCE_DETECT (1 << 3) +# define CRT_HOTPLUG_DETECT_VOLTAGE_325MV (0 << 2) +# define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2) +# define CRT_HOTPLUG_MASK (0x3fc) /* Bits 9-2 */ + +#define PORT_HOTPLUG_STAT 0x61114 +# define HDMIB_HOTPLUG_INT_STATUS (1 << 29) +# define HDMIC_HOTPLUG_INT_STATUS (1 << 28) +# define HDMID_HOTPLUG_INT_STATUS (1 << 27) +# define CRT_HOTPLUG_INT_STATUS (1 << 11) +# define TV_HOTPLUG_INT_STATUS (1 << 10) +# define CRT_HOTPLUG_MONITOR_MASK (3 << 8) +# define CRT_HOTPLUG_MONITOR_COLOR (3 << 8) +# define CRT_HOTPLUG_MONITOR_MONO (2 << 8) +# define CRT_HOTPLUG_MONITOR_NONE (0 << 8) +# define SDVOC_HOTPLUG_INT_STATUS (1 << 7) +# define SDVOB_HOTPLUG_INT_STATUS (1 << 6) + +#define SDVOB 0x61140 +#define SDVOC 0x61160 +#define SDVO_ENABLE (1 << 31) +#define SDVO_PIPE_B_SELECT (1 << 30) +#define SDVO_STALL_SELECT (1 << 29) +#define SDVO_INTERRUPT_ENABLE (1 << 26) + +#define DISPLAY_HOTPLUG_CTL 0x61164 +/* + * 915G/GM SDVO pixel multiplier. + * + * Programmed value is multiplier - 1, up to 5x. + * + * \sa DPLL_MD_UDI_MULTIPLIER_MASK + */ +#define SDVO_PORT_MULTIPLY_MASK (7 << 23) +#define SDVO_PORT_MULTIPLY_SHIFT 23 +#define SDVO_PHASE_SELECT_MASK (15 << 19) +#define SDVO_PHASE_SELECT_DEFAULT (6 << 19) +#define SDVO_CLOCK_OUTPUT_INVERT (1 << 18) +#define SDVOC_GANG_MODE (1 << 16) +#define SDVO_ENCODING_SDVO (0x0 << 10) +#define SDVO_ENCODING_HDMI (0x2 << 10) +/* Requird for HDMI operation */ +#define SDVO_NULL_PACKETS_DURING_VSYNC (1 << 9) +#define SDVO_BORDER_ENABLE (1 << 7) +#define SDVO_AUDIO_ENABLE (1 << 6) +/* New with 965, default is to be set */ +#define SDVO_VSYNC_ACTIVE_HIGH (1 << 4) +/* New with 965, default is to be set */ +#define SDVO_HSYNC_ACTIVE_HIGH (1 << 3) +/* 915/945 only, read-only bit */ +#define SDVOB_PCIE_CONCURRENCY (1 << 3) +#define SDVO_DETECTED (1 << 2) +/* Bits to be preserved when writing */ +#define SDVOB_PRESERVE_MASK ((1 << 17) | (1 << 16) | (1 << 14)) +#define SDVOC_PRESERVE_MASK (1 << 17) + +#define UDIB_SVB_SHB_CODES 0x61144 +#define UDIB_SHA_BLANK_CODES 0x61148 +#define UDIB_START_END_FILL_CODES 0x6114c + + +#define SDVOUDI 0x61150 + +#define I830_HTOTAL_MASK 0xfff0000 +#define I830_HACTIVE_MASK 0x7ff + +#define I830_HBLANKEND_MASK 0xfff0000 +#define I830_HBLANKSTART_MASK 0xfff + +#define I830_HSYNCEND_MASK 0xfff0000 +#define I830_HSYNCSTART_MASK 0xfff + +#define I830_VTOTAL_MASK 0xfff0000 +#define I830_VACTIVE_MASK 0x7ff + +#define I830_VBLANKEND_MASK 0xfff0000 +#define I830_VBLANKSTART_MASK 0xfff + +#define I830_VSYNCEND_MASK 0xfff0000 +#define I830_VSYNCSTART_MASK 0xfff + +#define I830_PIPEA_HORZ_MASK 0x7ff0000 +#define I830_PIPEA_VERT_MASK 0x7ff + +#define ADPA 0x61100 +#define ADPA_DAC_ENABLE (1<<31) +#define ADPA_DAC_DISABLE 0 +#define ADPA_PIPE_SELECT_MASK (1<<30) +#define ADPA_PIPE_A_SELECT 0 +#define ADPA_PIPE_B_SELECT (1<<30) +#define ADPA_USE_VGA_HVPOLARITY (1<<15) +#define ADPA_SETS_HVPOLARITY 0 +#define ADPA_VSYNC_CNTL_DISABLE (1<<11) +#define ADPA_VSYNC_CNTL_ENABLE 0 +#define ADPA_HSYNC_CNTL_DISABLE (1<<10) +#define ADPA_HSYNC_CNTL_ENABLE 0 +#define ADPA_VSYNC_ACTIVE_HIGH (1<<4) +#define ADPA_VSYNC_ACTIVE_LOW 0 +#define ADPA_HSYNC_ACTIVE_HIGH (1<<3) +#define ADPA_HSYNC_ACTIVE_LOW 0 + +#define PCH_DSP_CHICKEN1 0x42000 +#define PCH_DSP_CHICKEN2 0x42004 +#define PCH_DSP_CHICKEN3 0x4200c +#define PCH_DSPCLK_GATE_D 0x42020 +#define PCH_DSPRAMCLK_GATE_D 0x42024 +#define PCH_3DCGDIS0 0x46020 +#define PCH_3DCGDIS1 0x46024 +#define PCH_3DRAMCGDIS0 0x46028 +#define SOUTH_DSPCLK_GATE_D 0xc2020 + +#define CPU_eDP_A 0x64000 +#define PCH_DP_B 0xe4100 +#define PCH_DP_C 0xe4200 +#define PCH_DP_D 0xe4300 + +#define DVOA 0x61120 +#define DVOB 0x61140 +#define DVOC 0x61160 +#define DVO_ENABLE (1 << 31) +#define DVO_PIPE_B_SELECT (1 << 30) +#define DVO_PIPE_STALL_UNUSED (0 << 28) +#define DVO_PIPE_STALL (1 << 28) +#define DVO_PIPE_STALL_TV (2 << 28) +#define DVO_PIPE_STALL_MASK (3 << 28) +#define DVO_USE_VGA_SYNC (1 << 15) +#define DVO_DATA_ORDER_I740 (0 << 14) +#define DVO_DATA_ORDER_FP (1 << 14) +#define DVO_VSYNC_DISABLE (1 << 11) +#define DVO_HSYNC_DISABLE (1 << 10) +#define DVO_VSYNC_TRISTATE (1 << 9) +#define DVO_HSYNC_TRISTATE (1 << 8) +#define DVO_BORDER_ENABLE (1 << 7) +#define DVO_DATA_ORDER_GBRG (1 << 6) +#define DVO_DATA_ORDER_RGGB (0 << 6) +#define DVO_DATA_ORDER_GBRG_ERRATA (0 << 6) +#define DVO_DATA_ORDER_RGGB_ERRATA (1 << 6) +#define DVO_VSYNC_ACTIVE_HIGH (1 << 4) +#define DVO_HSYNC_ACTIVE_HIGH (1 << 3) +#define DVO_BLANK_ACTIVE_HIGH (1 << 2) +#define DVO_OUTPUT_CSTATE_PIXELS (1 << 1) /* SDG only */ +#define DVO_OUTPUT_SOURCE_SIZE_PIXELS (1 << 0) /* SDG only */ +#define DVO_PRESERVE_MASK (0x7<<24) + +#define DVOA_SRCDIM 0x61124 +#define DVOB_SRCDIM 0x61144 +#define DVOC_SRCDIM 0x61164 +#define DVO_SRCDIM_HORIZONTAL_SHIFT 12 +#define DVO_SRCDIM_VERTICAL_SHIFT 0 + +/* @defgroup LVDS + * @{ + */ +/* + * This register controls the LVDS output enable, pipe selection, and data + * format selection. + * + * All of the clock/data pairs are force powered down by power sequencing. + */ +#define LVDS 0x61180 +/* + * Enables the LVDS port. This bit must be set before DPLLs are enabled, as + * the DPLL semantics change when the LVDS is assigned to that pipe. + */ +# define LVDS_PORT_EN (1 << 31) +/* Selects pipe B for LVDS data. Must be set on pre-965. */ +# define LVDS_PIPEB_SELECT (1 << 30) + +/* on 965, dithering is enabled in this register, not PFIT_CONTROL */ +# define LVDS_DITHER_ENABLE (1 << 25) + +/* + * Selects between .0 and .1 formats: + * + * 0 = 1x18.0, 2x18.0, 1x24.0 or 2x24.0 + * 1 = 1x24.1 or 2x24.1 + */ +# define LVDS_DATA_FORMAT_DOT_ONE (1 << 24) + +/* Using LE instead of HS on second channel control signal */ +# define LVDS_LE_CONTROL_ENABLE (1 << 23) + +/* Using LF instead of VS on second channel control signal */ +# define LVDS_LF_CONTROL_ENABLE (1 << 22) + +/* invert vsync signal polarity */ +# define LVDS_VSYNC_POLARITY_INVERT (1 << 21) + +/* invert hsync signal polarity */ +# define LVDS_HSYNC_POLARITY_INVERT (1 << 20) + +/* invert display enable signal polarity */ +# define LVDS_DE_POLARITY_INVERT (1 << 19) + +/* + * Control signals for second channel, ignored in single channel modes + */ + +/* send DE, HS, VS on second channel */ +# define LVDS_SECOND_CHANNEL_DE_HS_VS (0 << 17) + +# define LVDS_SECOND_CHANNEL_RESERVED (1 << 17) + +/* Send zeros instead of DE, HS, VS on second channel */ +# define LVDS_SECOND_CHANNEL_ZEROS (2 << 17) + +/* Set DE=0, HS=LE, VS=LF on second channel */ +# define LVDS_SECOND_CHANNEL_HS_VS (3 << 17) + +/* + * Send duplicate data for channel reserved bits, otherwise send zeros + */ +# define LVDS_CHANNEL_DUP_RESERVED (1 << 16) + +/* + * Enable border for unscaled (or aspect-scaled) display + */ +# define LVDS_BORDER_ENABLE (1 << 15) + +/* + * Tri-state the LVDS buffers when powered down, otherwise + * they are set to 0V + */ +# define LVDS_POWER_DOWN_TRI_STATE (1 << 10) + +/* + * Enables the A0-A2 data pairs and CLKA, containing 18 bits of color data per + * pixel. + */ +# define LVDS_A0A2_CLKA_POWER_MASK (3 << 8) +# define LVDS_A0A2_CLKA_POWER_DOWN (0 << 8) +# define LVDS_A0A2_CLKA_POWER_UP (3 << 8) +/* + * Controls the A3 data pair, which contains the additional LSBs for 24 bit + * mode. Only enabled if LVDS_A0A2_CLKA_POWER_UP also indicates it should be + * on. + */ +# define LVDS_A3_POWER_MASK (3 << 6) +# define LVDS_A3_POWER_DOWN (0 << 6) +# define LVDS_A3_POWER_UP (3 << 6) +/* + * Controls the CLKB pair. This should only be set when LVDS_B0B3_POWER_UP + * is set. + */ +# define LVDS_CLKB_POWER_MASK (3 << 4) +# define LVDS_CLKB_POWER_DOWN (0 << 4) +# define LVDS_CLKB_POWER_UP (3 << 4) + +/* + * Controls the B0-B3 data pairs. This must be set to match the DPLL p2 + * setting for whether we are in dual-channel mode. The B3 pair will + * additionally only be powered up when LVDS_A3_POWER_UP is set. + */ +# define LVDS_B0B3_POWER_MASK (3 << 2) +# define LVDS_B0B3_POWER_DOWN (0 << 2) +# define LVDS_B0B3_POWER_UP (3 << 2) + +/* @} */ + +#define DP_B 0x64100 +#define DPB_AUX_CH_CTL 0x64110 +#define DPB_AUX_CH_DATA1 0x64114 +#define DPB_AUX_CH_DATA2 0x64118 +#define DPB_AUX_CH_DATA3 0x6411c +#define DPB_AUX_CH_DATA4 0x64120 +#define DPB_AUX_CH_DATA5 0x64124 + +#define DP_C 0x64200 +#define DPC_AUX_CH_CTL 0x64210 +#define DPC_AUX_CH_DATA1 0x64214 +#define DPC_AUX_CH_DATA2 0x64218 +#define DPC_AUX_CH_DATA3 0x6421c +#define DPC_AUX_CH_DATA4 0x64220 +#define DPC_AUX_CH_DATA5 0x64224 + +#define DP_D 0x64300 +#define DPD_AUX_CH_CTL 0x64310 +#define DPD_AUX_CH_DATA1 0x64314 +#define DPD_AUX_CH_DATA2 0x64318 +#define DPD_AUX_CH_DATA3 0x6431c +#define DPD_AUX_CH_DATA4 0x64320 +#define DPD_AUX_CH_DATA5 0x64324 + +/* + * Two channel clock control. Turn this on if you need clkb for two channel mode + * Overridden by global LVDS power sequencing + */ + +/* clkb off */ +# define LVDS_CLKB_POWER_DOWN (0 << 4) + +/* powered up, but clkb forced to 0 */ +# define LVDS_CLKB_POWER_PARTIAL (1 << 4) + +/* clock B running */ +# define LVDS_CLKB_POWER_UP (3 << 4) + +/* + * Two channel mode B0-B2 control. Sets state when power is on. + * Set to POWER_DOWN in single channel mode, other settings enable + * two channel mode. The CLKB power control controls whether that clock + * is enabled during two channel mode. + * + */ +/* Everything is off, including B3 and CLKB */ +# define LVDS_B_POWER_DOWN (0 << 2) + +/* B0, B1, B2 and data lines forced to 0. timing is active */ +# define LVDS_B_POWER_PARTIAL (1 << 2) + +/* data lines active (both timing and colour) */ +# define LVDS_B_POWER_UP (3 << 2) + +/* @defgroup TV_CTL + * @{ + */ +#define TV_CTL 0x68000 +/* Enables the TV encoder */ +# define TV_ENC_ENABLE (1 << 31) +/* Sources the TV encoder input from pipe B instead of A. */ +# define TV_ENC_PIPEB_SELECT (1 << 30) +/* Outputs composite video (DAC A only) */ +# define TV_ENC_OUTPUT_COMPOSITE (0 << 28) +/* Outputs SVideo video (DAC B/C) */ +# define TV_ENC_OUTPUT_SVIDEO (1 << 28) +/* Outputs Component video (DAC A/B/C) */ +# define TV_ENC_OUTPUT_COMPONENT (2 << 28) +/* Outputs Composite and SVideo (DAC A/B/C) */ +# define TV_ENC_OUTPUT_SVIDEO_COMPOSITE (3 << 28) +# define TV_TRILEVEL_SYNC (1 << 21) +/* Enables slow sync generation (945GM only) */ +# define TV_SLOW_SYNC (1 << 20) +/* Selects 4x oversampling for 480i and 576p */ +# define TV_OVERSAMPLE_4X (0 << 18) +/* Selects 2x oversampling for 720p and 1080i */ +# define TV_OVERSAMPLE_2X (1 << 18) +/* Selects no oversampling for 1080p */ +# define TV_OVERSAMPLE_NONE (2 << 18) +/* Selects 8x oversampling */ +# define TV_OVERSAMPLE_8X (3 << 18) +/* Selects progressive mode rather than interlaced */ +# define TV_PROGRESSIVE (1 << 17) +/* Sets the colorburst to PAL mode. Required for non-M PAL modes. */ +# define TV_PAL_BURST (1 << 16) +/* Field for setting delay of Y compared to C */ +# define TV_YC_SKEW_MASK (7 << 12) +/* Enables a fix for 480p/576p standard definition modes on the 915GM only */ +# define TV_ENC_SDP_FIX (1 << 11) +/* + * Enables a fix for the 915GM only. + * + * Not sure what it does. + */ +# define TV_ENC_C0_FIX (1 << 10) +/* Bits that must be preserved by software */ +# define TV_CTL_SAVE ((1 << 11) | (3 << 9) | (7 << 6) | 0xf) +# define TV_FUSE_STATE_MASK (3 << 4) +/* Read-only state that reports all features enabled */ +# define TV_FUSE_STATE_ENABLED (0 << 4) +/* Read-only state that reports that Macrovision is disabled in hardware*/ +# define TV_FUSE_STATE_NO_MACROVISION (1 << 4) +/* Read-only state that reports that TV-out is disabled in hardware. */ +# define TV_FUSE_STATE_DISABLED (2 << 4) +/* Normal operation */ +# define TV_TEST_MODE_NORMAL (0 << 0) +/* Encoder test pattern 1 - combo pattern */ +# define TV_TEST_MODE_PATTERN_1 (1 << 0) +/* Encoder test pattern 2 - full screen vertical 75% color bars */ +# define TV_TEST_MODE_PATTERN_2 (2 << 0) +/* Encoder test pattern 3 - full screen horizontal 75% color bars */ +# define TV_TEST_MODE_PATTERN_3 (3 << 0) +/* Encoder test pattern 4 - random noise */ +# define TV_TEST_MODE_PATTERN_4 (4 << 0) +/* Encoder test pattern 5 - linear color ramps */ +# define TV_TEST_MODE_PATTERN_5 (5 << 0) +/* + * This test mode forces the DACs to 50% of full output. + * + * This is used for load detection in combination with TVDAC_SENSE_MASK + */ +# define TV_TEST_MODE_MONITOR_DETECT (7 << 0) +# define TV_TEST_MODE_MASK (7 << 0) +/* @} */ + +/* @defgroup TV_DAC + * @{ + */ +#define TV_DAC 0x68004 +/* + * Reports that DAC state change logic has reported change (RO). + * + * This gets cleared when TV_DAC_STATE_EN is cleared +*/ +# define TVDAC_STATE_CHG (1 << 31) +# define TVDAC_SENSE_MASK (7 << 28) +/* Reports that DAC A voltage is above the detect threshold */ +# define TVDAC_A_SENSE (1 << 30) +/* Reports that DAC B voltage is above the detect threshold */ +# define TVDAC_B_SENSE (1 << 29) +/* Reports that DAC C voltage is above the detect threshold */ +# define TVDAC_C_SENSE (1 << 28) +/* + * Enables DAC state detection logic, for load-based TV detection. + * + * The PLL of the chosen pipe (in TV_CTL) must be running, and the encoder set + * to off, for load detection to work. + */ +# define TVDAC_STATE_CHG_EN (1 << 27) +/* Sets the DAC A sense value to high */ +# define TVDAC_A_SENSE_CTL (1 << 26) +/* Sets the DAC B sense value to high */ +# define TVDAC_B_SENSE_CTL (1 << 25) +/* Sets the DAC C sense value to high */ +# define TVDAC_C_SENSE_CTL (1 << 24) +/* Overrides the ENC_ENABLE and DAC voltage levels */ +# define DAC_CTL_OVERRIDE (1 << 7) +/* Sets the slew rate. Must be preserved in software */ +# define ENC_TVDAC_SLEW_FAST (1 << 6) +# define DAC_A_1_3_V (0 << 4) +# define DAC_A_1_1_V (1 << 4) +# define DAC_A_0_7_V (2 << 4) +# define DAC_A_OFF (3 << 4) +# define DAC_B_1_3_V (0 << 2) +# define DAC_B_1_1_V (1 << 2) +# define DAC_B_0_7_V (2 << 2) +# define DAC_B_OFF (3 << 2) +# define DAC_C_1_3_V (0 << 0) +# define DAC_C_1_1_V (1 << 0) +# define DAC_C_0_7_V (2 << 0) +# define DAC_C_OFF (3 << 0) +/* @} */ + +/* + * CSC coefficients are stored in a floating point format with 9 bits of + * mantissa and 2 or 3 bits of exponent. The exponent is represented as 2*-n, + * where 2-bit exponents are unsigned n, and 3-bit exponents are signed n with + * -1 (0x3) being the only legal negative value. + */ +#define TV_CSC_Y 0x68010 +# define TV_RY_MASK 0x07ff0000 +# define TV_RY_SHIFT 16 +# define TV_GY_MASK 0x00000fff +# define TV_GY_SHIFT 0 + +#define TV_CSC_Y2 0x68014 +# define TV_BY_MASK 0x07ff0000 +# define TV_BY_SHIFT 16 +/* + * Y attenuation for component video. + * + * Stored in 1.9 fixed point. + */ +# define TV_AY_MASK 0x000003ff +# define TV_AY_SHIFT 0 + +#define TV_CSC_U 0x68018 +# define TV_RU_MASK 0x07ff0000 +# define TV_RU_SHIFT 16 +# define TV_GU_MASK 0x000007ff +# define TV_GU_SHIFT 0 + +#define TV_CSC_U2 0x6801c +# define TV_BU_MASK 0x07ff0000 +# define TV_BU_SHIFT 16 +/* + * U attenuation for component video. + * + * Stored in 1.9 fixed point. + */ +# define TV_AU_MASK 0x000003ff +# define TV_AU_SHIFT 0 + +#define TV_CSC_V 0x68020 +# define TV_RV_MASK 0x0fff0000 +# define TV_RV_SHIFT 16 +# define TV_GV_MASK 0x000007ff +# define TV_GV_SHIFT 0 + +#define TV_CSC_V2 0x68024 +# define TV_BV_MASK 0x07ff0000 +# define TV_BV_SHIFT 16 +/* + * V attenuation for component video. + * + * Stored in 1.9 fixed point. + */ +# define TV_AV_MASK 0x000007ff +# define TV_AV_SHIFT 0 + +/* @defgroup TV_CSC_KNOBS + * @{ + */ +#define TV_CLR_KNOBS 0x68028 +/* 2s-complement brightness adjustment */ +# define TV_BRIGHTNESS_MASK 0xff000000 +# define TV_BRIGHTNESS_SHIFT 24 +/* Contrast adjustment, as a 2.6 unsigned floating point number */ +# define TV_CONTRAST_MASK 0x00ff0000 +# define TV_CONTRAST_SHIFT 16 +/* Saturation adjustment, as a 2.6 unsigned floating point number */ +# define TV_SATURATION_MASK 0x0000ff00 +# define TV_SATURATION_SHIFT 8 +/* Hue adjustment, as an integer phase angle in degrees */ +# define TV_HUE_MASK 0x000000ff +# define TV_HUE_SHIFT 0 +/* @} */ + +/* @defgroup TV_CLR_LEVEL + * @{ + */ +#define TV_CLR_LEVEL 0x6802c +/* Controls the DAC level for black */ +# define TV_BLACK_LEVEL_MASK 0x01ff0000 +# define TV_BLACK_LEVEL_SHIFT 16 +/* Controls the DAC level for blanking */ +# define TV_BLANK_LEVEL_MASK 0x000001ff +# define TV_BLANK_LEVEL_SHIFT 0 +/* @} */ + +/* @defgroup TV_H_CTL_1 + * @{ + */ +#define TV_H_CTL_1 0x68030 +/* Number of pixels in the hsync. */ +# define TV_HSYNC_END_MASK 0x1fff0000 +# define TV_HSYNC_END_SHIFT 16 +/* Total number of pixels minus one in the line (display and blanking). */ +# define TV_HTOTAL_MASK 0x00001fff +# define TV_HTOTAL_SHIFT 0 +/* @} */ + +/* @defgroup TV_H_CTL_2 + * @{ + */ +#define TV_H_CTL_2 0x68034 +/* Enables the colorburst (needed for non-component color) */ +# define TV_BURST_ENA (1 << 31) +/* Offset of the colorburst from the start of hsync, in pixels minus one. */ +# define TV_HBURST_START_SHIFT 16 +# define TV_HBURST_START_MASK 0x1fff0000 +/* Length of the colorburst */ +# define TV_HBURST_LEN_SHIFT 0 +# define TV_HBURST_LEN_MASK 0x0001fff +/* @} */ + +/* @defgroup TV_H_CTL_3 + * @{ + */ +#define TV_H_CTL_3 0x68038 +/* End of hblank, measured in pixels minus one from start of hsync */ +# define TV_HBLANK_END_SHIFT 16 +# define TV_HBLANK_END_MASK 0x1fff0000 +/* Start of hblank, measured in pixels minus one from start of hsync */ +# define TV_HBLANK_START_SHIFT 0 +# define TV_HBLANK_START_MASK 0x0001fff +/* @} */ + +/* @defgroup TV_V_CTL_1 + * @{ + */ +#define TV_V_CTL_1 0x6803c +/* XXX */ +# define TV_NBR_END_SHIFT 16 +# define TV_NBR_END_MASK 0x07ff0000 +/* XXX */ +# define TV_VI_END_F1_SHIFT 8 +# define TV_VI_END_F1_MASK 0x00003f00 +/* XXX */ +# define TV_VI_END_F2_SHIFT 0 +# define TV_VI_END_F2_MASK 0x0000003f +/* @} */ + +/* @defgroup TV_V_CTL_2 + * @{ + */ +#define TV_V_CTL_2 0x68040 +/* Length of vsync, in half lines */ +# define TV_VSYNC_LEN_MASK 0x07ff0000 +# define TV_VSYNC_LEN_SHIFT 16 +/* Offset of the start of vsync in field 1, measured in one less than the + * number of half lines. + */ +# define TV_VSYNC_START_F1_MASK 0x00007f00 +# define TV_VSYNC_START_F1_SHIFT 8 +/* + * Offset of the start of vsync in field 2, measured in one less than the + * number of half lines. + */ +# define TV_VSYNC_START_F2_MASK 0x0000007f +# define TV_VSYNC_START_F2_SHIFT 0 +/* @} */ + +/* @defgroup TV_V_CTL_3 + * @{ + */ +#define TV_V_CTL_3 0x68044 +/* Enables generation of the equalization signal */ +# define TV_EQUAL_ENA (1 << 31) +/* Length of vsync, in half lines */ +# define TV_VEQ_LEN_MASK 0x007f0000 +# define TV_VEQ_LEN_SHIFT 16 +/* Offset of the start of equalization in field 1, measured in one less than + * the number of half lines. + */ +# define TV_VEQ_START_F1_MASK 0x0007f00 +# define TV_VEQ_START_F1_SHIFT 8 +/* + * Offset of the start of equalization in field 2, measured in one less than + * the number of half lines. + */ +# define TV_VEQ_START_F2_MASK 0x000007f +# define TV_VEQ_START_F2_SHIFT 0 +/* @} */ + +/* @defgroup TV_V_CTL_4 + * @{ + */ +#define TV_V_CTL_4 0x68048 +/* + * Offset to start of vertical colorburst, measured in one less than the + * number of lines from vertical start. + */ +# define TV_VBURST_START_F1_MASK 0x003f0000 +# define TV_VBURST_START_F1_SHIFT 16 +/* + * Offset to the end of vertical colorburst, measured in one less than the + * number of lines from the start of NBR. + */ +# define TV_VBURST_END_F1_MASK 0x000000ff +# define TV_VBURST_END_F1_SHIFT 0 +/* @} */ + +/* @defgroup TV_V_CTL_5 + * @{ + */ +#define TV_V_CTL_5 0x6804c +/* + * Offset to start of vertical colorburst, measured in one less than the + * number of lines from vertical start. + */ +# define TV_VBURST_START_F2_MASK 0x003f0000 +# define TV_VBURST_START_F2_SHIFT 16 +/* + * Offset to the end of vertical colorburst, measured in one less than the + * number of lines from the start of NBR. + */ +# define TV_VBURST_END_F2_MASK 0x000000ff +# define TV_VBURST_END_F2_SHIFT 0 +/* @} */ + +/* @defgroup TV_V_CTL_6 + * @{ + */ +#define TV_V_CTL_6 0x68050 +/* + * Offset to start of vertical colorburst, measured in one less than the + * number of lines from vertical start. + */ +# define TV_VBURST_START_F3_MASK 0x003f0000 +# define TV_VBURST_START_F3_SHIFT 16 +/* + * Offset to the end of vertical colorburst, measured in one less than the + * number of lines from the start of NBR. + */ +# define TV_VBURST_END_F3_MASK 0x000000ff +# define TV_VBURST_END_F3_SHIFT 0 +/* @} */ + +/* @defgroup TV_V_CTL_7 + * @{ + */ +#define TV_V_CTL_7 0x68054 +/* + * Offset to start of vertical colorburst, measured in one less than the + * number of lines from vertical start. + */ +# define TV_VBURST_START_F4_MASK 0x003f0000 +# define TV_VBURST_START_F4_SHIFT 16 +/* + * Offset to the end of vertical colorburst, measured in one less than the + * number of lines from the start of NBR. + */ +# define TV_VBURST_END_F4_MASK 0x000000ff +# define TV_VBURST_END_F4_SHIFT 0 +/* @} */ + +/* @defgroup TV_SC_CTL_1 + * @{ + */ +#define TV_SC_CTL_1 0x68060 +/* Turns on the first subcarrier phase generation DDA */ +# define TV_SC_DDA1_EN (1 << 31) +/* Turns on the first subcarrier phase generation DDA */ +# define TV_SC_DDA2_EN (1 << 30) +/* Turns on the first subcarrier phase generation DDA */ +# define TV_SC_DDA3_EN (1 << 29) +/* Sets the subcarrier DDA to reset frequency every other field */ +# define TV_SC_RESET_EVERY_2 (0 << 24) +/* Sets the subcarrier DDA to reset frequency every fourth field */ +# define TV_SC_RESET_EVERY_4 (1 << 24) +/* Sets the subcarrier DDA to reset frequency every eighth field */ +# define TV_SC_RESET_EVERY_8 (2 << 24) +/* Sets the subcarrier DDA to never reset the frequency */ +# define TV_SC_RESET_NEVER (3 << 24) +/* Sets the peak amplitude of the colorburst.*/ +# define TV_BURST_LEVEL_MASK 0x00ff0000 +# define TV_BURST_LEVEL_SHIFT 16 +/* Sets the increment of the first subcarrier phase generation DDA */ +# define TV_SCDDA1_INC_MASK 0x00000fff +# define TV_SCDDA1_INC_SHIFT 0 +/* @} */ + +/* @defgroup TV_SC_CTL_2 + * @{ + */ +#define TV_SC_CTL_2 0x68064 +/* Sets the rollover for the second subcarrier phase generation DDA */ +# define TV_SCDDA2_SIZE_MASK 0x7fff0000 +# define TV_SCDDA2_SIZE_SHIFT 16 +/* Sets the increent of the second subcarrier phase generation DDA */ +# define TV_SCDDA2_INC_MASK 0x00007fff +# define TV_SCDDA2_INC_SHIFT 0 +/* @} */ + +/* @defgroup TV_SC_CTL_3 + * @{ + */ +#define TV_SC_CTL_3 0x68068 +/* Sets the rollover for the third subcarrier phase generation DDA */ +# define TV_SCDDA3_SIZE_MASK 0x7fff0000 +# define TV_SCDDA3_SIZE_SHIFT 16 +/* Sets the increent of the third subcarrier phase generation DDA */ +# define TV_SCDDA3_INC_MASK 0x00007fff +# define TV_SCDDA3_INC_SHIFT 0 +/* @} */ + +/* @defgroup TV_WIN_POS + * @{ + */ +#define TV_WIN_POS 0x68070 +/* X coordinate of the display from the start of horizontal active */ +# define TV_XPOS_MASK 0x1fff0000 +# define TV_XPOS_SHIFT 16 +/* Y coordinate of the display from the start of vertical active (NBR) */ +# define TV_YPOS_MASK 0x00000fff +# define TV_YPOS_SHIFT 0 +/* @} */ + +/* @defgroup TV_WIN_SIZE + * @{ + */ +#define TV_WIN_SIZE 0x68074 +/* Horizontal size of the display window, measured in pixels*/ +# define TV_XSIZE_MASK 0x1fff0000 +# define TV_XSIZE_SHIFT 16 +/* + * Vertical size of the display window, measured in pixels. + * + * Must be even for interlaced modes. + */ +# define TV_YSIZE_MASK 0x00000fff +# define TV_YSIZE_SHIFT 0 +/* @} */ + +/* @defgroup TV_FILTER_CTL_1 + * @{ + */ +#define TV_FILTER_CTL_1 0x68080 +/* + * Enables automatic scaling calculation. + * + * If set, the rest of the registers are ignored, and the calculated values can + * be read back from the register. + */ +# define TV_AUTO_SCALE (1 << 31) +/* + * Disables the vertical filter. + * + * This is required on modes more than 1024 pixels wide */ +# define TV_V_FILTER_BYPASS (1 << 29) +/* Enables adaptive vertical filtering */ +# define TV_VADAPT (1 << 28) +# define TV_VADAPT_MODE_MASK (3 << 26) +/* Selects the least adaptive vertical filtering mode */ +# define TV_VADAPT_MODE_LEAST (0 << 26) +/* Selects the moderately adaptive vertical filtering mode */ +# define TV_VADAPT_MODE_MODERATE (1 << 26) +/* Selects the most adaptive vertical filtering mode */ +# define TV_VADAPT_MODE_MOST (3 << 26) +/* + * Sets the horizontal scaling factor. + * + * This should be the fractional part of the horizontal scaling factor divided + * by the oversampling rate. TV_HSCALE should be less than 1, and set to: + * + * (src width - 1) / ((oversample * dest width) - 1) + */ +# define TV_HSCALE_FRAC_MASK 0x00003fff +# define TV_HSCALE_FRAC_SHIFT 0 +/* @} */ + +/* @defgroup TV_FILTER_CTL_2 + * @{ + */ +#define TV_FILTER_CTL_2 0x68084 +/* + * Sets the integer part of the 3.15 fixed-point vertical scaling factor. + * + * TV_VSCALE should be (src height - 1) / ((interlace * dest height) - 1) + */ +# define TV_VSCALE_INT_MASK 0x00038000 +# define TV_VSCALE_INT_SHIFT 15 +/* + * Sets the fractional part of the 3.15 fixed-point vertical scaling factor. + * + * \sa TV_VSCALE_INT_MASK + */ +# define TV_VSCALE_FRAC_MASK 0x00007fff +# define TV_VSCALE_FRAC_SHIFT 0 +/* @} */ + +/* @defgroup TV_FILTER_CTL_3 + * @{ + */ +#define TV_FILTER_CTL_3 0x68088 +/* + * Sets the integer part of the 3.15 fixed-point vertical scaling factor. + * + * TV_VSCALE should be (src height - 1) / (1/4 * (dest height - 1)) + * + * For progressive modes, TV_VSCALE_IP_INT should be set to zeroes. + */ +# define TV_VSCALE_IP_INT_MASK 0x00038000 +# define TV_VSCALE_IP_INT_SHIFT 15 +/* + * Sets the fractional part of the 3.15 fixed-point vertical scaling factor. + * + * For progressive modes, TV_VSCALE_IP_INT should be set to zeroes. + * + * \sa TV_VSCALE_IP_INT_MASK + */ +# define TV_VSCALE_IP_FRAC_MASK 0x00007fff +# define TV_VSCALE_IP_FRAC_SHIFT 0 +/* @} */ + +/* @defgroup TV_CC_CONTROL + * @{ + */ +#define TV_CC_CONTROL 0x68090 +# define TV_CC_ENABLE (1 << 31) +/* + * Specifies which field to send the CC data in. + * + * CC data is usually sent in field 0. + */ +# define TV_CC_FID_MASK (1 << 27) +# define TV_CC_FID_SHIFT 27 +/* Sets the horizontal position of the CC data. Usually 135. */ +# define TV_CC_HOFF_MASK 0x03ff0000 +# define TV_CC_HOFF_SHIFT 16 +/* Sets the vertical position of the CC data. Usually 21 */ +# define TV_CC_LINE_MASK 0x0000003f +# define TV_CC_LINE_SHIFT 0 +/* @} */ + +/* @defgroup TV_CC_DATA + * @{ + */ +#define TV_CC_DATA 0x68094 +# define TV_CC_RDY (1 << 31) +/* Second word of CC data to be transmitted. */ +# define TV_CC_DATA_2_MASK 0x007f0000 +# define TV_CC_DATA_2_SHIFT 16 +/* First word of CC data to be transmitted. */ +# define TV_CC_DATA_1_MASK 0x0000007f +# define TV_CC_DATA_1_SHIFT 0 +/* @} + */ + +/* @{ */ +#define TV_H_LUMA_0 0x68100 +#define TV_H_LUMA_59 0x681ec +#define TV_H_CHROMA_0 0x68200 +#define TV_H_CHROMA_59 0x682ec +#define TV_V_LUMA_0 0x68300 +#define TV_V_LUMA_42 0x683a8 +#define TV_V_CHROMA_0 0x68400 +#define TV_V_CHROMA_42 0x684a8 +/* @} */ + +#define PIPEA_DSL 0x70000 + +#define PIPEACONF 0x70008 +#define PIPEACONF_ENABLE (1<<31) +#define PIPEACONF_DISABLE 0 +#define PIPEACONF_DOUBLE_WIDE (1<<30) +#define I965_PIPECONF_ACTIVE (1<<30) +#define PIPEACONF_SINGLE_WIDE 0 +#define PIPEACONF_PIPE_UNLOCKED 0 +#define PIPEACONF_PIPE_LOCKED (1<<25) +#define PIPEACONF_PALETTE 0 +#define PIPEACONF_GAMMA (1<<24) +#define PIPECONF_FORCE_BORDER (1<<25) +#define PIPECONF_PROGRESSIVE (0 << 21) +#define PIPECONF_INTERLACE_W_FIELD_INDICATION (6 << 21) +#define PIPECONF_INTERLACE_FIELD_0_ONLY (7 << 21) +/* ironlake: gamma */ +#define PIPECONF_PALETTE_8BIT (0<<24) +#define PIPECONF_PALETTE_10BIT (1<<24) +#define PIPECONF_PALETTE_12BIT (2<<24) +#define PIPECONF_FORCE_BORDER (1<<25) +#define PIPECONF_PROGRESSIVE (0 << 21) +#define PIPECONF_INTERLACE_W_FIELD_INDICATION (6 << 21) +#define PIPECONF_INTERLACE_FIELD_0_ONLY (7 << 21) +/* ironlake */ +#define PIPECONF_MSA_TIMING_DELAY (0<<18) /* for eDP */ +#define PIPECONF_NO_DYNAMIC_RATE_CHANGE (0 << 16) +#define PIPECONF_NO_ROTATION (0<<14) +#define PIPECONF_FULL_COLOR_RANGE (0<<13) +#define PIPECONF_CE_COLOR_RANGE (1<<13) +#define PIPECONF_COLOR_SPACE_RGB (0<<11) +#define PIPECONF_COLOR_SPACE_YUV601 (1<<11) +#define PIPECONF_COLOR_SPACE_YUV709 (2<<11) +#define PIPECONF_CONNECT_DEFAULT (0<<9) +#define PIPECONF_8BPP (0<<5) +#define PIPECONF_10BPP (1<<5) +#define PIPECONF_6BPP (2<<5) +#define PIPECONF_12BPP (3<<5) +#define PIPECONF_ENABLE_DITHER (1<<4) +#define PIPECONF_DITHER_SPATIAL (0<<2) +#define PIPECONF_DITHER_ST1 (1<<2) +#define PIPECONF_DITHER_ST2 (2<<2) +#define PIPECONF_DITHER_TEMPORAL (3<<2) + +#define PIPEAGCMAXRED 0x70010 +#define PIPEAGCMAXGREEN 0x70014 +#define PIPEAGCMAXBLUE 0x70018 +#define PIPEASTAT 0x70024 +# define FIFO_UNDERRUN (1 << 31) +# define CRC_ERROR_ENABLE (1 << 29) +# define CRC_DONE_ENABLE (1 << 28) +# define GMBUS_EVENT_ENABLE (1 << 27) +# define VSYNC_INT_ENABLE (1 << 25) +# define DLINE_COMPARE_ENABLE (1 << 24) +# define DPST_EVENT_ENABLE (1 << 23) +# define LBLC_EVENT_ENABLE (1 << 22) +# define OFIELD_INT_ENABLE (1 << 21) +# define EFIELD_INT_ENABLE (1 << 20) +# define SVBLANK_INT_ENABLE (1 << 18) +# define VBLANK_INT_ENABLE (1 << 17) +# define OREG_UPDATE_ENABLE (1 << 16) +# define CRC_ERROR_INT_STATUS (1 << 13) +# define CRC_DONE_INT_STATUS (1 << 12) +# define GMBUS_INT_STATUS (1 << 11) +# define VSYNC_INT_STATUS (1 << 9) +# define DLINE_COMPARE_STATUS (1 << 8) +# define DPST_EVENT_STATUS (1 << 7) +# define LBLC_EVENT_STATUS (1 << 6) +# define OFIELD_INT_STATUS (1 << 5) +# define EFIELD_INT_STATUS (1 << 4) +# define SVBLANK_INT_STATUS (1 << 2) +# define VBLANK_INT_STATUS (1 << 1) +# define OREG_UPDATE_STATUS (1 << 0) + +#define FW_BLC 0x020d8 +#define FW_BLC2 0x020dc +#define FW_BLC_SELF 0x020e0 /* 915+ only */ + +#define DSPARB 0x70030 +#define DSPARB_CSTART_SHIFT 7 +#define DSPARB_BSTART_SHIFT 0 +#define DSPARB_BEND_SHIFT 9 /* on 855 */ +#define DSPARB_AEND_SHIFT 0 +#define DSPFW1 0x70034 +#define DSPFW2 0x70038 +#define DSPFW3 0x7003c +/* + * The two pipe frame counter registers are not synchronized, so + * reading a stable value is somewhat tricky. The following code + * should work: + * + * do { + * high1 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >> PIPE_FRAME_HIGH_SHIFT; + * low1 = ((INREG(PIPEAFRAMEPIXEL) & PIPE_FRAME_LOW_MASK) >> PIPE_FRAME_LOW_SHIFT); + * high2 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >> PIPE_FRAME_HIGH_SHIFT); + * } while (high1 != high2); + * frame = (high1 << 8) | low1; + */ +#define PIPEAFRAMEHIGH 0x70040 +#define PIPE_FRAME_HIGH_MASK 0x0000ffff +#define PIPE_FRAME_HIGH_SHIFT 0 +#define PIPEAFRAMEPIXEL 0x70044 +#define PIPE_FRAME_LOW_MASK 0xff000000 +#define PIPE_FRAME_LOW_SHIFT 24 +/* + * Pixel within the current frame is counted in the PIPEAFRAMEPIXEL register + * and is 24 bits wide. + */ +#define PIPE_PIXEL_MASK 0x00ffffff +#define PIPE_PIXEL_SHIFT 0 +/* + * g4x+ frame/flip counters + */ +#define PIPEAFRMCOUNT_G4X 0x70040 +#define PIPEAFLIPCOUNT_G4X 0x70044 +/* + * Computing GMCH M and N values. + * + * GMCH M/N = dot clock * bytes per pixel / ls_clk * # of lanes + * + * ls_clk (we assume) is the DP link clock (1.62 or 2.7 GHz) + * + * The GMCH value is used internally + */ +#define PIPEA_GMCH_DATA_M 0x70050 + +/* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */ +#define PIPE_GMCH_DATA_M_TU_SIZE_MASK (0x3f << 25) +#define PIPE_GMCH_DATA_M_TU_SIZE_SHIFT 25 + +#define PIPE_GMCH_DATA_M_MASK (0xffffff) + +#define PIPEA_GMCH_DATA_N 0x70054 +#define PIPE_GMCH_DATA_N_MASK (0xffffff) + +/* + * Computing Link M and N values. + * + * Link M / N = pixel_clock / ls_clk + * + * (the DP spec calls pixel_clock the 'strm_clk') + * + * The Link value is transmitted in the Main Stream + * Attributes and VB-ID. + */ + +#define PIPEA_DP_LINK_M 0x70060 +#define PIPEA_DP_LINK_M_MASK (0xffffff) + +#define PIPEA_DP_LINK_N 0x70064 +#define PIPEA_DP_LINK_N_MASK (0xffffff) + +#define PIPEB_DSL 0x71000 + +#define PIPEBCONF 0x71008 + +#define PIPEBGCMAXRED 0x71010 +#define PIPEBGCMAXGREEN 0x71014 +#define PIPEBGCMAXBLUE 0x71018 +#define PIPEBSTAT 0x71024 +#define PIPEBFRAMEHIGH 0x71040 +#define PIPEBFRAMEPIXEL 0x71044 +#define PIPEBFRMCOUNT_G4X 0x71040 +#define PIPEBFLIPCOUNT_G4X 0x71044 + +#define PIPEB_GMCH_DATA_M 0x71050 +#define PIPEB_GMCH_DATA_N 0x71054 +#define PIPEB_DP_LINK_M 0x71060 +#define PIPEB_DP_LINK_N 0x71064 + +#define PIPEC_DSL 0x72000 + +#define PIPECCONF 0x72008 + +#define PIPECGCMAXRED 0x72010 +#define PIPECGCMAXGREEN 0x72014 +#define PIPECGCMAXBLUE 0x72018 +#define PIPECSTAT 0x72024 +#define PIPECFRMCOUNT_G4X 0x72040 +#define PIPECFLIPCOUNT_G4X 0x72044 + +#define PIPEC_GMCH_DATA_M 0x72050 +#define PIPEC_GMCH_DATA_N 0x72054 +#define PIPEC_DP_LINK_M 0x72060 +#define PIPEC_DP_LINK_N 0x72064 + +#define PIPEEDPCONF 0x7F008 + +#define DSPACNTR 0x70180 +#define DSPBCNTR 0x71180 +#define DSPCCNTR 0x72180 +#define DISPLAY_PLANE_ENABLE (1<<31) +#define DISPLAY_PLANE_DISABLE 0 +#define DISPLAY_PLANE_TILED (1<<10) +#define DISPPLANE_GAMMA_ENABLE (1<<30) +#define DISPPLANE_GAMMA_DISABLE 0 +#define DISPPLANE_PIXFORMAT_MASK (0xf<<26) +#define DISPPLANE_8BPP (0x2<<26) +#define DISPPLANE_15_16BPP (0x4<<26) +#define DISPPLANE_16BPP (0x5<<26) +#define DISPPLANE_32BPP_NO_ALPHA (0x6<<26) +#define DISPPLANE_32BPP (0x7<<26) +#define DISPPLANE_STEREO_ENABLE (1<<25) +#define DISPPLANE_STEREO_DISABLE 0 +#define DISPPLANE_SEL_PIPE_MASK (1<<24) +#define DISPPLANE_SEL_PIPE_A 0 +#define DISPPLANE_SEL_PIPE_B (1<<24) +#define DISPPLANE_SRC_KEY_ENABLE (1<<22) +#define DISPPLANE_SRC_KEY_DISABLE 0 +#define DISPPLANE_LINE_DOUBLE (1<<20) +#define DISPPLANE_NO_LINE_DOUBLE 0 +#define DISPPLANE_STEREO_POLARITY_FIRST 0 +#define DISPPLANE_STEREO_POLARITY_SECOND (1<<18) +/* plane B only */ +#define DISPPLANE_ALPHA_TRANS_ENABLE (1<<15) +#define DISPPLANE_ALPHA_TRANS_DISABLE 0 +#define DISPPLANE_SPRITE_ABOVE_DISPLAYA 0 +#define DISPPLANE_SPRITE_ABOVE_OVERLAY (1) + +#define DSPABASE 0x70184 +#define DSPASTRIDE 0x70188 + +#define DSPBBASE 0x71184 +#define DSPBADDR DSPBBASE +#define DSPBSTRIDE 0x71188 + +#define DSPCBASE 0x72184 +#define DSPCADDR DSPCBASE +#define DSPCSTRIDE 0x72188 + +#define DSPAKEYVAL 0x70194 +#define DSPAKEYMASK 0x70198 + +#define DSPAPOS 0x7018C /* reserved */ +#define DSPASIZE 0x70190 +#define DSPBPOS 0x7118C +#define DSPBSIZE 0x71190 + +#define DSPASURF 0x7019C +#define DSPATILEOFF 0x701A4 +#define DSPASURFLIVE 0x701AC + +#define DSPBSURF 0x7119C +#define DSPBTILEOFF 0x711A4 +#define DSPBSURFLIVE 0x711AC + +#define DSPCSURF 0x7219C +#define DSPCTILEOFF 0x721A4 +#define DSPCSURFLIVE 0x721AC + +#define VGACNTRL 0x71400 +# define VGA_DISP_DISABLE (1 << 31) +# define VGA_2X_MODE (1 << 30) +# define VGA_PIPE_B_SELECT (1 << 29) + +/* Various masks for reserved bits, etc. */ +#define I830_FWATER1_MASK (~((1<<11)|(1<<10)|(1<<9)| \ + (1<<8)|(1<<26)|(1<<25)|(1<<24)|(1<<5)|(1<<4)|(1<<3)| \ + (1<<2)|(1<<1)|1|(1<<20)|(1<<19)|(1<<18)|(1<<17)|(1<<16))) +#define I830_FWATER2_MASK ~(0) + +#define DV0A_RESERVED ((1<<26)|(1<<25)|(1<<24)|(1<<23)|(1<<22)|(1<<21)|(1<<20)|(1<<19)|(1<<18)|(1<<16)|(1<<5)|(1<<1)|1) +#define DV0B_RESERVED ((1<<27)|(1<<26)|(1<<25)|(1<<24)|(1<<23)|(1<<22)|(1<<21)|(1<<20)|(1<<19)|(1<<18)|(1<<16)|(1<<5)|(1<<1)|1) +#define VGA0_N_DIVISOR_MASK ((1<<21)|(1<<20)|(1<<19)|(1<<18)|(1<<17)|(1<<16)) +#define VGA0_M1_DIVISOR_MASK ((1<<13)|(1<<12)|(1<<11)|(1<<10)|(1<<9)|(1<<8)) +#define VGA0_M2_DIVISOR_MASK ((1<<5)|(1<<4)|(1<<3)|(1<<2)|(1<<1)|1) +#define VGA0_M1M2N_RESERVED ~(VGA0_N_DIVISOR_MASK|VGA0_M1_DIVISOR_MASK|VGA0_M2_DIVISOR_MASK) +#define VGA0_POSTDIV_MASK ((1<<7)|(1<<5)|(1<<4)|(1<<3)|(1<<2)|(1<<1)|1) +#define VGA1_POSTDIV_MASK ((1<<15)|(1<<13)|(1<<12)|(1<<11)|(1<<10)|(1<<9)|(1<<8)) +#define VGA_POSTDIV_RESERVED ~(VGA0_POSTDIV_MASK|VGA1_POSTDIV_MASK|(1<<7)|(1<<15)) +#define DPLLA_POSTDIV_MASK ((1<<23)|(1<<21)|(1<<20)|(1<<19)|(1<<18)|(1<<17)|(1<<16)) +#define DPLLA_RESERVED ((1<<27)|(1<<26)|(1<<25)|(1<<24)|(1<<22)|(1<<15)|(1<<12)|(1<<11)|(1<<10)|(1<<9)|(1<<8)|(1<<7)|(1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)|(1<<1)|1) +#define ADPA_RESERVED ((1<<2)|(1<<1)|1|(1<<9)|(1<<8)|(1<<7)|(1<<6)|(1<<5)|(1<<30)|(1<<29)|(1<<28)|(1<<27)|(1<<26)|(1<<25)|(1<<24)|(1<<23)|(1<<22)|(1<<21)|(1<<20)|(1<<19)|(1<<18)|(1<<17)|(1<<16)) +#define SUPER_WORD 32 +#define BURST_A_MASK ((1<<11)|(1<<10)|(1<<9)|(1<<8)) +#define BURST_B_MASK ((1<<26)|(1<<25)|(1<<24)) +#define WATER_A_MASK ((1<<5)|(1<<4)|(1<<3)|(1<<2)|(1<<1)|1) +#define WATER_B_MASK ((1<<20)|(1<<19)|(1<<18)|(1<<17)|(1<<16)) +#define WATER_RESERVED ((1<<31)|(1<<30)|(1<<29)|(1<<28)|(1<<27)|(1<<23)|(1<<22)|(1<<21)|(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<7)|(1<<6)) +#define PIPEACONF_RESERVED ((1<<29)|(1<<28)|(1<<27)|(1<<23)|(1<<22)|(1<<21)|(1<<20)|(1<<19)|(1<<18)|(1<<17)|(1<<16)|0xffff) +#define PIPEBCONF_RESERVED ((1<<30)|(1<<29)|(1<<28)|(1<<27)|(1<<26)|(1<<25)|(1<<23)|(1<<22)|(1<<21)|(1<<20)|(1<<19)|(1<<18)|(1<<17)|(1<<16)|0xffff) +#define DSPACNTR_RESERVED ((1<<23)|(1<<19)|(1<<17)|(1<<16)|0xffff) +#define DSPBCNTR_RESERVED ((1<<23)|(1<<19)|(1<<17)|(1<<16)|0x7ffe) + +#define I830_GMCH_CTRL 0x52 + +#define I830_GMCH_ENABLED 0x4 +#define I830_GMCH_MEM_MASK 0x1 +#define I830_GMCH_MEM_64M 0x1 +#define I830_GMCH_MEM_128M 0 + +#define I830_GMCH_GMS_MASK 0x70 +#define I830_GMCH_GMS_DISABLED 0x00 +#define I830_GMCH_GMS_LOCAL 0x10 +#define I830_GMCH_GMS_STOLEN_512 0x20 +#define I830_GMCH_GMS_STOLEN_1024 0x30 +#define I830_GMCH_GMS_STOLEN_8192 0x40 + +#define I830_RDRAM_CHANNEL_TYPE 0x03010 +#define I830_RDRAM_ND(x) (((x) & 0x20) >> 5) +#define I830_RDRAM_DDT(x) (((x) & 0x18) >> 3) + +#define I855_GMCH_GMS_MASK (0xF << 4) +#define I855_GMCH_GMS_DISABLED 0x00 +#define I855_GMCH_GMS_STOLEN_1M (0x1 << 4) +#define I855_GMCH_GMS_STOLEN_4M (0x2 << 4) +#define I855_GMCH_GMS_STOLEN_8M (0x3 << 4) +#define I855_GMCH_GMS_STOLEN_16M (0x4 << 4) +#define I855_GMCH_GMS_STOLEN_32M (0x5 << 4) +#define I915G_GMCH_GMS_STOLEN_48M (0x6 << 4) +#define I915G_GMCH_GMS_STOLEN_64M (0x7 << 4) +#define G33_GMCH_GMS_STOLEN_128M (0x8 << 4) +#define G33_GMCH_GMS_STOLEN_256M (0x9 << 4) +#define INTEL_GMCH_GMS_STOLEN_96M (0xa << 4) +#define INTEL_GMCH_GMS_STOLEN_160M (0xb << 4) +#define INTEL_GMCH_GMS_STOLEN_224M (0xc << 4) +#define INTEL_GMCH_GMS_STOLEN_352M (0xd << 4) + + +#define I85X_CAPID 0x44 +#define I85X_VARIANT_MASK 0x7 +#define I85X_VARIANT_SHIFT 5 +#define I855_GME 0x0 +#define I855_GM 0x4 +#define I852_GME 0x2 +#define I852_GM 0x5 + +#define I915_GCFGC 0xf0 +#define I915_LOW_FREQUENCY_ENABLE (1 << 7) +#define I915_DISPLAY_CLOCK_190_200_MHZ (0 << 4) +#define I915_DISPLAY_CLOCK_333_MHZ (4 << 4) +#define I915_DISPLAY_CLOCK_MASK (7 << 4) + +#define I855_HPLLCC 0xc0 +#define I855_CLOCK_CONTROL_MASK (3 << 0) +#define I855_CLOCK_133_200 (0 << 0) +#define I855_CLOCK_100_200 (1 << 0) +#define I855_CLOCK_100_133 (2 << 0) +#define I855_CLOCK_166_250 (3 << 0) + +/* BLT commands */ +#define COLOR_BLT_CMD ((2<<29)|(0x40<<22)|(0x3)) +#define COLOR_BLT_WRITE_ALPHA (1<<21) +#define COLOR_BLT_WRITE_RGB (1<<20) + +#define XY_COLOR_BLT_CMD_NOLEN ((2<<29)|(0x50<<22)) +#define XY_COLOR_BLT_WRITE_ALPHA (1<<21) +#define XY_COLOR_BLT_WRITE_RGB (1<<20) +#define XY_COLOR_BLT_TILED (1<<11) + +#define XY_SETUP_CLIP_BLT_CMD ((2<<29)|(3<<22)|1) + +#define XY_SRC_COPY_BLT_CMD ((2<<29)|(0x53<<22)) +#define XY_SRC_COPY_BLT_WRITE_ALPHA (1<<21) +#define XY_SRC_COPY_BLT_WRITE_RGB (1<<20) +#define XY_SRC_COPY_BLT_SRC_TILED (1<<15) +#define XY_SRC_COPY_BLT_DST_TILED (1<<11) + +#define SRC_COPY_BLT_CMD ((2<<29)|(0x43<<22)|0x4) +#define SRC_COPY_BLT_WRITE_ALPHA (1<<21) +#define SRC_COPY_BLT_WRITE_RGB (1<<20) + +#define XY_PAT_BLT_IMMEDIATE ((2<<29)|(0x72<<22)) + +#define XY_MONO_PAT_BLT_CMD ((0x2<<29)|(0x52<<22)|0x7) +#define XY_MONO_PAT_VERT_SEED ((1<<10)|(1<<9)|(1<<8)) +#define XY_MONO_PAT_HORT_SEED ((1<<14)|(1<<13)|(1<<12)) +#define XY_MONO_PAT_BLT_WRITE_ALPHA (1<<21) +#define XY_MONO_PAT_BLT_WRITE_RGB (1<<20) + +#define XY_MONO_SRC_BLT_CMD ((0x2<<29)|(0x54<<22)|(0x6)) +#define XY_MONO_SRC_BLT_WRITE_ALPHA (1<<21) +#define XY_MONO_SRC_BLT_WRITE_RGB (1<<20) + +#define MI_STORE_DWORD_IMM ((0x20<<23)|2) +#define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */ + +#define MI_SET_CONTEXT (0x18<<23) +#define CTXT_NO_RESTORE (1) +#define CTXT_PALETTE_SAVE_DISABLE (1<<3) +#define CTXT_PALETTE_RESTORE_DISABLE (1<<2) + +/* Dword 0 */ +#define MI_VERTEX_BUFFER (0x17<<23) +#define MI_VERTEX_BUFFER_IDX(x) (x<<20) +#define MI_VERTEX_BUFFER_PITCH(x) (x<<13) +#define MI_VERTEX_BUFFER_WIDTH(x) (x<<6) +/* Dword 1 */ +#define MI_VERTEX_BUFFER_DISABLE (1) + +/* Overlay Flip */ +#define MI_OVERLAY_FLIP (0x11<<23) +#define MI_OVERLAY_FLIP_CONTINUE (0<<21) +#define MI_OVERLAY_FLIP_ON (1<<21) +#define MI_OVERLAY_FLIP_OFF (2<<21) + +/* Wait for Events */ +#define MI_WAIT_FOR_EVENT (0x03<<23) +#define MI_WAIT_FOR_PIPEB_SVBLANK (1<<18) +#define MI_WAIT_FOR_PIPEA_SVBLANK (1<<17) +#define MI_WAIT_FOR_OVERLAY_FLIP (1<<16) +#define MI_WAIT_FOR_PIPEB_VBLANK (1<<7) +#define MI_WAIT_FOR_PIPEA_VBLANK (1<<3) +#define MI_WAIT_FOR_PIPEB_SCAN_LINE_WINDOW (1<<5) +#define MI_WAIT_FOR_PIPEA_SCAN_LINE_WINDOW (1<<1) + +#define MI_LOAD_SCAN_LINES_INCL (0x12<<23) +#define MI_LOAD_REGISTER_IMM ((0x22 << 23) | 1) + +/* Flush */ +#define MI_FLUSH (0x04<<23) +#define MI_WRITE_DIRTY_STATE (1<<4) +#define MI_END_SCENE (1<<3) +#define MI_GLOBAL_SNAPSHOT_COUNT_RESET (1<<3) +#define MI_INHIBIT_RENDER_CACHE_FLUSH (1<<2) +#define MI_STATE_INSTRUCTION_CACHE_FLUSH (1<<1) +#define MI_INVALIDATE_MAP_CACHE (1<<0) +/* broadwater flush bits */ +#define BRW_MI_GLOBAL_SNAPSHOT_RESET (1 << 3) + +/* Noop */ +#define MI_NOOP 0x00 +#define MI_NOOP_WRITE_ID (1<<22) +#define MI_NOOP_ID_MASK (1<<22 - 1) + +#define STATE3D_COLOR_FACTOR ((0x3<<29)|(0x1d<<24)|(0x01<<16)) + +/* Batch */ +#define MI_BATCH_BUFFER ((0x30 << 23) | 1) +#define MI_BATCH_BUFFER_START (0x31 << 23) +#define MI_BATCH_BUFFER_END (0xA << 23) +#define MI_BATCH_NON_SECURE (1) +#define MI_BATCH_NON_SECURE_I965 (1 << 8) +#define MI_BATCH_NON_SECURE_HSW (1<<13) /* Additional bit for RCS */ + +#define MAX_DISPLAY_PIPES 2 + +typedef enum { + CrtIndex = 0, + TvIndex, + DfpIndex, + LfpIndex, + Crt2Index, + Tv2Index, + Dfp2Index, + Lfp2Index, + NumDisplayTypes +} DisplayType; + +/* What's connected to the pipes (as reported by the BIOS) */ +#define PIPE_ACTIVE_MASK 0xff +#define PIPE_CRT_ACTIVE (1 << CrtIndex) +#define PIPE_TV_ACTIVE (1 << TvIndex) +#define PIPE_DFP_ACTIVE (1 << DfpIndex) +#define PIPE_LCD_ACTIVE (1 << LfpIndex) +#define PIPE_CRT2_ACTIVE (1 << Crt2Index) +#define PIPE_TV2_ACTIVE (1 << Tv2Index) +#define PIPE_DFP2_ACTIVE (1 << Dfp2Index) +#define PIPE_LCD2_ACTIVE (1 << Lfp2Index) + +#define PIPE_SIZED_DISP_MASK (PIPE_DFP_ACTIVE | \ + PIPE_LCD_ACTIVE | \ + PIPE_DFP2_ACTIVE) + +#define PIPE_A_SHIFT 0 +#define PIPE_B_SHIFT 8 +#define PIPE_SHIFT(n) ((n) == 0 ? \ + PIPE_A_SHIFT : PIPE_B_SHIFT) + +/* + * Some BIOS scratch area registers. The 845 (and 830?) store the amount + * of video memory available to the BIOS in SWF1. + */ + +#define SWF0 0x71410 +#define SWF1 0x71414 +#define SWF2 0x71418 +#define SWF3 0x7141c +#define SWF4 0x71420 +#define SWF5 0x71424 +#define SWF6 0x71428 + +/* + * 855 scratch registers. + */ +#define SWF00 0x70410 +#define SWF01 0x70414 +#define SWF02 0x70418 +#define SWF03 0x7041c +#define SWF04 0x70420 +#define SWF05 0x70424 +#define SWF06 0x70428 + +#define SWF10 SWF0 +#define SWF11 SWF1 +#define SWF12 SWF2 +#define SWF13 SWF3 +#define SWF14 SWF4 +#define SWF15 SWF5 +#define SWF16 SWF6 + +#define SWF30 0x72414 +#define SWF31 0x72418 +#define SWF32 0x7241c + +/* + * Overlay registers. These are overlay registers accessed via MMIO. + * Those loaded via the overlay register page are defined in i830_video.c. + */ +#define OVADD 0x30000 + +#define DOVSTA 0x30008 +#define OC_BUF (0x3<<20) + +#define OGAMC5 0x30010 +#define OGAMC4 0x30014 +#define OGAMC3 0x30018 +#define OGAMC2 0x3001c +#define OGAMC1 0x30020 +#define OGAMC0 0x30024 + + +/* + * Palette registers + */ +#define PALETTE_A 0x0a000 +#define PALETTE_B 0x0a800 + +/* Framebuffer compression */ +#define FBC_CFB_BASE 0x03200 /* 4k page aligned */ +#define FBC_LL_BASE 0x03204 /* 4k page aligned */ +#define FBC_CONTROL 0x03208 +#define FBC_CTL_EN (1<<31) +#define FBC_CTL_PERIODIC (1<<30) +#define FBC_CTL_INTERVAL_SHIFT (16) +#define FBC_CTL_UNCOMPRESSIBLE (1<<14) +#define FBC_CTL_STRIDE_SHIFT (5) +#define FBC_CTL_FENCENO (1<<0) +#define FBC_COMMAND 0x0320c +#define FBC_CMD_COMPRESS (1<<0) +#define FBC_STATUS 0x03210 +#define FBC_STAT_COMPRESSING (1<<31) +#define FBC_STAT_COMPRESSED (1<<30) +#define FBC_STAT_MODIFIED (1<<29) +#define FBC_STAT_CURRENT_LINE (1<<0) +#define FBC_CONTROL2 0x03214 +#define FBC_CTL_FENCE_DBL (0<<4) +#define FBC_CTL_IDLE_IMM (0<<2) +#define FBC_CTL_IDLE_FULL (1<<2) +#define FBC_CTL_IDLE_LINE (2<<2) +#define FBC_CTL_IDLE_DEBUG (3<<2) +#define FBC_CTL_CPU_FENCE (1<<1) +#define FBC_CTL_PLANEA (0<<0) +#define FBC_CTL_PLANEB (1<<0) +#define FBC_FENCE_OFF 0x0321b +#define FBC_MOD_NUM 0x03220 +#define FBC_TAG_DEBUG 0x03300 + +#define FBC_LL_SIZE (1536) +#define FBC_LL_PAD (32) + +/* Framebuffer compression version 2 */ +#define DPFC_CB_BASE 0x3200 +#define DPFC_CONTROL 0x3208 +#define DPFC_CTL_EN (1<<31) +#define DPFC_CTL_PLANEA (0<<30) +#define DPFC_CTL_PLANEB (1<<30) +#define DPFC_CTL_FENCE_EN (1<<29) +#define DPFC_CTL_LIMIT_1X (0<<6) +#define DPFC_CTL_LIMIT_2X (1<<6) +#define DPFC_CTL_LIMIT_4X (2<<6) +#define DPFC_RECOMP_CTL 0x320c +#define DPFC_RECOMP_STALL_EN (1<<27) +#define DPFC_RECOMP_STALL_WM_SHIFT (16) +#define DPFC_RECOMP_STALL_WM_MASK (0x07ff0000) +#define DPFC_RECOMP_TIMER_COUNT_SHIFT (0) +#define DPFC_RECOMP_TIMER_COUNT_MASK (0x0000003f) +#define DPFC_STATUS 0x3210 +#define DPFC_INVAL_SEG_SHIFT (16) +#define DPFC_INVAL_SEG_MASK (0x07ff0000) +#define DPFC_COMP_SEG_SHIFT (0) +#define DPFC_COMP_SEG_MASK (0x000003ff) +#define DPFC_STATUS2 0x3214 +#define DPFC_FENCE_YOFF 0x3218 + +#define PEG_BAND_GAP_DATA 0x14d68 + +#define MCHBAR_RENDER_STANDBY 0x111B8 +#define RENDER_STANDBY_ENABLE (1 << 30) + + +/* Ironlake */ + +/* warmup time in us */ +#define WARMUP_PCH_REF_CLK_SSC_MOD 1 +#define WARMUP_PCH_FDI_RECEIVER_PLL 25 +#define WARMUP_PCH_DPLL 50 +#define WARMUP_CPU_DP_PLL 20 +#define WARMUP_CPU_FDI_TRANSMITTER_PLL 10 +#define WARMUP_DMI_LATENCY 20 +#define FDI_TRAIN_PATTERN_1_TIME 0.5 +#define FDI_TRAIN_PATTERN_2_TIME 1.5 +#define FDI_ONE_IDLE_PATTERN_TIME 31 + +#define CPU_VGACNTRL 0x41000 + +#define DIGITAL_PORT_HOTPLUG_CNTRL 0x44030 +#define DIGITAL_PORTA_HOTPLUG_ENABLE (1 << 4) +#define DIGITAL_PORTA_SHORT_PULSE_2MS (0 << 2) +#define DIGITAL_PORTA_SHORT_PULSE_4_5MS (1 << 2) +#define DIGITAL_PORTA_SHORT_PULSE_6MS (2 << 2) +#define DIGITAL_PORTA_SHORT_PULSE_100MS (3 << 2) +#define DIGITAL_PORTA_NO_DETECT (0 << 0) +#define DIGITAL_PORTA_LONG_PULSE_DETECT_MASK (1 << 1) +#define DIGITAL_PORTA_SHORT_PULSE_DETECT_MASK (1 << 0) + +/* refresh rate hardware control */ +#define RR_HW_CTL 0x45300 +#define RR_HW_LOW_POWER_FRAMES_MASK 0xff +#define RR_HW_HIGH_POWER_FRAMES_MASK 0xff00 + +#define FDI_PLL_BIOS_0 0x46000 +#define FDI_PLL_BIOS_1 0x46004 +#define FDI_PLL_BIOS_2 0x46008 +#define DISPLAY_PORT_PLL_BIOS_0 0x4600c +#define DISPLAY_PORT_PLL_BIOS_1 0x46010 +#define DISPLAY_PORT_PLL_BIOS_2 0x46014 + +#define FDI_PLL_FREQ_CTL 0x46030 +#define FDI_PLL_FREQ_CHANGE_REQUEST (1<<24) +#define FDI_PLL_FREQ_LOCK_LIMIT_MASK 0xfff00 +#define FDI_PLL_FREQ_DISABLE_COUNT_LIMIT_MASK 0xff + +#define PIPEA_DATA_M1 0x60030 +#define TU_SIZE(x) (((x)-1) << 25) /* default size 64 */ +#define TU_SIZE_MASK 0x7e000000 +#define PIPEA_DATA_M1_OFFSET 0 +#define PIPEA_DATA_N1 0x60034 +#define PIPEA_DATA_N1_OFFSET 0 + +#define PIPEA_DATA_M2 0x60038 +#define PIPEA_DATA_M2_OFFSET 0 +#define PIPEA_DATA_N2 0x6003c +#define PIPEA_DATA_N2_OFFSET 0 + +#define PIPEA_LINK_M1 0x60040 +#define PIPEA_LINK_M1_OFFSET 0 +#define PIPEA_LINK_N1 0x60044 +#define PIPEA_LINK_N1_OFFSET 0 + +#define PIPEA_LINK_M2 0x60048 +#define PIPEA_LINK_M2_OFFSET 0 +#define PIPEA_LINK_N2 0x6004c +#define PIPEA_LINK_N2_OFFSET 0 + +/* PIPEB timing regs are same start from 0x61000 */ + +#define PIPEB_DATA_M1 0x61030 +#define PIPEB_DATA_N1 0x61034 + +#define PIPEB_DATA_M2 0x61038 +#define PIPEB_DATA_N2 0x6103c + +#define PIPEB_LINK_M1 0x61040 +#define PIPEB_LINK_N1 0x61044 + +#define PIPEB_LINK_M2 0x61048 +#define PIPEB_LINK_N2 0x6104c + +/* PIPEC timing regs */ + +#define PIPEC_DATA_M1 0x62030 +#define PIPEC_DATA_N1 0x62034 + +#define PIPEC_DATA_M2 0x62038 +#define PIPEC_DATA_N2 0x6203c + +#define PIPEC_LINK_M1 0x62040 +#define PIPEC_LINK_N1 0x62044 + +#define PIPEC_LINK_M2 0x62048 +#define PIPEC_LINK_N2 0x6204c + +#define PIPEEDP_DATA_M1 0x6F030 +#define PIPEEDP_DATA_N1 0x6F034 + +#define PIPEEDP_LINK_M1 0x6F040 +#define PIPEEDP_LINK_N1 0x6F044 + +/* PIPECONF for pipe A/B addr is same */ + +/* cusor A is only connected to pipe A, + cursor B is connected to pipe B. Otherwise no change. */ + +/* Plane A/B, DSPACNTR/DSPBCNTR addr not changed */ + +/* CPU panel fitter */ +#define PFA_CTL_1 0x68080 +#define PFB_CTL_1 0x68880 +#define PFC_CTL_1 0x69080 +#define PF_ENABLE (1<<31) +#define PFA_CTL_2 0x68084 +#define PFB_CTL_2 0x68884 +#define PFC_CTL_2 0x69084 +#define PFA_CTL_3 0x68088 +#define PFB_CTL_3 0x68888 +#define PFC_CTL_3 0x69088 +#define PFA_CTL_4 0x68090 +#define PFB_CTL_4 0x68890 +#define PFC_CTL_4 0x69090 + +#define PFA_WIN_POS 0x68070 +#define PFB_WIN_POS 0x68870 +#define PFC_WIN_POS 0x69070 +#define PFA_WIN_SIZE 0x68074 +#define PFB_WIN_SIZE 0x68874 +#define PFC_WIN_SIZE 0x69074 + +/* legacy palette */ +#define LGC_PALETTE_A 0x4a000 +#define LGC_PALETTE_B 0x4a800 + +/* interrupts */ +#define DE_MASTER_IRQ_CONTROL (1 << 31) +#define DE_SPRITEB_FLIP_DONE (1 << 29) +#define DE_SPRITEA_FLIP_DONE (1 << 28) +#define DE_PLANEB_FLIP_DONE (1 << 27) +#define DE_PLANEA_FLIP_DONE (1 << 26) +#define DE_PCU_EVENT (1 << 25) +#define DE_GTT_FAULT (1 << 24) +#define DE_POISON (1 << 23) +#define DE_PERFORM_COUNTER (1 << 22) +#define DE_PCH_EVENT (1 << 21) +#define DE_AUX_CHANNEL_A (1 << 20) +#define DE_DP_A_HOTPLUG (1 << 19) +#define DE_GSE (1 << 18) +#define DE_PIPEB_VBLANK (1 << 15) +#define DE_PIPEB_EVEN_FIELD (1 << 14) +#define DE_PIPEB_ODD_FIELD (1 << 13) +#define DE_PIPEB_LINE_COMPARE (1 << 12) +#define DE_PIPEB_VSYNC (1 << 11) +#define DE_PIPEB_FIFO_UNDERRUN (1 << 8) +#define DE_PIPEA_VBLANK (1 << 7) +#define DE_PIPEA_EVEN_FIELD (1 << 6) +#define DE_PIPEA_ODD_FIELD (1 << 5) +#define DE_PIPEA_LINE_COMPARE (1 << 4) +#define DE_PIPEA_VSYNC (1 << 3) +#define DE_PIPEA_FIFO_UNDERRUN (1 << 0) + +#define DEISR 0x44000 +#define DEIMR 0x44004 +#define DEIIR 0x44008 +#define DEIER 0x4400c + +#define GEN8_DE_PIPE_ISR(pipe) (0x44400 + 0x10 * (pipe)) +#define GEN8_DE_PIPE_IMR(pipe) (0x44404 + 0x10 * (pipe)) +#define GEN8_DE_PIPE_IIR(pipe) (0x44408 + 0x10 * (pipe)) +#define GEN8_DE_PIPE_IER(pipe) (0x4440c + 0x10 * (pipe)) + +/* GT interrupt */ +#define GT_SYNC_STATUS (1 << 2) +#define GT_USER_INTERRUPT (1 << 0) + +#define GTISR 0x44010 +#define GTIMR 0x44014 +#define GTIIR 0x44018 +#define GTIER 0x4401c + +/* PCH */ + +/* south display engine interrupt */ +#define SDE_CRT_HOTPLUG (1 << 11) +#define SDE_PORTD_HOTPLUG (1 << 10) +#define SDE_PORTC_HOTPLUG (1 << 9) +#define SDE_PORTB_HOTPLUG (1 << 8) +#define SDE_SDVOB_HOTPLUG (1 << 6) + +#define SDEISR 0xc4000 +#define SDEIMR 0xc4004 +#define SDEIIR 0xc4008 +#define SDEIER 0xc400c + +/* digital port hotplug */ +#define PCH_PORT_HOTPLUG 0xc4030 +#define PORTD_HOTPLUG_ENABLE (1 << 20) +#define PORTD_PULSE_DURATION_2ms (0) +#define PORTD_PULSE_DURATION_4_5ms (1 << 18) +#define PORTD_PULSE_DURATION_6ms (2 << 18) +#define PORTD_PULSE_DURATION_100ms (3 << 18) +#define PORTD_HOTPLUG_NO_DETECT (0) +#define PORTD_HOTPLUG_SHORT_DETECT (1 << 16) +#define PORTD_HOTPLUG_LONG_DETECT (1 << 17) +#define PORTC_HOTPLUG_ENABLE (1 << 12) +#define PORTC_PULSE_DURATION_2ms (0) +#define PORTC_PULSE_DURATION_4_5ms (1 << 10) +#define PORTC_PULSE_DURATION_6ms (2 << 10) +#define PORTC_PULSE_DURATION_100ms (3 << 10) +#define PORTC_HOTPLUG_NO_DETECT (0) +#define PORTC_HOTPLUG_SHORT_DETECT (1 << 8) +#define PORTC_HOTPLUG_LONG_DETECT (1 << 9) +#define PORTB_HOTPLUG_ENABLE (1 << 4) +#define PORTB_PULSE_DURATION_2ms (0) +#define PORTB_PULSE_DURATION_4_5ms (1 << 2) +#define PORTB_PULSE_DURATION_6ms (2 << 2) +#define PORTB_PULSE_DURATION_100ms (3 << 2) +#define PORTB_HOTPLUG_NO_DETECT (0) +#define PORTB_HOTPLUG_SHORT_DETECT (1 << 0) +#define PORTB_HOTPLUG_LONG_DETECT (1 << 1) + +#define PCH_GPIOA 0xc5010 +#define PCH_GPIOB 0xc5014 +#define PCH_GPIOC 0xc5018 +#define PCH_GPIOD 0xc501c +#define PCH_GPIOE 0xc5020 +#define PCH_GPIOF 0xc5024 +#define PCH_GMBUS0 0xc5100 +#define PCH_GMBUS1 0xc5104 +#define PCH_GMBUS2 0xc5108 +#define PCH_GMBUS3 0xc510c +#define PCH_GMBUS4 0xc5110 +#define PCH_GMBUS5 0xc5120 + +#define PCH_DPLL_A 0xc6014 +#define PCH_DPLL_B 0xc6018 + +#define PCH_FPA0 0xc6040 +#define PCH_FPA1 0xc6044 +#define PCH_FPB0 0xc6048 +#define PCH_FPB1 0xc604c + +#define PCH_DPLL_TEST 0xc606c + +#define PCH_DREF_CONTROL 0xC6200 +#define DREF_CONTROL_MASK 0x7fc3 +#define DREF_CPU_SOURCE_OUTPUT_DISABLE (0<<13) +#define DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD (2<<13) +#define DREF_CPU_SOURCE_OUTPUT_NONSPREAD (3<<13) +#define DREF_SSC_SOURCE_DISABLE (0<<11) +#define DREF_SSC_SOURCE_ENABLE (2<<11) +#define DREF_NONSPREAD_SOURCE_DISABLE (0<<9) +#define DREF_NONSPREAD_SOURCE_ENABLE (2<<9) +#define DREF_SUPERSPREAD_SOURCE_DISABLE (0<<7) +#define DREF_SUPERSPREAD_SOURCE_ENABLE (2<<7) +#define DREF_SSC4_DOWNSPREAD (0<<6) +#define DREF_SSC4_CENTERSPREAD (1<<6) +#define DREF_SSC1_DISABLE (0<<1) +#define DREF_SSC1_ENABLE (1<<1) +#define DREF_SSC4_DISABLE (0) +#define DREF_SSC4_ENABLE (1) + +#define PCH_RAWCLK_FREQ 0xc6204 +#define FDL_TP1_TIMER_SHIFT 12 +#define FDL_TP1_TIMER_MASK (3<<12) +#define FDL_TP2_TIMER_SHIFT 10 +#define FDL_TP2_TIMER_MASK (3<<10) +#define RAWCLK_FREQ_MASK 0x3ff + +#define PCH_DPLL_TMR_CFG 0xc6208 + +#define PCH_SSC4_PARMS 0xc6210 +#define PCH_SSC4_AUX_PARMS 0xc6214 + +/* CPT */ +#define PCH_DPLL_ANALOG_CTL 0xc6300 + +#define PCH_DPLL_SEL 0xc7000 +#define TRANSA_DPLL_ENABLE (1<<3) +#define TRANSA_DPLLA_SEL (0) +#define TRANSA_DPLLB_SEL (1<<0) +#define TRANSB_DPLL_ENABLE (1<<7) +#define TRANSB_DPLLA_SEL (0<<4) +#define TRANSB_DPLLB_SEL (1<<4) +#define TRANSC_DPLL_ENABLE (1<<11) +#define TRANSC_DPLLA_SEL (0<<8) +#define TRANSC_DPLLB_SEL (1<<8) + +/* transcoder */ + +#define TRANS_HTOTAL_A 0xe0000 +#define TRANS_HTOTAL_SHIFT 16 +#define TRANS_HACTIVE_SHIFT 0 +#define TRANS_HBLANK_A 0xe0004 +#define TRANS_HBLANK_END_SHIFT 16 +#define TRANS_HBLANK_START_SHIFT 0 +#define TRANS_HSYNC_A 0xe0008 +#define TRANS_HSYNC_END_SHIFT 16 +#define TRANS_HSYNC_START_SHIFT 0 +#define TRANS_VTOTAL_A 0xe000c +#define TRANS_VTOTAL_SHIFT 16 +#define TRANS_VACTIVE_SHIFT 0 +#define TRANS_VBLANK_A 0xe0010 +#define TRANS_VBLANK_END_SHIFT 16 +#define TRANS_VBLANK_START_SHIFT 0 +#define TRANS_VSYNC_A 0xe0014 +#define TRANS_VSYNC_END_SHIFT 16 +#define TRANS_VSYNC_START_SHIFT 0 +#define TRANS_VSYNCSHIFT_A 0xe0028 + +#define TRANSA_DATA_M1 0xe0030 +#define TRANSA_DATA_N1 0xe0034 +#define TRANSA_DATA_M2 0xe0038 +#define TRANSA_DATA_N2 0xe003c +#define TRANSA_DP_LINK_M1 0xe0040 +#define TRANSA_DP_LINK_N1 0xe0044 +#define TRANSA_DP_LINK_M2 0xe0048 +#define TRANSA_DP_LINK_N2 0xe004c + +#define TRANS_HTOTAL_B 0xe1000 +#define TRANS_HBLANK_B 0xe1004 +#define TRANS_HSYNC_B 0xe1008 +#define TRANS_VTOTAL_B 0xe100c +#define TRANS_VBLANK_B 0xe1010 +#define TRANS_VSYNC_B 0xe1014 +#define TRANS_VSYNCSHIFT_B 0xe1028 + +#define TRANSB_DATA_M1 0xe1030 +#define TRANSB_DATA_N1 0xe1034 +#define TRANSB_DATA_M2 0xe1038 +#define TRANSB_DATA_N2 0xe103c +#define TRANSB_DP_LINK_M1 0xe1040 +#define TRANSB_DP_LINK_N1 0xe1044 +#define TRANSB_DP_LINK_M2 0xe1048 +#define TRANSB_DP_LINK_N2 0xe104c + +#define TRANS_HTOTAL_C 0xe2000 +#define TRANS_HBLANK_C 0xe2004 +#define TRANS_HSYNC_C 0xe2008 +#define TRANS_VTOTAL_C 0xe200c +#define TRANS_VBLANK_C 0xe2010 +#define TRANS_VSYNC_C 0xe2014 +#define TRANS_VSYNCSHIFT_C 0xe2028 + +#define TRANSC_DATA_M1 0xe2030 +#define TRANSC_DATA_N1 0xe2034 +#define TRANSC_DATA_M2 0xe2038 +#define TRANSC_DATA_N2 0xe203c +#define TRANSC_DP_LINK_M1 0xe2040 +#define TRANSC_DP_LINK_N1 0xe2044 +#define TRANSC_DP_LINK_M2 0xe2048 +#define TRANSC_DP_LINK_N2 0xe204c + +#define TRANSACONF 0xf0008 +#define TRANSBCONF 0xf1008 +#define TRANSCCONF 0xf2008 +#define TRANS_DISABLE (0<<31) +#define TRANS_ENABLE (1<<31) +#define TRANS_STATE_MASK (1<<30) +#define TRANS_STATE_DISABLE (0<<30) +#define TRANS_STATE_ENABLE (1<<30) +#define TRANS_FSYNC_DELAY_HB1 (0<<27) +#define TRANS_FSYNC_DELAY_HB2 (1<<27) +#define TRANS_FSYNC_DELAY_HB3 (2<<27) +#define TRANS_FSYNC_DELAY_HB4 (3<<27) +#define TRANS_DP_AUDIO_ONLY (1<<26) +#define TRANS_DP_VIDEO_AUDIO (0<<26) +#define TRANS_PROGRESSIVE (0<<21) +#define TRANS_8BPC (0<<5) +#define TRANS_10BPC (1<<5) +#define TRANS_6BPC (2<<5) +#define TRANS_12BPC (3<<5) + +#define FDI_RXA_CHICKEN 0xc200c +#define FDI_RXB_CHICKEN 0xc2010 +#define FDI_RX_PHASE_SYNC_POINTER_ENABLE (1) + +/* CPU: FDI_TX */ +#define FDI_TXA_CTL 0x60100 +#define FDI_TXB_CTL 0x61100 +#define FDI_TXC_CTL 0x62100 +#define FDI_TX_DISABLE (0<<31) +#define FDI_TX_ENABLE (1<<31) +#define FDI_LINK_TRAIN_PATTERN_1 (0<<28) +#define FDI_LINK_TRAIN_PATTERN_2 (1<<28) +#define FDI_LINK_TRAIN_PATTERN_IDLE (2<<28) +#define FDI_LINK_TRAIN_NONE (3<<28) +#define FDI_LINK_TRAIN_VOLTAGE_0_4V (0<<25) +#define FDI_LINK_TRAIN_VOLTAGE_0_6V (1<<25) +#define FDI_LINK_TRAIN_VOLTAGE_0_8V (2<<25) +#define FDI_LINK_TRAIN_VOLTAGE_1_2V (3<<25) +#define FDI_LINK_TRAIN_PRE_EMPHASIS_NONE (0<<22) +#define FDI_LINK_TRAIN_PRE_EMPHASIS_1_5X (1<<22) +#define FDI_LINK_TRAIN_PRE_EMPHASIS_2X (2<<22) +#define FDI_LINK_TRAIN_PRE_EMPHASIS_3X (3<<22) +/* ILK always use 400mV 0dB for voltage swing and pre-emphasis level. + SNB has different settings. */ +/* SNB A-stepping */ +#define FDI_LINK_TRAIN_400MV_0DB_SNB_A (0x38<<22) +#define FDI_LINK_TRAIN_400MV_6DB_SNB_A (0x02<<22) +#define FDI_LINK_TRAIN_600MV_3_5DB_SNB_A (0x01<<22) +#define FDI_LINK_TRAIN_800MV_0DB_SNB_A (0x0<<22) +/* SNB B-stepping */ +#define FDI_LINK_TRAIN_400MV_0DB_SNB_B (0x0<<22) +#define FDI_LINK_TRAIN_400MV_6DB_SNB_B (0x3a<<22) +#define FDI_LINK_TRAIN_600MV_3_5DB_SNB_B (0x39<<22) +#define FDI_LINK_TRAIN_800MV_0DB_SNB_B (0x38<<22) +#define FDI_LINK_TRAIN_VOL_EMP_MASK (0x3f<<22) +#define FDI_DP_PORT_WIDTH_X1 (0<<19) +#define FDI_DP_PORT_WIDTH_X2 (1<<19) +#define FDI_DP_PORT_WIDTH_X3 (2<<19) +#define FDI_DP_PORT_WIDTH_X4 (3<<19) +#define FDI_TX_ENHANCE_FRAME_ENABLE (1<<18) +/* Ironlake: hardwired to 1 */ +#define FDI_TX_PLL_ENABLE (1<<14) +/* both Tx and Rx */ +#define FDI_SCRAMBLING_ENABLE (0<<7) +#define FDI_SCRAMBLING_DISABLE (1<<7) + +/* Additional cpu TX control regs, from ivb bspec */ +#define DPAFE_BMFUNC 0x6c024 +#define DPAFE_DL_IREFCAL0 0x6c02c +#define DPAFE_DL_IREFCAL1 0x6c030 +#define DPAFE_DP_IREFCAL 0x6c034 + +/* FDI_RX, FDI_X is hard-wired to Transcoder_X */ +#define FDI_RXA_CTL 0xf000c +#define FDI_RXB_CTL 0xf100c +#define FDI_RXC_CTL 0xf200c +#define FDI_RX_ENABLE (1<<31) +#define FDI_RX_DISABLE (0<<31) +/* train, dp width same as FDI_TX */ +#define FDI_DP_PORT_WIDTH_X8 (7<<19) +#define FDI_8BPC (0<<16) +#define FDI_10BPC (1<<16) +#define FDI_6BPC (2<<16) +#define FDI_12BPC (3<<16) +#define FDI_LINK_REVERSE_OVERWRITE (1<<15) +#define FDI_DMI_LINK_REVERSE_MASK (1<<14) +#define FDI_RX_PLL_ENABLE (1<<13) +#define FDI_FS_ERR_CORRECT_ENABLE (1<<11) +#define FDI_FE_ERR_CORRECT_ENABLE (1<<10) +#define FDI_FS_ERR_REPORT_ENABLE (1<<9) +#define FDI_FE_ERR_REPORT_ENABLE (1<<8) +#define FDI_RX_ENHANCE_FRAME_ENABLE (1<<6) +#define FDI_SEL_RAWCLK (0<<4) +#define FDI_SEL_PCDCLK (1<<4) +/* CPT */ +#define FDI_AUTO_TRAINING (1<<10) +#define FDI_LINK_TRAIN_PATTERN_1_CPT (0<<8) +#define FDI_LINK_TRAIN_PATTERN_2_CPT (1<<8) +#define FDI_LINK_TRAIN_PATTERN_IDLE_CPT (2<<8) +#define FDI_LINK_TRAIN_NORMAL_CPT (3<<8) +#define FDI_LINK_TRAIN_PATTERN_MASK_CPT (3<<8) + +#define FDI_RXA_MISC 0xf0010 +#define FDI_RXB_MISC 0xf1010 +#define FDI_RXC_MISC 0xf2010 +#define FDI_RXA_TUSIZE1 0xf0030 +#define FDI_RXA_TUSIZE2 0xf0038 +#define FDI_RXB_TUSIZE1 0xf1030 +#define FDI_RXB_TUSIZE2 0xf1038 +#define FDI_RXC_TUSIZE1 0xf2030 +#define FDI_RXC_TUSIZE2 0xf2038 + +/* FDI_RX interrupt register format */ +#define FDI_RX_INTER_LANE_ALIGN (1<<10) +#define FDI_RX_SYMBOL_LOCK (1<<9) /* train 2 */ +#define FDI_RX_BIT_LOCK (1<<8) /* train 1 */ +#define FDI_RX_TRAIN_PATTERN_2_FAIL (1<<7) +#define FDI_RX_FS_CODE_ERR (1<<6) +#define FDI_RX_FE_CODE_ERR (1<<5) +#define FDI_RX_SYMBOL_ERR_RATE_ABOVE (1<<4) +#define FDI_RX_HDCP_LINK_FAIL (1<<3) +#define FDI_RX_PIXEL_FIFO_OVERFLOW (1<<2) +#define FDI_RX_CROSS_CLOCK_OVERFLOW (1<<1) +#define FDI_RX_SYMBOL_QUEUE_OVERFLOW (1<<0) + +#define FDI_RXA_IIR 0xf0014 +#define FDI_RXA_IMR 0xf0018 +#define FDI_RXB_IIR 0xf1014 +#define FDI_RXB_IMR 0xf1018 + +#define FDI_PLL_CTL_1 0xfe000 +#define FDI_PLL_CTL_2 0xfe004 + +/* CRT */ +#define PCH_ADPA 0xe1100 +#define ADPA_TRANS_SELECT_MASK (1<<30) +#define ADPA_TRANS_A_SELECT 0 +#define ADPA_TRANS_B_SELECT (1<<30) +/* HPD is here */ +#define ADPA_CRT_HOTPLUG_MASK 0x03ff0000 /* bit 25-16 */ +#define ADPA_CRT_HOTPLUG_MONITOR_NONE (0<<24) +#define ADPA_CRT_HOTPLUG_MONITOR_MASK (3<<24) +#define ADPA_CRT_HOTPLUG_MONITOR_COLOR (3<<24) +#define ADPA_CRT_HOTPLUG_MONITOR_MONO (2<<24) +#define ADPA_CRT_HOTPLUG_ENABLE (1<<23) +#define ADPA_CRT_HOTPLUG_PERIOD_64 (0<<22) +#define ADPA_CRT_HOTPLUG_PERIOD_128 (1<<22) +#define ADPA_CRT_HOTPLUG_WARMUP_5MS (0<<21) +#define ADPA_CRT_HOTPLUG_WARMUP_10MS (1<<21) +#define ADPA_CRT_HOTPLUG_SAMPLE_2S (0<<20) +#define ADPA_CRT_HOTPLUG_SAMPLE_4S (1<<20) +#define ADPA_CRT_HOTPLUG_VOLTAGE_40 (0<<18) +#define ADPA_CRT_HOTPLUG_VOLTAGE_50 (1<<18) +#define ADPA_CRT_HOTPLUG_VOLTAGE_60 (2<<18) +#define ADPA_CRT_HOTPLUG_VOLTAGE_70 (3<<18) +#define ADPA_CRT_HOTPLUG_VOLREF_325MV (0<<17) +#define ADPA_CRT_HOTPLUG_VOLREF_475MV (1<<17) +#define ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16) +/* polarity control not changed */ + +/* or SDVOB */ +#define HDMIB 0xe1140 +#define PORT_ENABLE (1 << 31) +#define TRANSCODER_A (0) +#define TRANSCODER_B (1 << 30) +#define COLOR_FORMAT_8bpc (0) +#define COLOR_FORMAT_12bpc (3 << 26) +#define SDVOB_HOTPLUG_ENABLE (1 << 23) +#define SDVO_ENCODING (0) +#define TMDS_ENCODING (2 << 10) +#define NULL_PACKET_VSYNC_ENABLE (1 << 9) +#define SDVOB_BORDER_ENABLE (1 << 7) +#define AUDIO_ENABLE (1 << 6) +#define VSYNC_ACTIVE_HIGH (1 << 4) +#define HSYNC_ACTIVE_HIGH (1 << 3) +#define PORT_DETECTED (1 << 2) + +#define HDMIC 0xe1150 +#define HDMID 0xe1160 +#define PCH_LVDS 0xe1180 + +/* Since IVB, the old _CTL2 is now _CTL and the old _CTL is now _DATA. */ +#define BLC_PWM_CPU_CTL2 0x48250 +#define BLC_PWM2_CPU_CTL2 0x48350 +#define PWM_ENABLE (1 << 31) +#define PWM_PIPE_A (0 << 29) +#define PWM_PIPE_B (1 << 29) +#define BLC_PWM_CPU_CTL 0x48254 +#define BLC_PWM2_CPU_CTL 0x48354 +#define BLC_MISC_CTL 0x48360 + +#define UTIL_PIN_CTL 0x48400 + +#define BLC_PWM_PCH_CTL1 0xc8250 +#define PWM_PCH_ENABLE (1 << 31) +#define PWM_POLARITY_ACTIVE_LOW (1 << 29) +#define PWM_POLARITY_ACTIVE_HIGH (0 << 29) +#define PWM_POLARITY_ACTIVE_LOW2 (1 << 28) +#define PWM_POLARITY_ACTIVE_HIGH2 (0 << 28) + +#define BLC_PWM_PCH_CTL2 0xc8254 + +#define PCH_PP_STATUS 0xc7200 +#define PCH_PP_CONTROL 0xc7204 +#define EDP_FORCE_VDD (1 << 3) +#define EDP_BLC_ENABLE (1 << 2) +#define PANEL_POWER_RESET (1 << 1) +#define PANEL_POWER_OFF (0 << 0) +#define PANEL_POWER_ON (1 << 0) +#define PCH_PP_ON_DELAYS 0xc7208 +#define EDP_PANEL (1 << 30) +#define PCH_PP_OFF_DELAYS 0xc720c +#define PCH_PP_DIVISOR 0xc7210 + +#define AUD_CONFIG 0x62000 +#define AUD_DEBUG 0x62010 +#define AUD_VID_DID 0x62020 +#define AUD_RID 0x62024 +#define AUD_SUBN_CNT 0x62028 +#define AUD_FUNC_GRP 0x62040 +#define AUD_SUBN_CNT2 0x62044 +#define AUD_GRP_CAP 0x62048 +#define AUD_PWRST 0x6204c +#define AUD_SUPPWR 0x62050 +#define AUD_SID 0x62054 +#define AUD_OUT_CWCAP 0x62070 +#define AUD_OUT_PCMSIZE 0x62074 +#define AUD_OUT_STR 0x62078 +#define AUD_OUT_DIG_CNVT 0x6207c +#define AUD_OUT_CH_STR 0x62080 +#define AUD_OUT_STR_DESC 0x62084 +#define AUD_PINW_CAP 0x620a0 +#define AUD_PIN_CAP 0x620a4 +#define AUD_PINW_CONNLNG 0x620a8 +#define AUD_PINW_CONNLST 0x620ac +#define AUD_PINW_CNTR 0x620b0 +#define AUD_PINW_UNSOLRESP 0x620b8 +#define AUD_CNTL_ST 0x620b4 +#define AUD_PINW_CONFIG 0x620bc +#define AUD_HDMIW_STATUS 0x620d4 +#define AUD_HDMIW_HDMIEDID 0x6210c +#define AUD_HDMIW_INFOFR 0x62118 +#define AUD_CONV_CHCNT 0x62120 +#define AUD_CTS_ENABLE 0x62128 + +#define VIDEO_DIP_CTL 0x61170 +#define VIDEO_DIP_DATA 0x61178 + +/* CPT */ +#define TRANS_DP_CTL_A 0xe0300 +#define TRANS_DP_CTL_B 0xe1300 +#define TRANS_DP_CTL_C 0xe2300 +#define TRANS_DP_OUTPUT_ENABLE (1<<31) +#define TRANS_DP_PORT_SEL_B (0<<29) +#define TRANS_DP_PORT_SEL_C (1<<29) +#define TRANS_DP_PORT_SEL_D (2<<29) +#define TRANS_DP_PORT_SEL_MASK (3<<29) +#define TRANS_DP_AUDIO_ONLY (1<<26) +#define TRANS_DP_ENH_FRAMING (1<<18) +#define TRANS_DP_8BPC (0<<9) +#define TRANS_DP_10BPC (1<<9) +#define TRANS_DP_6BPC (2<<9) +#define TRANS_DP_12BPC (3<<9) +#define TRANS_DP_VSYNC_ACTIVE_HIGH (1<<4) +#define TRANS_DP_VSYNC_ACTIVE_LOW 0 +#define TRANS_DP_HSYNC_ACTIVE_HIGH (1<<3) +#define TRANS_DP_HSYNC_ACTIVE_LOW 0 + +/* Debug regs */ +#define GEN6_TD_CTL 0x7000 /* <= GEN5 was at 0x8000 */ +#define GEN6_TD_CTL_FORCE_TD_BKPT (1<<4) + +/* Port debugging + */ + +#define PORT_DBG 0x42308 +#define PORT_DBG_DRRS_HW_STATE_OFF (0<<30) +#define PORT_DBG_DRRS_HW_STATE_LOW (1<<30) +#define PORT_DBG_DRRS_HW_STATE_HIGH (2<<30) + +/* RC6 residence counters + */ +#define RC6_RESIDENCY_TIME 0x138108 +#define RC6p_RESIDENCY_TIME 0x13810C +#define RC6pp_RESIDENCY_TIME 0x138110 + +#define GEN6_RPNSWREQ 0xA008 +#define GEN6_RC_VIDEO_FREQ 0xA00C +#define GEN6_RC_CONTROL 0xA090 +#define GEN6_RP_DOWN_TIMEOUT 0xA010 +#define GEN6_RP_INTERRUPT_LIMITS 0xA014 +#define GEN6_RPSTAT1 0xA01C +#define GEN6_RP_CONTROL 0xA024 +#define GEN6_RP_UP_THRESHOLD 0xA02C +#define GEN6_RP_DOWN_THRESHOLD 0xA030 +#define GEN6_RP_CUR_UP_EI 0xA050 +#define GEN6_RP_CUR_UP 0xA054 +#define GEN6_RP_PREV_UP 0xA058 +#define GEN6_RP_CUR_DOWN_EI 0xA05C +#define GEN6_RP_CUR_DOWN 0xA060 +#define GEN6_RP_PREV_DOWN 0xA064 +#define GEN6_RP_UP_EI 0xA068 +#define GEN6_RP_DOWN_EI 0xA06C +#define GEN6_RP_IDLE_HYSTERSIS 0xA070 +#define GEN6_RC_STATE 0xA094 +#define GEN6_RC1_WAKE_RATE_LIMIT 0xA098 +#define GEN6_RC6_WAKE_RATE_LIMIT 0xA09C +#define GEN6_RC6pp_WAKE_RATE_LIMIT 0xA0A0 +#define GEN6_RC_EVALUATION_INTERVAL 0xA0A8 +#define GEN6_RC_IDLE_HYSTERSIS 0xA0AC +#define GEN6_RC_SLEEP 0xA0B0 +#define GEN6_RC1e_THRESHOLD 0xA0B4 +#define GEN6_RC6_THRESHOLD 0xA0B8 +#define GEN6_RC6p_THRESHOLD 0xA0BC +#define GEN6_RC6pp_THRESHOLD 0xA0C0 +#define GEN6_PMINTRMSK 0xA168 +#define GEN6_RC_EVALUATION_INTERVAL 0xA0A8 +#define GEN6_RC_IDLE_HYSTERSIS 0xA0AC +#define GEN6_PMIER 0x4402C +#define GEN6_PMIMR 0x44024 /* rps_lock */ +#define GEN6_PMINTRMSK 0xA168 + +/* Haswell-related items */ + +/* HSW Power Wells */ +#define HSW_PWR_WELL_CTL1 0x45400 /* BIOS */ +#define HSW_PWR_WELL_CTL2 0x45404 /* Driver */ +#define HSW_PWR_WELL_CTL3 0x45408 /* KVMR */ +#define HSW_PWR_WELL_CTL4 0x4540C /* Debug */ +#define HSW_PWR_WELL_ENABLE_REQUEST (1<<31) +#define HSW_PWR_WELL_STATE_ENABLED (1<<30) +#define HSW_PWR_WELL_CTL5 0x45410 +#define HSW_PWR_WELL_ENABLE_SINGLE_STEP (1<<31) +#define HSW_PWR_WELL_PWR_GATE_OVERRIDE (1<<20) +#define HSW_PWR_WELL_FORCE_ON (1<<19) +#define HSW_PWR_WELL_CTL6 0x45414 + +/* Per-pipe DDI Function Control */ +#define PIPE_DDI_FUNC_CTL_A 0x60400 +#define PIPE_DDI_FUNC_CTL_B 0x61400 +#define PIPE_DDI_FUNC_CTL_C 0x62400 +#define PIPE_DDI_FUNC_CTL_EDP 0x6F400 +#define DDI_FUNC_CTL(pipe) _PIPE(pipe, \ + PIPE_DDI_FUNC_CTL_A, \ + PIPE_DDI_FUNC_CTL_B) +#define PIPE_DDI_FUNC_ENABLE (1<<31) +/* Those bits are ignored by pipe EDP since it can only connect to DDI A */ +#define PIPE_DDI_PORT_MASK (0xf<<28) +#define PIPE_DDI_SELECT_PORT(x) ((x)<<28) +#define PIPE_DDI_MODE_SELECT_HDMI (0<<24) +#define PIPE_DDI_MODE_SELECT_DVI (1<<24) +#define PIPE_DDI_MODE_SELECT_DP_SST (2<<24) +#define PIPE_DDI_MODE_SELECT_DP_MST (3<<24) +#define PIPE_DDI_MODE_SELECT_FDI (4<<24) +#define PIPE_DDI_BPC_8 (0<<20) +#define PIPE_DDI_BPC_10 (1<<20) +#define PIPE_DDI_BPC_6 (2<<20) +#define PIPE_DDI_BPC_12 (3<<20) +#define PIPE_DDI_BFI_ENABLE (1<<4) +#define PIPE_DDI_PORT_WIDTH_X1 (0<<1) +#define PIPE_DDI_PORT_WIDTH_X2 (1<<1) +#define PIPE_DDI_PORT_WIDTH_X4 (3<<1) + +/* DisplayPort Transport Control */ +#define DP_TP_CTL_A 0x64040 +#define DP_TP_CTL_B 0x64140 +#define DP_TP_CTL_C 0x64240 +#define DP_TP_CTL_D 0x64340 +#define DP_TP_CTL_E 0x64440 +#define DP_TP_CTL_ENABLE (1<<31) +#define DP_TP_CTL_MODE_SST (0<<27) +#define DP_TP_CTL_MODE_MST (1<<27) +#define DP_TP_CTL_ENHANCED_FRAME_ENABLE (1<<18) +#define DP_TP_CTL_FDI_AUTOTRAIN (1<<15) +#define DP_TP_CTL_LINK_TRAIN_MASK (7<<8) +#define DP_TP_CTL_LINK_TRAIN_PAT1 (0<<8) +#define DP_TP_CTL_LINK_TRAIN_PAT2 (1<<8) +#define DP_TP_CTL_LINK_TRAIN_NORMAL (3<<8) + +/* DisplayPort Transport Status */ +#define DP_TP_STATUS_A 0x64044 +#define DP_TP_STATUS_B 0x64144 +#define DP_TP_STATUS_C 0x64244 +#define DP_TP_STATUS_D 0x64344 +#define DP_TP_STATUS_E 0x64444 +#define DP_TP_STATUS_AUTOTRAIN_DONE (1<<12) + +/* DDI Buffer Control */ +#define DDI_BUF_CTL_A 0x64000 +#define DDI_BUF_CTL_B 0x64100 +#define DDI_BUF_CTL_C 0x64200 +#define DDI_BUF_CTL_D 0x64300 +#define DDI_BUF_CTL_E 0x64400 +#define DDI_BUF_CTL_ENABLE (1<<31) +#define DDI_BUF_EMP_400MV_0DB_HSW (0<<24) /* Sel0 */ +#define DDI_BUF_EMP_400MV_3_5DB_HSW (1<<24) /* Sel1 */ +#define DDI_BUF_EMP_400MV_6DB_HSW (2<<24) /* Sel2 */ +#define DDI_BUF_EMP_400MV_9_5DB_HSW (3<<24) /* Sel3 */ +#define DDI_BUF_EMP_600MV_0DB_HSW (4<<24) /* Sel4 */ +#define DDI_BUF_EMP_600MV_3_5DB_HSW (5<<24) /* Sel5 */ +#define DDI_BUF_EMP_600MV_6DB_HSW (6<<24) /* Sel6 */ +#define DDI_BUF_EMP_800MV_0DB_HSW (7<<24) /* Sel7 */ +#define DDI_BUF_EMP_800MV_3_5DB_HSW (8<<24) /* Sel8 */ +#define DDI_BUF_EMP_MASK (0xf<<24) +#define DDI_BUF_IS_IDLE (1<<7) +#define DDI_PORT_WIDTH_X1 (0<<1) +#define DDI_PORT_WIDTH_X2 (1<<1) +#define DDI_PORT_WIDTH_X4 (3<<1) +#define DDI_INIT_DISPLAY_DETECTED (1<<0) + +/* LPT PIXCLK_GATE */ +#define PIXCLK_GATE 0xC6020 +#define PIXCLK_GATE_UNGATE 1<<0 +#define PIXCLK_GATE_GATE 0<<0 + +/* SPLL */ +#define SPLL_CTL 0x46020 +#define SPLL_PLL_ENABLE (1<<31) +#define SPLL_PLL_SCC (1<<28) +#define SPLL_PLL_NON_SCC (2<<28) +#define SPLL_PLL_FREQ_810MHz (0<<26) +#define SPLL_PLL_FREQ_1350MHz (1<<26) + +/* WRPLL */ +#define WRPLL_CTL1 0x46040 +#define WRPLL_CTL2 0x46060 +#define WRPLL_PLL_ENABLE (1<<31) +#define WRPLL_PLL_SELECT_SSC (0x01<<28) +#define WRPLL_PLL_SELECT_NON_SCC (0x02<<28) +#define WRPLL_PLL_SELECT_LCPLL_2700 (0x03<<28) +/* WRPLL divider programming */ +#define WRPLL_DIVIDER_REFERENCE(x) ((x)<<0) +#define WRPLL_DIVIDER_POST(x) ((x)<<8) +#define WRPLL_DIVIDER_FEEDBACK(x) ((x)<<16) + +/* Port clock selection */ +#define PORT_CLK_SEL_A 0x46100 +#define PORT_CLK_SEL_B 0x46104 +#define PORT_CLK_SEL_C 0x46108 +#define PORT_CLK_SEL_D 0x4610C +#define PORT_CLK_SEL_E 0x46110 +#define PORT_CLK_SEL_LCPLL_2700 (0<<29) +#define PORT_CLK_SEL_LCPLL_1350 (1<<29) +#define PORT_CLK_SEL_LCPLL_810 (2<<29) +#define PORT_CLK_SEL_SPLL (3<<29) +#define PORT_CLK_SEL_WRPLL1 (4<<29) +#define PORT_CLK_SEL_WRPLL2 (5<<29) + +/* Pipe clock selection */ +#define PIPE_CLK_SEL_A 0x46140 +#define PIPE_CLK_SEL_B 0x46144 +#define PIPE_CLK_SEL_C 0x46148 +/* For each pipe, we need to select the corresponding port clock */ +#define PIPE_CLK_SEL_DISABLED (0x0<<29) +#define PIPE_CLK_SEL_PORT(x) ((x+1)<<29) + +/* LCPLL Control */ +#define LCPLL_CTL 0x130040 +#define LCPLL_PLL_DISABLE (1<<31) +#define LCPLL_PLL_LOCK (1<<30) +#define LCPLL_CD_CLOCK_DISABLE (1<<25) +#define LCPLL_CD2X_CLOCK_DISABLE (1<<23) + +/* Pipe WM_LINETIME - watermark line time */ +#define WM_PIPE_A 0x45100 +#define WM_PIPE_B 0x45104 +#define WM_PIPE_C 0x45200 +#define WM_LP1 0x45108 +#define WM_LP2 0x4510C +#define WM_LP3 0x45110 +#define WM_LP1_SPR 0x45120 +#define WM_LP2_SPR 0x45124 +#define WM_LP3_SPR 0x45128 +#define WM_MISC 0x45260 +#define WM_SR_CNT 0x45264 +#define WM_DBG 0x45280 +#define PIPE_WM_LINETIME_A 0x45270 +#define PIPE_WM_LINETIME_B 0x45274 +#define PIPE_WM_LINETIME_C 0x45278 +#define PIPE_WM_LINETIME_MASK (0x1ff) +#define PIPE_WM_LINETIME_TIME(x) ((x)) +#define PIPE_WM_LINETIME_IPS_LINETIME_MASK (0x1ff<<16) +#define PIPE_WM_LINETIME_IPS_LINETIME(x) ((x)<<16) + +/* SFUSE_STRAP */ +#define SFUSE_STRAP 0xc2014 +#define SFUSE_STRAP_DDIB_DETECTED (1<<2) +#define SFUSE_STRAP_DDIC_DETECTED (1<<1) +#define SFUSE_STRAP_DDID_DETECTED (1<<0) + +/* Valleyview related items */ +#define VLV_DISPLAY_BASE 0x180000 + +/* + * IOSF sideband + */ +#define VLV_IOSF_DOORBELL_REQ (VLV_DISPLAY_BASE + 0x2100) +#define IOSF_DEVFN_SHIFT 24 +#define IOSF_OPCODE_SHIFT 16 +#define IOSF_PORT_SHIFT 8 +#define IOSF_BYTE_ENABLES_SHIFT 4 +#define IOSF_BAR_SHIFT 1 +#define IOSF_SB_BUSY (1<<0) +#define IOSF_PORT_BUNIT 0x3 +#define IOSF_PORT_PUNIT 0x4 +#define IOSF_PORT_NC 0x11 +#define IOSF_PORT_DPIO 0x12 +#define IOSF_PORT_DPIO_2 0x1a +#define IOSF_PORT_GPIO_NC 0x13 +#define IOSF_PORT_CCK 0x14 +#define IOSF_PORT_CCU 0xA9 +#define IOSF_PORT_GPS_CORE 0x48 +#define IOSF_PORT_FLISDSI 0x1B +#define VLV_IOSF_DATA (VLV_DISPLAY_BASE + 0x2104) +#define VLV_IOSF_ADDR (VLV_DISPLAY_BASE + 0x2108) + +#endif /* _I810_REG_H */ diff --git a/lib/ioctl_wrappers.c b/lib/ioctl_wrappers.c new file mode 100644 index 0000000..66c90de --- /dev/null +++ b/lib/ioctl_wrappers.c @@ -0,0 +1,1144 @@ +/* + * Copyright © 2007, 2011, 2013, 2014 Intel Corporation + * + * 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: + * Eric Anholt + * Daniel Vetter + * + */ + +#ifndef ANDROID +#define _GNU_SOURCE +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drmtest.h" +#include "i915_drm.h" +#include "intel_chipset.h" +#include "intel_io.h" +#include "igt_debugfs.h" +#include "config.h" + +#include "ioctl_wrappers.h" + +/** + * SECTION:ioctl_wrappers + * @short_description: ioctl wrappers and related functions + * @title: ioctl wrappers + * @include: ioctl_wrappers.h + * + * This helper library contains simple functions to wrap the raw drm/i915 kernel + * ioctls. The normal versions never pass any error codes to the caller and use + * igt_assert() to check for error conditions instead. For some ioctls raw + * wrappers which do pass on error codes are available. These raw wrappers have + * a __ prefix. + * + * For wrappers which check for feature bits there can also be two versions: The + * normal one simply returns a boolean to the caller. But when skipping the + * testcase entirely is the right action then it's better to use igt_skip() + * directly in the wrapper. Such functions have _require_ in their name to + * distinguish them. + */ + +/** + * gem_handle_to_libdrm_bo: + * @bufmgr: libdrm buffer manager instance + * @fd: open i915 drm file descriptor + * @name: buffer name in libdrm + * @handle: gem buffer object handle + * + * This helper function imports a raw gem buffer handle into the libdrm buffer + * manager. + * + * Returns: The imported libdrm buffer manager object. + */ +drm_intel_bo * +gem_handle_to_libdrm_bo(drm_intel_bufmgr *bufmgr, int fd, const char *name, uint32_t handle) +{ + struct drm_gem_flink flink; + int ret; + drm_intel_bo *bo; + + memset(&flink, 0, sizeof(handle)); + flink.handle = handle; + ret = ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink); + igt_assert(ret == 0); + errno = 0; + + bo = drm_intel_bo_gem_create_from_name(bufmgr, name, flink.name); + igt_assert(bo); + + return bo; +} + +/** + * gem_get_tiling: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * @tiling: (out) tiling mode of the gem buffer + * @swizzle: (out) bit 6 swizzle mode + * + * This wraps the GET_TILING ioctl. + */ +void +gem_get_tiling(int fd, uint32_t handle, uint32_t *tiling, uint32_t *swizzle) +{ + struct drm_i915_gem_get_tiling get_tiling; + int ret; + + memset(&get_tiling, 0, sizeof(get_tiling)); + get_tiling.handle = handle; + + ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_GET_TILING, &get_tiling); + igt_assert(ret == 0); + + *tiling = get_tiling.tiling_mode; + *swizzle = get_tiling.swizzle_mode; +} + +int __gem_set_tiling(int fd, uint32_t handle, uint32_t tiling, uint32_t stride) +{ + struct drm_i915_gem_set_tiling st; + int ret; + + memset(&st, 0, sizeof(st)); + do { + st.handle = handle; + st.tiling_mode = tiling; + st.stride = tiling ? stride : 0; + + ret = ioctl(fd, DRM_IOCTL_I915_GEM_SET_TILING, &st); + } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); + if (ret != 0) + return -errno; + + errno = 0; + igt_assert(st.tiling_mode == tiling); + return 0; +} + +/** + * gem_set_tiling: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * @tiling: tiling mode bits + * @stride: stride of the buffer when using a tiled mode, otherwise must be 0 + * + * This wraps the SET_TILING ioctl. + */ +void gem_set_tiling(int fd, uint32_t handle, uint32_t tiling, uint32_t stride) +{ + igt_assert(__gem_set_tiling(fd, handle, tiling, stride) == 0); +} + +struct local_drm_i915_gem_caching { + uint32_t handle; + uint32_t caching; +}; + +#define LOCAL_DRM_I915_GEM_SET_CACHEING 0x2f +#define LOCAL_DRM_I915_GEM_GET_CACHEING 0x30 +#define LOCAL_DRM_IOCTL_I915_GEM_SET_CACHEING \ + DRM_IOW(DRM_COMMAND_BASE + LOCAL_DRM_I915_GEM_SET_CACHEING, struct local_drm_i915_gem_caching) +#define LOCAL_DRM_IOCTL_I915_GEM_GET_CACHEING \ + DRM_IOWR(DRM_COMMAND_BASE + LOCAL_DRM_I915_GEM_GET_CACHEING, struct local_drm_i915_gem_caching) + +/** + * gem_set_caching: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * @caching: caching mode bits + * + * This wraps the SET_CACHING ioctl. Note that this function internally calls + * igt_require() when SET_CACHING isn't available, hence automatically skips the + * test. Therefore always extract test logic which uses this into its own + * subtest. + */ +void gem_set_caching(int fd, uint32_t handle, uint32_t caching) +{ + struct local_drm_i915_gem_caching arg; + int ret; + + memset(&arg, 0, sizeof(arg)); + arg.handle = handle; + arg.caching = caching; + ret = ioctl(fd, LOCAL_DRM_IOCTL_I915_GEM_SET_CACHEING, &arg); + + igt_assert(ret == 0 || (errno == ENOTTY || errno == EINVAL)); + igt_require(ret == 0); + errno = 0; +} + +/** + * gem_get_caching: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * + * This wraps the GET_CACHING ioctl. + * + * Returns: The current caching mode bits. + */ +uint32_t gem_get_caching(int fd, uint32_t handle) +{ + struct local_drm_i915_gem_caching arg; + int ret; + + arg.handle = handle; + arg.caching = 0; + ret = ioctl(fd, LOCAL_DRM_IOCTL_I915_GEM_GET_CACHEING, &arg); + igt_assert(ret == 0); + errno = 0; + + return arg.caching; +} + +/** + * gem_open: + * @fd: open i915 drm file descriptor + * @name: flink buffer name + * + * This wraps the GEM_OPEN ioctl, which is used to import an flink name. + * + * Returns: gem file-private buffer handle of the open object. + */ +uint32_t gem_open(int fd, uint32_t name) +{ + struct drm_gem_open open_struct; + int ret; + + memset(&open_struct, 0, sizeof(open_struct)); + open_struct.name = name; + ret = ioctl(fd, DRM_IOCTL_GEM_OPEN, &open_struct); + igt_assert(ret == 0); + igt_assert(open_struct.handle != 0); + errno = 0; + + return open_struct.handle; +} + +/** + * gem_flink: + * @fd: open i915 drm file descriptor + * @handle: file-private gem buffer object handle + * + * This wraps the GEM_FLINK ioctl, which is used to export a gem buffer object + * into the device-global flink namespace. See gem_open() for opening such a + * buffer name on a different i915 drm file descriptor. + * + * Returns: The created flink buffer name. + */ +uint32_t gem_flink(int fd, uint32_t handle) +{ + struct drm_gem_flink flink; + int ret; + + memset(&flink, 0, sizeof(flink)); + flink.handle = handle; + ret = ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink); + igt_assert(ret == 0); + errno = 0; + + return flink.name; +} + +/** + * gem_close: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * + * This wraps the GEM_CLOSE ioctl, which to release a file-private gem buffer + * handle. + */ +void gem_close(int fd, uint32_t handle) +{ + struct drm_gem_close close_bo; + + memset(&close_bo, 0, sizeof(close_bo)); + close_bo.handle = handle; + do_ioctl(fd, DRM_IOCTL_GEM_CLOSE, &close_bo); +} + +/** + * gem_write: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * @offset: offset within the buffer of the subrange + * @buf: pointer to the data to write into the buffer + * @length: size of the subrange + * + * This wraps the PWRITE ioctl, which is to upload a linear data to a subrange + * of a gem buffer object. + */ +void gem_write(int fd, uint32_t handle, uint32_t offset, const void *buf, uint32_t length) +{ + struct drm_i915_gem_pwrite gem_pwrite; + + memset(&gem_pwrite, 0, sizeof(gem_pwrite)); + gem_pwrite.handle = handle; + gem_pwrite.offset = offset; + gem_pwrite.size = length; + gem_pwrite.data_ptr = (uintptr_t)buf; + do_ioctl(fd, DRM_IOCTL_I915_GEM_PWRITE, &gem_pwrite); +} + +/** + * gem_read: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * @offset: offset within the buffer of the subrange + * @buf: pointer to the data to read into + * @length: size of the subrange + * + * This wraps the PREAD ioctl, which is to download a linear data to a subrange + * of a gem buffer object. + */ +void gem_read(int fd, uint32_t handle, uint32_t offset, void *buf, uint32_t length) +{ + struct drm_i915_gem_pread gem_pread; + + memset(&gem_pread, 0, sizeof(gem_pread)); + gem_pread.handle = handle; + gem_pread.offset = offset; + gem_pread.size = length; + gem_pread.data_ptr = (uintptr_t)buf; + do_ioctl(fd, DRM_IOCTL_I915_GEM_PREAD, &gem_pread); +} + +/** + * gem_set_domain: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * @read_domains: gem domain bits for read access + * @write_domain: gem domain bit for write access + * + * This wraps the SET_DOMAIN ioctl, which is used to control the coherency of + * the gem buffer object between the cpu and gtt mappings. It is also use to + * synchronize with outstanding rendering in general, but for that use-case + * please have a look at gem_sync(). + */ +void gem_set_domain(int fd, uint32_t handle, + uint32_t read_domains, uint32_t write_domain) +{ + struct drm_i915_gem_set_domain set_domain; + + memset(&set_domain, 0, sizeof(set_domain)); + set_domain.handle = handle; + set_domain.read_domains = read_domains; + set_domain.write_domain = write_domain; + + do_ioctl(fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain); +} + +/** + * gem_sync: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * + * This is a wrapper around gem_set_domain() which simply blocks for any + * outstanding rendering to complete. + */ +void gem_sync(int fd, uint32_t handle) +{ + gem_set_domain(fd, handle, I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); +} + +uint32_t __gem_create(int fd, int size) +{ + struct drm_i915_gem_create create; + int ret; + + memset(&create, 0, sizeof(create)); + create.handle = 0; + create.size = size; + ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create); + + if (ret < 0) + return 0; + + errno = 0; + return create.handle; +} + +/** + * gem_create: + * @fd: open i915 drm file descriptor + * @size: desired size of the buffer + * + * This wraps the GEM_CREATE ioctl, which allocates a new gem buffer object of + * @size. + * + * Returns: The file-private handle of the created buffer object + */ +uint32_t gem_create(int fd, int size) +{ + struct drm_i915_gem_create create; + + memset(&create, 0, sizeof(create)); + create.handle = 0; + create.size = size; + do_ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create); + igt_assert(create.handle); + + return create.handle; +} + +/** + * gem_execbuf: + * @fd: open i915 drm file descriptor + * @execbuf: execbuffer data structure + * + * This wraps the EXECBUFFER2 ioctl, which submits a batchbuffer for the gpu to + * run. + */ +void gem_execbuf(int fd, struct drm_i915_gem_execbuffer2 *execbuf) +{ + int ret; + + ret = drmIoctl(fd, + DRM_IOCTL_I915_GEM_EXECBUFFER2, + execbuf); + igt_assert(ret == 0); + errno = 0; +} + +/** + * gem_mmap__gtt: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * @size: size of the gem buffer + * @prot: memory protection bits as used by mmap() + * + * This functions wraps up procedure to establish a memory mapping through the + * GTT. + * + * Returns: A pointer to the created memory mapping. + */ +void *gem_mmap__gtt(int fd, uint32_t handle, int size, int prot) +{ + struct drm_i915_gem_mmap_gtt mmap_arg; + void *ptr; + + memset(&mmap_arg, 0, sizeof(mmap_arg)); + mmap_arg.handle = handle; + if (drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg)) + return NULL; + + ptr = mmap64(0, size, prot, MAP_SHARED, fd, mmap_arg.offset); + if (ptr == MAP_FAILED) + ptr = NULL; + else + errno = 0; + + return ptr; +} + +struct local_i915_gem_mmap_v2 { + uint32_t handle; + uint32_t pad; + uint64_t offset; + uint64_t size; + uint64_t addr_ptr; + uint64_t flags; +#define I915_MMAP_WC 0x1 +}; +#define LOCAL_IOCTL_I915_GEM_MMAP_v2 DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct local_i915_gem_mmap_v2) + +bool gem_mmap__has_wc(int fd) +{ + static int has_wc = -1; + + if (has_wc == -1) { + struct drm_i915_getparam gp; + int val = -1; + + has_wc = 0; + + memset(&gp, 0, sizeof(gp)); + gp.param = 30; /* MMAP_VERSION */ + gp.value = &val; + + /* Do we have the new mmap_ioctl? */ + ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp); + if (val >= 1) { + struct local_i915_gem_mmap_v2 arg; + + /* Does this device support wc-mmaps ? */ + memset(&arg, 0, sizeof(arg)); + arg.handle = gem_create(fd, 4096); + arg.offset = 0; + arg.size = 4096; + arg.flags = I915_MMAP_WC; + has_wc = drmIoctl(fd, LOCAL_IOCTL_I915_GEM_MMAP_v2, &arg) == 0; + gem_close(fd, arg.handle); + } + errno = 0; + } + + return has_wc > 0; +} + +/** + * gem_mmap__wc: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * @offset: offset in the gem buffer of te mmap arena + * @size: size of the mmap arena + * @prot: memory protection bits as used by mmap() + * + * This functions wraps up procedure to establish a memory mapping through + * direct cpu access, bypassing the gpu and cpu caches completely and also + * bypassing the GTT system agent (i.e. there is no automatic tiling of + * the mmapping through the fence registers). + * + * Returns: A pointer to the created memory mapping. + */ +void *gem_mmap__wc(int fd, uint32_t handle, int offset, int size, int prot) +{ + struct local_i915_gem_mmap_v2 arg; + + if (!gem_mmap__has_wc(fd)) { + errno = ENOSYS; + return NULL; + } + + memset(&arg, 0, sizeof(arg)); + arg.handle = handle; + arg.offset = offset; + arg.size = size; + arg.flags = I915_MMAP_WC; + if (drmIoctl(fd, LOCAL_IOCTL_I915_GEM_MMAP_v2, &arg)) + return NULL; + + errno = 0; + return (void *)(uintptr_t)arg.addr_ptr; +} + +/** + * gem_mmap__cpu: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * @offset: offset in the gem buffer of te mmap arena + * @size: size of the mmap arena + * @prot: memory protection bits as used by mmap() + * + * This functions wraps up procedure to establish a memory mapping through + * direct cpu access, bypassing the gpu completely. + * + * Returns: A pointer to the created memory mapping. + */ +void *gem_mmap__cpu(int fd, uint32_t handle, int offset, int size, int prot) +{ + struct drm_i915_gem_mmap mmap_arg; + + memset(&mmap_arg, 0, sizeof(mmap_arg)); + mmap_arg.handle = handle; + mmap_arg.offset = offset; + mmap_arg.size = size; + if (drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg)) + return NULL; + + errno = 0; + return (void *)(uintptr_t)mmap_arg.addr_ptr; +} + +/** + * gem_madvise: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * @state: desired madvise state + * + * This is a wraps the MADVISE ioctl, which is used in libdrm to implement + * opportunistic buffer object caching. Objects in the cache are set to DONTNEED + * (internally in the kernel tracked as purgeable objects). When such a cached + * object is in need again it must be set back to WILLNEED before first use. + * + * Returns: When setting the madvise state to WILLNEED this returns whether the + * backing storage was still available or not. + */ +int gem_madvise(int fd, uint32_t handle, int state) +{ + struct drm_i915_gem_madvise madv; + + memset(&madv, 0, sizeof(madv)); + madv.handle = handle; + madv.madv = state; + madv.retained = 1; + do_ioctl(fd, DRM_IOCTL_I915_GEM_MADVISE, &madv); + + return madv.retained; +} + +/** + * gem_context_create: + * @fd: open i915 drm file descriptor + * + * This is a wraps the CONTEXT_CREATE ioctl, which is used to allocate a new + * hardware context. Not that similarly to gem_set_caching() this wrapper calls + * igt_require() internally to correctly skip on kernels and platforms where hw + * context support is not available. + * + * Returns: The id of the allocated hw context. + */ +uint32_t gem_context_create(int fd) +{ + struct drm_i915_gem_context_create create; + int ret; + + memset(&create, 0, sizeof(create)); + ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_CREATE, &create); + igt_require(ret == 0 || (errno != ENODEV && errno != EINVAL)); + igt_assert(ret == 0); + errno = 0; + + return create.ctx_id; +} + +int __gem_context_destroy(int fd, uint32_t ctx_id) +{ + struct drm_i915_gem_context_destroy destroy; + int ret; + + memset(&destroy, 0, sizeof(destroy)); + destroy.ctx_id = ctx_id; + + ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_DESTROY, &destroy); + if (ret) + return -errno; + return 0; +} + +/** + * gem_context_destroy: + * @fd: open i915 drm file descriptor + * @ctx_id: i915 hw context id + * + * This is a wraps the CONTEXT_DESTROY ioctl, which is used to free a hardware + * context. + */ +void gem_context_destroy(int fd, uint32_t ctx_id) +{ + struct drm_i915_gem_context_destroy destroy; + + memset(&destroy, 0, sizeof(destroy)); + destroy.ctx_id = ctx_id; + + do_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_DESTROY, &destroy); +} + +/** + * gem_context_get_param: + * @fd: open i915 drm file descriptor + * @p: i915 hw context parameter + * + * This is a wraps the CONTEXT_GET_PARAM ioctl, which is used to free a hardware + * context. Not that similarly to gem_set_caching() this wrapper calls + * igt_require() internally to correctly skip on kernels and platforms where hw + * context parameter support is not available. + */ +void gem_context_get_param(int fd, struct local_i915_gem_context_param *p) +{ +#define LOCAL_I915_GEM_CONTEXT_GETPARAM 0x34 +#define LOCAL_IOCTL_I915_GEM_CONTEXT_GETPARAM DRM_IOWR (DRM_COMMAND_BASE + LOCAL_I915_GEM_CONTEXT_GETPARAM, struct local_i915_gem_context_param) + do_ioctl(fd, LOCAL_IOCTL_I915_GEM_CONTEXT_GETPARAM, p); +} + +/** + * gem_context_set_param: + * @fd: open i915 drm file descriptor + * @p: i915 hw context parameter + * + * This is a wraps the CONTEXT_SET_PARAM ioctl, which is used to free a hardware + * context. Not that similarly to gem_set_caching() this wrapper calls + * igt_require() internally to correctly skip on kernels and platforms where hw + * context parameter support is not available. + */ +void gem_context_set_param(int fd, struct local_i915_gem_context_param *p) +{ +#define LOCAL_I915_GEM_CONTEXT_SETPARAM 0x35 +#define LOCAL_IOCTL_I915_GEM_CONTEXT_SETPARAM DRM_IOWR (DRM_COMMAND_BASE + LOCAL_I915_GEM_CONTEXT_SETPARAM, struct local_i915_gem_context_param) + do_ioctl(fd, LOCAL_IOCTL_I915_GEM_CONTEXT_SETPARAM, p); +} + +/** + * gem_context_require_param: + * @fd: open i915 drm file descriptor + * + * Feature test macro to query whether hw context parameter support for @param + * is available. Automatically skips through igt_require() if not. + */ +void gem_context_require_param(int fd, uint64_t param) +{ + struct local_i915_gem_context_param p; + + p.context = 0; + p.param = param; + p.value = 0; + p.size = 0; + + igt_require(drmIoctl(fd, LOCAL_IOCTL_I915_GEM_CONTEXT_GETPARAM, &p) == 0); +} + +/** + * gem_sw_finish: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * + * This is a wraps the SW_FINISH ioctl, which is used to flush out frontbuffer + * rendering done through the direct cpu memory mappings. Shipping userspace + * does _not_ call this after frontbuffer rendering through gtt memory mappings. + */ +void gem_sw_finish(int fd, uint32_t handle) +{ + struct drm_i915_gem_sw_finish finish; + + memset(&finish, 0, sizeof(finish)); + finish.handle = handle; + + do_ioctl(fd, DRM_IOCTL_I915_GEM_SW_FINISH, &finish); +} + +/** + * gem_bo_busy: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * + * This is a wraps the BUSY ioctl, which tells whether a buffer object is still + * actively used by the gpu in a execbuffer. + * + * Returns: The busy state of the buffer object. + */ +bool gem_bo_busy(int fd, uint32_t handle) +{ + struct drm_i915_gem_busy busy; + + memset(&busy, 0, sizeof(busy)); + busy.handle = handle; + + do_ioctl(fd, DRM_IOCTL_I915_GEM_BUSY, &busy); + + return !!busy.busy; +} + + +/* feature test helpers */ + +/** + * gem_uses_aliasing_ppgtt: + * @fd: open i915 drm file descriptor + * + * Feature test macro to check whether the kernel internally uses ppgtt to + * execute batches. The /aliasing/ in the function name is a bit a misnomer, + * this driver parameter is also true when full ppgtt address spaces are + * available since for batchbuffer construction only ppgtt or global gtt is + * relevant. + * + * Returns: Whether batches are run through ppgtt. + */ +bool gem_uses_aliasing_ppgtt(int fd) +{ + struct drm_i915_getparam gp; + int val = 0; + + memset(&gp, 0, sizeof(gp)); + gp.param = 18; /* HAS_ALIASING_PPGTT */ + gp.value = &val; + + if (ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp, sizeof(gp))) + return 0; + + errno = 0; + return val; +} + +/** + * gem_available_fences: + * @fd: open i915 drm file descriptor + * + * Feature test macro to query the kernel for the number of available fences + * usable in a batchbuffer. Only relevant for pre-gen4. + * + * Returns: The number of available fences. + */ +int gem_available_fences(int fd) +{ + struct drm_i915_getparam gp; + int val = 0; + + memset(&gp, 0, sizeof(gp)); + gp.param = I915_PARAM_NUM_FENCES_AVAIL; + gp.value = &val; + + if (ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp, sizeof(gp))) + return 0; + + errno = 0; + return val; +} + +bool gem_has_llc(int fd) +{ + struct drm_i915_getparam gp; + int val = 0; + + memset(&gp, 0, sizeof(gp)); + gp.param = I915_PARAM_HAS_LLC; + gp.value = &val; + + if (ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp, sizeof(gp))) + return 0; + + errno = 0; + return val; +} + +/** + * gem_get_num_rings: + * @fd: open i915 drm file descriptor + * + * Feature test macro to query the number of available rings. This is useful in + * test loops which need to step through all rings and similar logic. + * + * For more explicit tests of ring availability see gem_has_enable_ring() and + * the ring specific versions like gem_has_bsd(). + * + * Returns: The number of available rings. + */ +int gem_get_num_rings(int fd) +{ + int num_rings = 1; /* render ring is always available */ + + if (gem_has_bsd(fd)) + num_rings++; + else + goto skip; + + if (gem_has_blt(fd)) + num_rings++; + else + goto skip; + + if (gem_has_vebox(fd)) + num_rings++; + else + goto skip; + + +skip: + return num_rings; +} + +/** + * gem_has_enable_ring: + * @fd: open i915 drm file descriptor + * @param: ring flag bit as used in gem_execbuf() + * + * Feature test macro to query whether a specific ring is available. + * + * Returns: Whether the ring is available or not. + */ +bool gem_has_enable_ring(int fd,int param) +{ + drm_i915_getparam_t gp; + int tmp = 0; + + memset(&gp, 0, sizeof(gp)); + gp.value = &tmp; + gp.param = param; + + if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp)) + return false; + + errno = 0; + return tmp > 0; +} + +/** + * gem_has_bsd: + * @fd: open i915 drm file descriptor + * + * Feature test macro to query whether the BSD ring is available. This is simply + * a specific version of gem_has_enable_ring() for the BSD ring. + * + * Note that recent Bspec calls this the VCS ring for Video Command Submission. + * + * Returns: Whether the BSD ring is available or not. + */ +bool gem_has_bsd(int fd) +{ + return gem_has_enable_ring(fd,I915_PARAM_HAS_BSD); +} + +/** + * gem_has_blt: + * @fd: open i915 drm file descriptor + * + * Feature test macro to query whether the blitter ring is available. This is simply + * a specific version of gem_has_enable_ring() for the blitter ring. + * + * Note that recent Bspec calls this the BCS ring for Blitter Command Submission. + * + * Returns: Whether the blitter ring is available or not. + */ +bool gem_has_blt(int fd) +{ + return gem_has_enable_ring(fd,I915_PARAM_HAS_BLT); +} + +#define LOCAL_I915_PARAM_HAS_VEBOX 22 +/** + * gem_has_vebox: + * @fd: open i915 drm file descriptor + * + * Feature test macro to query whether the vebox ring is available. This is simply + * a specific version of gem_has_enable_ring() for the vebox ring. + * + * Note that recent Bspec calls this the VECS ring for Video Enhancement Command + * Submission. + * + * Returns: Whether the vebox ring is available or not. + */ +bool gem_has_vebox(int fd) +{ + return gem_has_enable_ring(fd,LOCAL_I915_PARAM_HAS_VEBOX); +} + +#define LOCAL_I915_PARAM_HAS_BSD2 31 +/** + * gem_has_bsd2: + * @fd: open i915 drm file descriptor + * + * Feature test macro to query whether the BSD2 ring is available. This is simply + * a specific version of gem_has_enable_ring() for the BSD2 ring. + * + * Note that recent Bspec calls this the VCS ring for Video Command Submission. + * + * Returns: Whether the BSD ring is avaible or not. + */ +bool gem_has_bsd2(int fd) +{ + return gem_has_enable_ring(fd,LOCAL_I915_PARAM_HAS_BSD2); +} +/** + * gem_available_aperture_size: + * @fd: open i915 drm file descriptor + * + * Feature test macro to query the kernel for the available gpu aperture size + * usable in a batchbuffer. + * + * Returns: The available gtt address space size. + */ +uint64_t gem_available_aperture_size(int fd) +{ + struct drm_i915_gem_get_aperture aperture; + + memset(&aperture, 0, sizeof(aperture)); + aperture.aper_size = 256*1024*1024; + do_ioctl(fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &aperture); + + return aperture.aper_available_size; +} + +/** + * gem_aperture_size: + * @fd: open i915 drm file descriptor + * + * Feature test macro to query the kernel for the total gpu aperture size. + * + * Returns: The total gtt address space size. + */ +uint64_t gem_aperture_size(int fd) +{ + struct drm_i915_gem_get_aperture aperture; + + memset(&aperture, 0, sizeof(aperture)); + aperture.aper_size = 256*1024*1024; + do_ioctl(fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &aperture); + + return aperture.aper_size; +} + +/** + * gem_mappable_aperture_size: + * + * Feature test macro to query the kernel for the mappable gpu aperture size. + * This is the area available for GTT memory mappings. + * + * Returns: The mappable gtt address space size. + */ +uint64_t gem_mappable_aperture_size(void) +{ + struct pci_device *pci_dev = intel_get_pci_device(); + int bar; + + if (intel_gen(pci_dev->device_id) < 3) + bar = 0; + else + bar = 2; + + return pci_dev->regions[bar].size; +} + +/** + * gem_require_caching: + * @fd: open i915 drm file descriptor + * + * Feature test macro to query whether buffer object caching control is + * available. Automatically skips through igt_require() if not. + */ +void gem_require_caching(int fd) +{ + struct local_drm_i915_gem_caching arg; + int ret; + + memset(&arg, 0, sizeof(arg)); + arg.handle = gem_create(fd, 4096); + igt_assert(arg.handle != 0); + + arg.caching = 0; + ret = ioctl(fd, LOCAL_DRM_IOCTL_I915_GEM_SET_CACHEING, &arg); + gem_close(fd, arg.handle); + + igt_require(ret == 0); + errno = 0; +} + +/** + * gem_require_ring: + * @fd: open i915 drm file descriptor + * @ring_id: ring flag bit as used in gem_execbuf() + * + * Feature test macro to query whether a specific ring is available. + * In contrast to gem_has_enable_ring() this automagically skips if the ring + * isn't available by calling igt_require(). + */ +void gem_require_ring(int fd, int ring_id) +{ + switch (ring_id) { + case I915_EXEC_RENDER: + return; + case I915_EXEC_BLT: + igt_require(HAS_BLT_RING(intel_get_drm_devid(fd))); + return; + case I915_EXEC_BSD: + igt_require(HAS_BSD_RING(intel_get_drm_devid(fd))); + return; +#ifdef I915_EXEC_VEBOX + case I915_EXEC_VEBOX: + igt_require(gem_has_vebox(fd)); + return; +#endif + default: + igt_assert(0); + return; + } +} + +/* prime */ + +/** + * prime_handle_to_fd: + * @fd: open i915 drm file descriptor + * @handle: file-private gem buffer object handle + * + * This wraps the PRIME_HANDLE_TO_FD ioctl, which is used to export a gem buffer + * object into a global (i.e. potentially cross-device) dma-buf file-descriptor + * handle. + * + * Returns: The created dma-buf fd handle. + */ +int prime_handle_to_fd(int fd, uint32_t handle) +{ + struct drm_prime_handle args; + + memset(&args, 0, sizeof(args)); + args.handle = handle; + args.flags = DRM_CLOEXEC; + args.fd = -1; + + do_ioctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args); + + return args.fd; +} + +/** + * prime_fd_to_handle: + * @fd: open i915 drm file descriptor + * @dma_buf_fd: dma-buf fd handle + * + * This wraps the PRIME_FD_TO_HANDLE ioctl, which is used to import a dma-buf + * file-descriptor into a gem buffer object. + * + * Returns: The created gem buffer object handle. + */ +uint32_t prime_fd_to_handle(int fd, int dma_buf_fd) +{ + struct drm_prime_handle args; + + memset(&args, 0, sizeof(args)); + args.fd = dma_buf_fd; + args.flags = 0; + args.handle = 0; + + do_ioctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &args); + + return args.handle; +} + +/** + * prime_get_size: + * @dma_buf_fd: dma-buf fd handle + * + * This wraps the lseek() protocol used to query the invariant size of a + * dma-buf. Not all kernels support this, which is check with igt_require() and + * so will result in automagic test skipping. + * + * Returns: The lifetime-invariant size of the dma-buf object. + */ +off_t prime_get_size(int dma_buf_fd) +{ + off_t ret; + + ret = lseek(dma_buf_fd, 0, SEEK_END); + igt_assert(ret >= 0 || errno == ESPIPE); + igt_require(ret >= 0); + errno = 0; + + return ret; +} diff --git a/lib/ioctl_wrappers.h b/lib/ioctl_wrappers.h new file mode 100644 index 0000000..7c0c87e --- /dev/null +++ b/lib/ioctl_wrappers.h @@ -0,0 +1,138 @@ +/* + * Copyright © 2007,2014 Intel Corporation + * + * 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: + * Eric Anholt + * Daniel Vetter + * + */ + + +#ifndef IOCTL_WRAPPERS_H +#define IOCTL_WRAPPERS_H + +#include +#include +#include +#include + +/* libdrm interfacing */ +drm_intel_bo * gem_handle_to_libdrm_bo(drm_intel_bufmgr *bufmgr, int fd, + const char *name, uint32_t handle); + +/* ioctl_wrappers.c: + * + * ioctl wrappers and similar stuff for bare metal testing */ +void gem_get_tiling(int fd, uint32_t handle, uint32_t *tiling, uint32_t *swizzle); +void gem_set_tiling(int fd, uint32_t handle, uint32_t tiling, uint32_t stride); +int __gem_set_tiling(int fd, uint32_t handle, uint32_t tiling, uint32_t stride); + +void gem_set_caching(int fd, uint32_t handle, uint32_t caching); +uint32_t gem_get_caching(int fd, uint32_t handle); +uint32_t gem_flink(int fd, uint32_t handle); +uint32_t gem_open(int fd, uint32_t name); +void gem_close(int fd, uint32_t handle); +void gem_write(int fd, uint32_t handle, uint32_t offset, const void *buf, uint32_t length); +void gem_read(int fd, uint32_t handle, uint32_t offset, void *buf, uint32_t length); +void gem_set_domain(int fd, uint32_t handle, + uint32_t read_domains, uint32_t write_domain); +void gem_sync(int fd, uint32_t handle); +uint32_t __gem_create(int fd, int size); +uint32_t gem_create(int fd, int size); +void gem_execbuf(int fd, struct drm_i915_gem_execbuffer2 *execbuf); + +void *gem_mmap__gtt(int fd, uint32_t handle, int size, int prot); +void *gem_mmap__cpu(int fd, uint32_t handle, int offset, int size, int prot); + +bool gem_mmap__has_wc(int fd); +void *gem_mmap__wc(int fd, uint32_t handle, int offset, int size, int prot); + +/** + * gem_require_mmap_wc: + * @fd: open i915 drm file descriptor + * + * Feature test macro to query whether direct (i.e. cpu access path, bypassing + * the gtt) write-combine memory mappings are available. Automatically skips + * through igt_require() if not. + */ +#define gem_require_mmap_wc(fd) igt_require(gem_mmap__has_wc(fd)) + +/** + * gem_mmap: + * @fd: open i915 drm file descriptor + * @handle: gem buffer object handle + * @size: size of the gem buffer + * @prot: memory protection bits as used by mmap() + * + * This functions wraps up procedure to establish a memory mapping through the + * GTT. + * + * This is a simple convenience alias to gem_mmap__gtt() + * + * Returns: A pointer to the created memory mapping. + */ +#define gem_mmap(fd, handle, size, prot) gem_mmap__gtt(fd, handle, size, prot) + +int gem_madvise(int fd, uint32_t handle, int state); + +uint32_t gem_context_create(int fd); +void gem_context_destroy(int fd, uint32_t ctx_id); +int __gem_context_destroy(int fd, uint32_t ctx_id); +struct local_i915_gem_context_param { + uint32_t context; + uint32_t size; + uint64_t param; +#define LOCAL_CONTEXT_PARAM_BAN_PERIOD 0x1 + uint64_t value; +}; +void gem_context_require_param(int fd, uint64_t param); +void gem_context_get_param(int fd, struct local_i915_gem_context_param *p); +void gem_context_set_param(int fd, struct local_i915_gem_context_param *p); + +void gem_sw_finish(int fd, uint32_t handle); + +bool gem_bo_busy(int fd, uint32_t handle); + +/* feature test helpers */ +bool gem_has_llc(int fd); +int gem_get_num_rings(int fd); +bool gem_has_enable_ring(int fd,int param); +bool gem_has_bsd(int fd); +bool gem_has_blt(int fd); +bool gem_has_vebox(int fd); +bool gem_has_bsd2(int fd); +bool gem_uses_aliasing_ppgtt(int fd); +int gem_available_fences(int fd); +uint64_t gem_available_aperture_size(int fd); +uint64_t gem_aperture_size(int fd); +uint64_t gem_mappable_aperture_size(void); + +/* check functions which auto-skip tests by calling igt_skip() */ +void gem_require_caching(int fd); +void gem_require_ring(int fd, int ring_id); + +/* prime */ +int prime_handle_to_fd(int fd, uint32_t handle); +uint32_t prime_fd_to_handle(int fd, int dma_buf_fd); +off_t prime_get_size(int dma_buf_fd); + +#endif /* IOCTL_WRAPPERS_H */ diff --git a/lib/media_fill.h b/lib/media_fill.h new file mode 100644 index 0000000..2a30055 --- /dev/null +++ b/lib/media_fill.h @@ -0,0 +1,42 @@ +#ifndef RENDE_MEDIA_FILL_H +#define RENDE_MEDIA_FILL_H + +#include +#include "intel_batchbuffer.h" + +void +gen8_media_fillfunc(struct intel_batchbuffer *batch, + struct igt_buf *dst, + unsigned x, unsigned y, + unsigned width, unsigned height, + uint8_t color); + +void +gen7_media_fillfunc(struct intel_batchbuffer *batch, + struct igt_buf *dst, + unsigned x, unsigned y, + unsigned width, unsigned height, + uint8_t color); + +void +gen8lp_media_fillfunc(struct intel_batchbuffer *batch, + struct igt_buf *dst, + unsigned x, unsigned y, + unsigned width, unsigned height, + uint8_t color); + +void +gen9_media_fillfunc(struct intel_batchbuffer *batch, + struct igt_buf *dst, + unsigned x, unsigned y, + unsigned width, unsigned height, + uint8_t color); + +void +gen7_gpgpu_fillfunc(struct intel_batchbuffer *batch, + struct igt_buf *dst, + unsigned x, unsigned y, + unsigned width, unsigned height, + uint8_t color); + +#endif /* RENDE_MEDIA_FILL_H */ diff --git a/lib/rendercopy.h b/lib/rendercopy.h new file mode 100644 index 0000000..fdc3cab --- /dev/null +++ b/lib/rendercopy.h @@ -0,0 +1,55 @@ +#include "intel_batchbuffer.h" + + +static inline void emit_vertex_2s(struct intel_batchbuffer *batch, + int16_t x, int16_t y) +{ + OUT_BATCH((uint16_t)y << 16 | (uint16_t)x); +} + +static inline void emit_vertex(struct intel_batchbuffer *batch, + float f) +{ + union { float f; uint32_t ui; } u; + u.f = f; + OUT_BATCH(u.ui); +} + +static inline void emit_vertex_normalized(struct intel_batchbuffer *batch, + float f, float total) +{ + union { float f; uint32_t ui; } u; + u.f = f / total; + OUT_BATCH(u.ui); +} + +void gen9_render_copyfunc(struct intel_batchbuffer *batch, + drm_intel_context *context, + struct igt_buf *src, unsigned src_x, unsigned src_y, + unsigned width, unsigned height, + struct igt_buf *dst, unsigned dst_x, unsigned dst_y); +void gen8_render_copyfunc(struct intel_batchbuffer *batch, + drm_intel_context *context, + struct igt_buf *src, unsigned src_x, unsigned src_y, + unsigned width, unsigned height, + struct igt_buf *dst, unsigned dst_x, unsigned dst_y); +void gen7_render_copyfunc(struct intel_batchbuffer *batch, + drm_intel_context *context, + struct igt_buf *src, unsigned src_x, unsigned src_y, + unsigned width, unsigned height, + struct igt_buf *dst, unsigned dst_x, unsigned dst_y); +void gen6_render_copyfunc(struct intel_batchbuffer *batch, + drm_intel_context *context, + struct igt_buf *src, unsigned src_x, unsigned src_y, + unsigned width, unsigned height, + struct igt_buf *dst, unsigned dst_x, unsigned dst_y); +void gen3_render_copyfunc(struct intel_batchbuffer *batch, + drm_intel_context *context, + struct igt_buf *src, unsigned src_x, unsigned src_y, + unsigned width, unsigned height, + struct igt_buf *dst, unsigned dst_x, unsigned dst_y); +void gen2_render_copyfunc(struct intel_batchbuffer *batch, + drm_intel_context *context, + struct igt_buf *src, unsigned src_x, unsigned src_y, + unsigned width, unsigned height, + struct igt_buf *dst, unsigned dst_x, unsigned dst_y); diff --git a/m4/ax_pkg_swig.m4 b/m4/ax_pkg_swig.m4 new file mode 100644 index 0000000..d836eec --- /dev/null +++ b/m4/ax_pkg_swig.m4 @@ -0,0 +1,135 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_pkg_swig.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PKG_SWIG([major.minor.micro], [action-if-found], [action-if-not-found]) +# +# DESCRIPTION +# +# This macro searches for a SWIG installation on your system. If found, +# then SWIG is AC_SUBST'd; if not found, then $SWIG is empty. If SWIG is +# found, then SWIG_LIB is set to the SWIG library path, and AC_SUBST'd. +# +# You can use the optional first argument to check if the version of the +# available SWIG is greater than or equal to the value of the argument. It +# should have the format: N[.N[.N]] (N is a number between 0 and 999. Only +# the first N is mandatory.) If the version argument is given (e.g. +# 1.3.17), AX_PKG_SWIG checks that the swig package is this version number +# or higher. +# +# As usual, action-if-found is executed if SWIG is found, otherwise +# action-if-not-found is executed. +# +# In configure.in, use as: +# +# AX_PKG_SWIG(1.3.17, [], [ AC_MSG_ERROR([SWIG is required to build..]) ]) +# AX_SWIG_ENABLE_CXX +# AX_SWIG_MULTI_MODULE_SUPPORT +# AX_SWIG_PYTHON +# +# LICENSE +# +# Copyright (c) 2008 Sebastian Huber +# Copyright (c) 2008 Alan W. Irwin +# Copyright (c) 2008 Rafael Laboissiere +# Copyright (c) 2008 Andrew Collier +# Copyright (c) 2011 Murray Cumming +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 11 + +AC_DEFUN([AX_PKG_SWIG],[ + # Ubuntu has swig 2.0 as /usr/bin/swig2.0 + AC_PATH_PROGS([SWIG],[swig swig2.0]) + if test -z "$SWIG" ; then + m4_ifval([$3],[$3],[:]) + elif test -n "$1" ; then + AC_MSG_CHECKING([SWIG version]) + [swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`] + AC_MSG_RESULT([$swig_version]) + if test -n "$swig_version" ; then + # Calculate the required version number components + [required=$1] + [required_major=`echo $required | sed 's/[^0-9].*//'`] + if test -z "$required_major" ; then + [required_major=0] + fi + [required=`echo $required | sed 's/[0-9]*[^0-9]//'`] + [required_minor=`echo $required | sed 's/[^0-9].*//'`] + if test -z "$required_minor" ; then + [required_minor=0] + fi + [required=`echo $required | sed 's/[0-9]*[^0-9]//'`] + [required_patch=`echo $required | sed 's/[^0-9].*//'`] + if test -z "$required_patch" ; then + [required_patch=0] + fi + # Calculate the available version number components + [available=$swig_version] + [available_major=`echo $available | sed 's/[^0-9].*//'`] + if test -z "$available_major" ; then + [available_major=0] + fi + [available=`echo $available | sed 's/[0-9]*[^0-9]//'`] + [available_minor=`echo $available | sed 's/[^0-9].*//'`] + if test -z "$available_minor" ; then + [available_minor=0] + fi + [available=`echo $available | sed 's/[0-9]*[^0-9]//'`] + [available_patch=`echo $available | sed 's/[^0-9].*//'`] + if test -z "$available_patch" ; then + [available_patch=0] + fi + # Convert the version tuple into a single number for easier comparison. + # Using base 100 should be safe since SWIG internally uses BCD values + # to encode its version number. + required_swig_vernum=`expr $required_major \* 10000 \ + \+ $required_minor \* 100 \+ $required_patch` + available_swig_vernum=`expr $available_major \* 10000 \ + \+ $available_minor \* 100 \+ $available_patch` + + if test $available_swig_vernum -lt $required_swig_vernum; then + AC_MSG_WARN([SWIG version >= $1 is required. You have $swig_version.]) + SWIG='' + m4_ifval([$3],[$3],[]) + else + AC_MSG_CHECKING([for SWIG library]) + SWIG_LIB=`$SWIG -swiglib` + AC_MSG_RESULT([$SWIG_LIB]) + m4_ifval([$2],[$2],[]) + fi + else + AC_MSG_WARN([cannot determine SWIG version]) + SWIG='' + m4_ifval([$3],[$3],[]) + fi + fi + AC_SUBST([SWIG_LIB]) +]) diff --git a/m4/ax_python_devel.m4 b/m4/ax_python_devel.m4 new file mode 100644 index 0000000..cf2163c --- /dev/null +++ b/m4/ax_python_devel.m4 @@ -0,0 +1,324 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_python_devel.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PYTHON_DEVEL([version]) +# +# DESCRIPTION +# +# Note: Defines as a precious variable "PYTHON_VERSION". Don't override it +# in your configure.ac. +# +# This macro checks for Python and tries to get the include path to +# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS) +# output variables. It also exports $(PYTHON_EXTRA_LIBS) and +# $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code. +# +# You can search for some particular version of Python by passing a +# parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please +# note that you *have* to pass also an operator along with the version to +# match, and pay special attention to the single quotes surrounding the +# version number. Don't use "PYTHON_VERSION" for this: that environment +# variable is declared as precious and thus reserved for the end-user. +# +# This macro should work for all versions of Python >= 2.1.0. As an end +# user, you can disable the check for the python version by setting the +# PYTHON_NOVERSIONCHECK environment variable to something else than the +# empty string. +# +# If you need to use this macro for an older Python version, please +# contact the authors. We're always open for feedback. +# +# LICENSE +# +# Copyright (c) 2009 Sebastian Huber +# Copyright (c) 2009 Alan W. Irwin +# Copyright (c) 2009 Rafael Laboissiere +# Copyright (c) 2009 Andrew Collier +# Copyright (c) 2009 Matteo Settenvini +# Copyright (c) 2009 Horst Knorr +# Copyright (c) 2013 Daniel Mullner +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 16 + +AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) +AC_DEFUN([AX_PYTHON_DEVEL],[ + # + # Allow the use of a (user set) custom python version + # + AC_ARG_VAR([PYTHON_VERSION],[The installed Python + version to use, for example '2.3'. This string + will be appended to the Python interpreter + canonical name.]) + + AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) + if test -z "$PYTHON"; then + AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path]) + PYTHON_VERSION="" + fi + + # + # Check for a version of Python >= 2.1.0 + # + AC_MSG_CHECKING([for a version of Python >= '2.1.0']) + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[[0]]; \ + print (ver >= '2.1.0')"` + if test "$ac_supports_python_ver" != "True"; then + if test -z "$PYTHON_NOVERSIONCHECK"; then + AC_MSG_RESULT([no]) + AC_MSG_FAILURE([ +This version of the AC@&t@_PYTHON_DEVEL macro +doesn't work properly with versions of Python before +2.1.0. You may need to re-run configure, setting the +variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, +PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. +Moreover, to disable this check, set PYTHON_NOVERSIONCHECK +to something else than an empty string. +]) + else + AC_MSG_RESULT([skip at user request]) + fi + else + AC_MSG_RESULT([yes]) + fi + + # + # if the macro parameter ``version'' is set, honour it + # + if test -n "$1"; then + AC_MSG_CHECKING([for a version of Python $1]) + ac_supports_python_ver=`$PYTHON -c "import sys; \ + ver = sys.version.split ()[[0]]; \ + print (ver $1)"` + if test "$ac_supports_python_ver" = "True"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AC_MSG_ERROR([this package requires Python $1. +If you have it installed, but it isn't the default Python +interpreter in your system path, please pass the PYTHON_VERSION +variable to configure. See ``configure --help'' for reference. +]) + PYTHON_VERSION="" + fi + fi + + # + # Check if you have distutils, else fail + # + AC_MSG_CHECKING([for the distutils Python package]) + ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` + if test -z "$ac_distutils_result"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AC_MSG_ERROR([cannot import Python module "distutils". +Please check your Python installation. The error was: +$ac_distutils_result]) + PYTHON_VERSION="" + fi + + # + # Check for Python include path + # + AC_MSG_CHECKING([for Python include path]) + if test -z "$PYTHON_CPPFLAGS"; then + python_path=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_inc ());"` + plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_inc (plat_specific=1));"` + if test -n "${python_path}"; then + if test "${plat_python_path}" != "${python_path}"; then + python_path="-I$python_path -I$plat_python_path" + else + python_path="-I$python_path" + fi + fi + PYTHON_CPPFLAGS=$python_path + fi + AC_MSG_RESULT([$PYTHON_CPPFLAGS]) + AC_SUBST([PYTHON_CPPFLAGS]) + + # + # Check for Python library path + # + AC_MSG_CHECKING([for Python library path]) + if test -z "$PYTHON_LDFLAGS"; then + # (makes two attempts to ensure we've got a version number + # from the interpreter) + ac_python_version=`cat<]], + [[Py_Initialize();]]) + ],[pythonexists=yes],[pythonexists=no]) + AC_LANG_POP([C]) + # turn back to default flags + CPPFLAGS="$ac_save_CPPFLAGS" + LIBS="$ac_save_LIBS" + + AC_MSG_RESULT([$pythonexists]) + + if test ! "x$pythonexists" = "xyes"; then + AC_MSG_FAILURE([ + Could not link test program to Python. Maybe the main Python library has been + installed in some non-standard library path. If so, pass it to configure, + via the LDFLAGS environment variable. + Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib" + ============================================================================ + ERROR! + You probably have to install the development version of the Python package + for your distribution. The exact name of this package varies among them. + ============================================================================ + ]) + PYTHON_VERSION="" + fi + + # + # all done! + # +]) diff --git a/m4/ax_swig_python.m4 b/m4/ax_swig_python.m4 new file mode 100644 index 0000000..bf22558 --- /dev/null +++ b/m4/ax_swig_python.m4 @@ -0,0 +1,64 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_swig_python.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_SWIG_PYTHON([use-shadow-classes = {no, yes}]) +# +# DESCRIPTION +# +# Checks for Python and provides the $(AX_SWIG_PYTHON_CPPFLAGS), and +# $(AX_SWIG_PYTHON_OPT) output variables. +# +# $(AX_SWIG_PYTHON_OPT) contains all necessary SWIG options to generate +# code for Python. Shadow classes are enabled unless the value of the +# optional first argument is exactly 'no'. If you need multi module +# support (provided by the AX_SWIG_MULTI_MODULE_SUPPORT macro) use +# $(AX_SWIG_PYTHON_LIBS) to link against the appropriate library. It +# contains the SWIG Python runtime library that is needed by the type +# check system for example. +# +# LICENSE +# +# Copyright (c) 2008 Sebastian Huber +# Copyright (c) 2008 Alan W. Irwin +# Copyright (c) 2008 Rafael Laboissiere +# Copyright (c) 2008 Andrew Collier +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 10 + +AU_ALIAS([SWIG_PYTHON], [AX_SWIG_PYTHON]) +AC_DEFUN([AX_SWIG_PYTHON],[ + AC_REQUIRE([AX_PKG_SWIG]) + AC_REQUIRE([AX_PYTHON_DEVEL]) + test "x$1" != "xno" || swig_shadow=" -noproxy" + AC_SUBST([AX_SWIG_PYTHON_OPT],[-python$swig_shadow]) + AC_SUBST([AX_SWIG_PYTHON_CPPFLAGS],[$PYTHON_CPPFLAGS]) +]) diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..7b4dd94 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,154 @@ +# Please keep sorted alphabetically +core_get_client_auth +core_getclient +core_getstats +core_getversion +drm_import_export +drm_read +drm_vma_limiter +drm_vma_limiter_cached +drm_vma_limiter_cpu +drm_vma_limiter_gtt +drv_hangman +drv_suspend +gem_alive +gem_bad_address +gem_bad_batch +gem_bad_blit +gem_bad_length +gem_bad_reloc +gem_basic +gem_caching +gem_close_race +gem_concurrent_blit +gem_cpu_reloc +gem_cs_prefetch +gem_cs_tlb +gem_ctx_bad_destroy +gem_ctx_bad_exec +gem_ctx_basic +gem_ctx_create +gem_ctx_exec +gem_ctx_thrash +gem_ctx_param_basic +gem_double_irq_loop +gem_dummy_reloc_loop +gem_evict_alignment +gem_evict_everything +gem_exec_bad_domains +gem_exec_big +gem_exec_blt +gem_exec_faulting_reloc +gem_exec_lut_handle +gem_exec_nop +gem_exec_params +gem_exec_parse +gem_fd_exhaustion +gem_fence_thrash +gem_fence_upload +gem_fenced_exec_thrash +gem_flink +gem_flink_race +gem_gpgpu_fill +gem_gtt_cpu_tlb +gem_gtt_hog +gem_gtt_speed +gem_hang +gem_hangcheck_forcewake +gem_largeobject +gem_linear_blits +gem_lut_handle +gem_madvise +gem_media_fill +gem_mmap +gem_mmap_gtt +gem_mmap_wc +gem_mmap_offset_exhaustion +gem_multi_bsd_sync_loop +gem_non_secure_batch +gem_partial_pwrite_pread +gem_persistent_relocs +gem_pin +gem_pipe_control_store_loop +gem_ppgtt +gem_pread +gem_pread_after_blit +gem_pwrite +gem_pwrite_pread +gem_readwrite +gem_reg_read +gem_reloc_overflow +gem_reloc_vs_gpu +gem_render_copy +gem_render_copy_redux +gem_render_linear_blits +gem_render_tiled_blits +gem_reset_stats +gem_ring_sync_copy +gem_ring_sync_loop +gem_ringfill +gem_seqno_wrap +gem_set_tiling_vs_blt +gem_set_tiling_vs_gtt +gem_set_tiling_vs_pwrite +gem_storedw_batches_loop +gem_storedw_loop_blt +gem_storedw_loop_bsd +gem_storedw_loop_render +gem_storedw_loop_vebox +gem_stress +gem_threaded_access_tiled +gem_tiled_blits +gem_tiled_fence_blits +gem_tiled_partial_pwrite_pread +gem_tiled_pread +gem_tiled_pread_pwrite +gem_tiled_swapping +gem_tiled_wb +gem_tiled_wc +gem_tiling_max_stride +gem_unfence_active_buffers +gem_unref_active_buffers +gem_userptr_blits +gem_wait +gem_write_read_ring_switch +gem_workarounds +gen3_mixed_blits +gen3_render_linear_blits +gen3_render_mixed_blits +gen3_render_tiledx_blits +gen3_render_tiledy_blits +gen7_forcewake_mt +kms_3d +kms_addfb +kms_cursor_crc +kms_fbc_crc +kms_fence_pin_leak +kms_flip +kms_flip_event_leak +kms_flip_tiling +kms_force_connector +kms_mmio_vs_cs_flip +kms_pipe_crc_basic +kms_plane +kms_psr_sink_crc +kms_pwrite_crc +kms_render +kms_rotation_crc +kms_setmode +kms_sink_crc_basic +kms_universal_plane +multi-tests.txt +pm_lpsp +pm_psr +pm_rc6_residency +pm_rpm +pm_rps +prime_nv_api +prime_nv_pcopy +prime_nv_test +prime_self_import +prime_udl +single-tests.txt +template +testdisplay diff --git a/tests/1080p-left.png b/tests/1080p-left.png new file mode 100644 index 0000000..67653ec Binary files /dev/null and b/tests/1080p-left.png differ diff --git a/tests/1080p-right.png b/tests/1080p-right.png new file mode 100644 index 0000000..d973565 Binary files /dev/null and b/tests/1080p-right.png differ diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..006555e --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,23 @@ +include Makefile.sources + +if BUILD_TESTS + +EXTRA_PROGRAMS = $(TESTS_progs) $(TESTS_progs_M) $(HANG) +EXTRA_DIST = $(TESTS_scripts) $(TESTS_scripts_M) $(scripts) $(IMAGES) $(common_files) + +CLEANFILES = $(EXTRA_PROGRAMS) + +AM_CFLAGS = $(DRM_CFLAGS) $(CWARNFLAGS) \ + -I$(srcdir)/.. \ + -I$(srcdir)/../lib \ + -include "$(srcdir)/../lib/check-ndebug.h" \ + -DIGT_DATADIR=\""$(abs_srcdir)"\" \ + $(NULL) + +LDADD = ../lib/libintel_tools.la $(PCIACCESS_LIBS) $(DRM_LIBS) + +LDADD += $(CAIRO_LIBS) $(LIBUDEV_LIBS) $(GLIB_LIBS) +AM_CFLAGS += $(CAIRO_CFLAGS) $(LIBUDEV_CFLAGS) $(GLIB_CFLAGS) + +endif + diff --git a/tests/Makefile.sources b/tests/Makefile.sources new file mode 100644 index 0000000..623a748 --- /dev/null +++ b/tests/Makefile.sources @@ -0,0 +1,16 @@ +noinst_PROGRAMS = \ + $(TESTS_progs) \ + $(NULL) + +TESTS_progs = \ + $(NULL) + +IMAGES = pass.png 1080p-left.png 1080p-right.png + +testdisplay_SOURCES = \ + testdisplay.c \ + testdisplay.h \ + testdisplay_hotplug.c \ + $(NULL) + +TESTS_progs += testdisplay diff --git a/tests/pass.png b/tests/pass.png new file mode 100644 index 0000000..36a5236 Binary files /dev/null and b/tests/pass.png differ diff --git a/tests/template.c b/tests/template.c new file mode 100644 index 0000000..24fd850 --- /dev/null +++ b/tests/template.c @@ -0,0 +1,80 @@ +/* + * Copyright © 2013 Intel Corporation + * + * 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: + * + */ + +#include "drmtest.h" + +/* + * Note that test function (and code called by them) should generally not return + * a variable indicating success/failure. Instead use the igt_require/igt_assert + * macros to skip out of the entire subtest. + * + * Also, helper functions should only return a status code if the callers have a + * real need to differentiate. If the only thing they do is call igt_assert or a + * similar macro then it'll result in simpler code when the check is moved + * completely into the helper. + */ +static void test_A(int fd) +{ +} + +static void test_B(int fd) +{ +} + +/* + * Variables which are written to in igt_fixtures/subtest blocks need to be + * allocated outside of the relevant function scope, otherwise gcc will wreak + * havoc (since these magic blocks use setjmp/longjmp internally). + * + * Common practice is to put variables used in the main test function into + * global scope, but only right above the main function itself (to avoid leaking + * it into other functions). + */ + +int drm_fd; + +igt_main +{ + igt_fixture { + drm_fd = drm_open_any(); + igt_require(drm_fd >= 0); + + /* Set up other interesting stuff shared by all tests. */ + } + + igt_subtest("A") + test_A(drm_fd); + igt_subtest("B") + test_B(drm_fd); + /* + * Note that subtest names can be programatically generated. See the + * various uses of igt_subtest_f for a few neat ideas. + */ + + igt_fixture { + close(drm_fd); + } +} diff --git a/tests/testdisplay.c b/tests/testdisplay.c new file mode 100644 index 0000000..35a39f9 --- /dev/null +++ b/tests/testdisplay.c @@ -0,0 +1,477 @@ +/* + * Copyright 2010 Intel Corporation + * Jesse Barnes + * + * 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 + * 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. + */ + +/* + * This program is intended for testing of display functionality. It should + * allow for testing of + * - hotplug + * - mode setting + * - clone & twin modes + * - panel fitting + * - test patterns & pixel generators + * Additional programs can test the detected outputs against VBT provided + * device lists (both docked & undocked). + * + * TODO: + * - pixel generator in transcoder + * - test pattern reg in pipe + * - test patterns on outputs (e.g. TV) + * - handle hotplug (leaks crtcs, can't handle clones) + * - allow mode force + * - expose output specific controls + * - e.g. DDC-CI brightness + * - HDMI controls + * - panel brightness + * - DP commands (e.g. poweroff) + * - verify outputs against VBT/physical connectors + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drmtest.h" +#include "testdisplay.h" +#include "igt_kms.h" + +#include +#include + + +static int tio_fd; +struct termios saved_tio; + +drmModeRes *resources; +int drm_fd, modes; +int test_all_modes = 0, test_preferred_mode = 0; + +unsigned int tiling = I915_TILING_X; //tiling = I915_TILING_NONE; +int sleep_between_modes = 5; +uint32_t depth = 24, stride, bpp = 32; + +drmModeModeInfo force_timing; + +int crtc_x, crtc_y, crtc_w, crtc_h, width, height; +unsigned int plane_fb_id; +unsigned int plane_crtc_id; +unsigned int plane_id; +int plane_width, plane_height; +static const uint32_t SPRITE_COLOR_KEY = 0x00aaaaaa; + +/* + * Mode setting with the kernel interfaces is a bit of a chore. + * First you have to find the connector in question and make sure the + * requested mode is available. + * Then you need to find the encoder attached to that connector so you + * can bind it with a free crtc. + */ +struct connector { + uint32_t id; + int mode_valid; + drmModeModeInfo mode; + drmModeEncoder *encoder; + drmModeConnector *connector; + int crtc; + int crtc_idx; + int pipe; +}; + +static void connector_find_preferred_mode(uint32_t connector_id, + unsigned long crtc_idx_mask, + int mode_num, struct connector *c) +{ + struct kmstest_connector_config config; + + if (!kmstest_get_connector_config(drm_fd, connector_id, crtc_idx_mask, + &config)) { + c->mode_valid = 0; + return; + } + + c->connector = config.connector; + c->encoder = config.encoder; + c->crtc = config.crtc->crtc_id; + c->crtc_idx = config.crtc_idx; + c->pipe = config.pipe; + + if (mode_num != -1) { + igt_assert(mode_num < config.connector->count_modes); + c->mode = config.connector->modes[mode_num]; + } else { + c->mode = config.default_mode; + } + c->mode_valid = 1; +} + +static void paint_image(cairo_t *cr, const char *file) +{ + int img_x, img_y, img_w, img_h, img_w_o, img_h_o; + double img_w_scale, img_h_scale; + + cairo_surface_t *image; + + img_y = height * (0.10 ); + img_h = height * 0.08 * 4; + img_w = img_h; + + img_x = (width / 2) - (img_w / 2); + + image = cairo_image_surface_create_from_png(file); + + img_w_o = cairo_image_surface_get_width(image); + img_h_o = cairo_image_surface_get_height(image); + + cairo_translate(cr, img_x, img_y); + + img_w_scale = (double)img_w / (double)img_w_o; + img_h_scale = (double)img_h / (double)img_h_o; + cairo_scale(cr, img_w_scale, img_h_scale); + + cairo_set_source_surface(cr, image, 0, 0); + cairo_scale(cr, 1, 1); + + cairo_paint(cr); + cairo_surface_destroy(image); +} + +static void paint_output_info(struct connector *c, struct igt_fb *fb) +{ + cairo_t *cr = igt_get_cairo_ctx(drm_fd, fb); + int l_width = fb->width; + int l_height = fb->height; + double str_width; + double x, y, top_y; + double max_width; + int i; + + paint_image(cr, IGT_DATADIR"/pass.png"); + + igt_assert(!cairo_status(cr)); + + cairo_destroy(cr); +} + +static void +set_mode(struct connector *c) +{ + unsigned int fb_id = 0; + struct igt_fb fb_info[2] = { }; + int j, test_mode_num, current_fb = 0, old_fb = -1; + + test_mode_num = 1; + if (test_all_modes) + test_mode_num = c->connector->count_modes; + + + + width = c->mode.hdisplay; + height = c->mode.vdisplay; + + fb_id = igt_create_fb(drm_fd, width, height, + igt_bpp_depth_to_drm_format(bpp, depth), + tiling, &fb_info[current_fb]); + paint_output_info(c, &fb_info[current_fb]); + + kmstest_dump_mode(&c->mode); + if (drmModeSetCrtc(drm_fd, c->crtc, fb_id, 0, 0, + &c->id, 1, &c->mode)) { + igt_warn("failed to set mode (%dx%d@%dHz): %s\n", width, height, c->mode.vrefresh, strerror(errno)); + } + + if (old_fb != -1) + igt_remove_fb(drm_fd, &fb_info[old_fb]); + old_fb = current_fb; + current_fb = 1 - current_fb; + + if (sleep_between_modes && test_all_modes) + sleep(sleep_between_modes); + + + if (test_all_modes) + igt_remove_fb(drm_fd, &fb_info[old_fb]); + + drmModeFreeEncoder(c->encoder); + drmModeFreeConnector(c->connector); +} + +/* + * Re-probe outputs and light up as many as possible. + * + * On Intel, we have two CRTCs that we can drive independently with + * different timings and scanout buffers. + * + * Each connector has a corresponding encoder, except in the SDVO case + * where an encoder may have multiple connectors. + */ +int update_display(void) +{ + struct connector *connectors; + int c; + + resources = drmModeGetResources(drm_fd); + if (!resources) { + igt_warn("drmModeGetResources failed: %s\n", strerror(errno)); + return 0; + } + + connectors = calloc(resources->count_connectors, + sizeof(struct connector)); + if (!connectors) + return 0; + + if (test_preferred_mode || test_all_modes ) { + unsigned long crtc_idx_mask = -1UL; + + /* Find any connected displays */ + for (c = 0; c < resources->count_connectors; c++) { + struct connector *connector = &connectors[c]; + + connector->id = resources->connectors[c]; + + connector_find_preferred_mode(connector->id, + crtc_idx_mask, + -1, + connector); + if (!connector->mode_valid) + continue; + + set_mode(connector); + + if (test_preferred_mode ) + crtc_idx_mask &= ~(1 << connector->crtc_idx); + + } + } + + free(connectors); + drmModeFreeResources(resources); + return 1; +} + +static char optstr[] = "3hiaf:s:d:p:mrto:j:"; + +static void __attribute__((noreturn)) usage(char *name, char opt) +{ + igt_info("usage: %s [-hiasdpmtf]\n", name); + igt_info("\t-i\tdump info\n"); + igt_info("\t-a\ttest all modes\n"); + igt_info("\t-s\t\tsleep between each mode test\n"); + igt_info("\t-d\t\tbit depth of scanout buffer\n"); + igt_info("\t-p\t,, test overlay plane\n"); + igt_info("\t-m\ttest the preferred mode\n"); + igt_info("\t-3\ttest all 3D modes\n"); + igt_info("\t-t\tuse a tiled framebuffer\n"); + igt_info("\t-j\tdo dpms off, optional arg to select dpms leve (1-3)\n"); + igt_info("\t-r\tprint a QR code on the screen whose content is \"pass\" for the automatic test\n"); + igt_info("\t-o\t,\tonly test specified mode on the specified display\n"); + igt_info("\t-f\t,,,,,\n"); + igt_info("\t\t,,,\n"); + igt_info("\t\ttest force mode\n"); + igt_info("\tDefault is to test all modes.\n"); + exit((opt != 'h') ? -1 : 0); +} + +#define dump_resource(res) if (res) dump_##res() + +static void cleanup_and_exit(int ret) +{ + close(drm_fd); + exit(ret); +} + +static gboolean input_event(GIOChannel *source, GIOCondition condition, + gpointer data) +{ + gchar buf[2]; + gsize count; + + count = read(g_io_channel_unix_get_fd(source), buf, sizeof(buf)); + if (buf[0] == 'q' && (count == 1 || buf[1] == '\n')) { + cleanup_and_exit(0); + } + + return TRUE; +} + +static void enter_exec_path( char **argv ) +{ + char *exec_path = NULL; + char *pos = NULL; + short len_path = 0; + int ret; + + len_path = strlen( argv[0] ); + exec_path = (char*) malloc(len_path); + + memcpy(exec_path, argv[0], len_path); + pos = strrchr(exec_path, '/'); + if (pos != NULL) + *(pos+1) = '\0'; + + ret = chdir(exec_path); + igt_assert(ret == 0); + free(exec_path); +} + +static void restore_termio_mode(int sig) +{ + tcsetattr(tio_fd, TCSANOW, &saved_tio); + close(tio_fd); +} + +static void set_termio_mode(void) +{ + struct termios tio; + + /* don't attempt to set terminal attributes if not in the foreground + * process group */ + if (getpgrp() != tcgetpgrp(STDOUT_FILENO)) + return; + + tio_fd = dup(STDIN_FILENO); + tcgetattr(tio_fd, &saved_tio); + igt_install_exit_handler(restore_termio_mode); + tio = saved_tio; + tio.c_lflag &= ~(ICANON | ECHO); + tcsetattr(tio_fd, TCSANOW, &tio); +} + +int main(int argc, char **argv) +{ + int c; + int ret = 0; + GIOChannel *stdinchannel; + GMainLoop *mainloop; + float force_clock; + struct option long_opts[] = { + {"help", 0, 0, 'h'}, + { 0, 0, 0, 0 } + }; + + igt_skip_on_simulation(); + + enter_exec_path( argv ); + + while ((c = getopt_long(argc, argv, optstr, long_opts, NULL)) != -1) { + switch (c) { + case 'a': + test_all_modes = 1; + break; + case 'f': + if(sscanf(optarg,"%f,%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu", + &force_clock,&force_timing.hdisplay, &force_timing.hsync_start,&force_timing.hsync_end,&force_timing.htotal, + &force_timing.vdisplay, &force_timing.vsync_start, &force_timing.vsync_end, &force_timing.vtotal)!= 9) + usage(argv[0], c); + force_timing.clock = force_clock*1000; + + break; + case 's': + sleep_between_modes = atoi(optarg); + break; + case 'd': + depth = atoi(optarg); + igt_info("using depth %d\n", depth); + break; + case 'm': + test_preferred_mode = 1; + break; + default: + /* fall through */ + case 'h': + usage(argv[0], c); + break; + } + } + + set_termio_mode(); + + if (!test_all_modes && !test_preferred_mode ) + test_all_modes = 1; + + drm_fd = drm_open_any(); + + kmstest_set_vt_graphics_mode(); + + mainloop = g_main_loop_new(NULL, FALSE); + if (!mainloop) { + igt_warn("failed to create glib mainloop\n"); + ret = -1; + goto out_close; + } + + if (!testdisplay_setup_hotplug()) { + igt_warn("failed to initialize hotplug support\n"); + goto out_mainloop; + } + + stdinchannel = g_io_channel_unix_new(0); + if (!stdinchannel) { + igt_warn("failed to create stdin GIO channel\n"); + goto out_hotplug; + } + + ret = g_io_add_watch(stdinchannel, G_IO_IN | G_IO_ERR, input_event, + NULL); + if (ret < 0) { + igt_warn("failed to add watch on stdin GIO channel\n"); + goto out_stdio; + } + + ret = 0; + + if (!update_display()) { + ret = 1; + goto out_stdio; + } + + if (test_all_modes) + goto out_stdio; + + g_main_loop_run(mainloop); + +out_stdio: + g_io_channel_shutdown(stdinchannel, TRUE, NULL); +out_hotplug: + testdisplay_cleanup_hotplug(); +out_mainloop: + g_main_loop_unref(mainloop); +out_close: + close(drm_fd); + + igt_assert(ret == 0); + + igt_exit(); +} diff --git a/tests/testdisplay.h b/tests/testdisplay.h new file mode 100644 index 0000000..962e621 --- /dev/null +++ b/tests/testdisplay.h @@ -0,0 +1,35 @@ +/* + * Copyright 2010 Intel Corporation + * Jesse Barnes + * + * 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 + * 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. + */ + +#include +#include +#include +#include + +extern int drm_fd; + +gboolean testdisplay_setup_hotplug(void); +void testdisplay_cleanup_hotplug(void); + +/* called by the hotplug code */ +int update_display(void); diff --git a/tests/testdisplay_hotplug.c b/tests/testdisplay_hotplug.c new file mode 100644 index 0000000..6e33ef3 --- /dev/null +++ b/tests/testdisplay_hotplug.c @@ -0,0 +1,138 @@ +/* + * Copyright 2010 Intel Corporation + * Jesse Barnes + * + * 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 + * 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. + */ + +#include +#include +#include + +#include "testdisplay.h" +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "igt_core.h" + +#if HAVE_UDEV +#include +static struct udev_monitor *uevent_monitor; +static struct udev *udev; +static GIOChannel *udevchannel; + +static gboolean hotplug_event(GIOChannel *source, GIOCondition condition, + gpointer data) +{ + struct udev_device *dev; + dev_t udev_devnum; + struct stat s; + const char *hotplug; + + dev = udev_monitor_receive_device(uevent_monitor); + if (!dev) + goto out; + + udev_devnum = udev_device_get_devnum(dev); + fstat(drm_fd, &s); + + hotplug = udev_device_get_property_value(dev, "HOTPLUG"); + + if (memcmp(&s.st_rdev, &udev_devnum, sizeof(dev_t)) == 0 && + hotplug && atoi(hotplug) == 1) + update_display(); + + udev_device_unref(dev); +out: + return TRUE; +} + + +gboolean testdisplay_setup_hotplug(void) +{ + int ret; + + udev = udev_new(); + if (!udev) { + igt_warn("failed to create udev object\n"); + goto out; + } + + uevent_monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (!uevent_monitor) { + igt_warn("failed to create udev event monitor\n"); + goto out; + } + + ret = udev_monitor_filter_add_match_subsystem_devtype(uevent_monitor, + "drm", + "drm_minor"); + if (ret < 0) { + igt_warn("failed to filter for drm events\n"); + goto out; + } + + ret = udev_monitor_enable_receiving(uevent_monitor); + if (ret < 0) { + igt_warn("failed to enable udev event reception\n"); + goto out; + } + + udevchannel = + g_io_channel_unix_new(udev_monitor_get_fd(uevent_monitor)); + if (!udevchannel) { + igt_warn("failed to create udev GIO channel\n"); + goto out; + } + + ret = g_io_add_watch(udevchannel, G_IO_IN | G_IO_ERR, hotplug_event, + udev); + if (ret < 0) { + igt_warn("failed to add watch on udev GIO channel\n"); + goto out; + } + + return TRUE; + +out: + testdisplay_cleanup_hotplug(); + return FALSE; +} + +void testdisplay_cleanup_hotplug(void) +{ + if (udevchannel) + g_io_channel_shutdown(udevchannel, TRUE, NULL); + if (uevent_monitor) + udev_monitor_unref(uevent_monitor); + if (udev) + udev_unref(udev); +} +#else +gboolean testdisplay_setup_hotplug(void) +{ + igt_warn("no hotplug support on this platform\n"); + return TRUE; +} + +void testdisplay_cleanup_hotplug(void) +{ +} +#endif -- cgit v1.2.3