Initial upstream commit

The history contained unlicensed code so everything got squashed, sorry.

Change-Id: I9f5775208f9df6fb29074bf3bc498f68cb17b3a0
Signed-off-by: Nico Huber <nico.huber@secunet.com>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..69f6818
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+/proof-allconfigs/
+/build/
+/dest/
+/*.gpr
+/.config
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..f9e358f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+name := gfxinit
+
+gfxinit-deplibs := libhw
+
+libhw-dir ?= ../libhwbase/dest
+include $(libhw-dir)/Makefile
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644
index 0000000..6eeafb9
--- /dev/null
+++ b/Makefile.inc
@@ -0,0 +1 @@
+subdirs-y += common
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..7efa9e1
--- /dev/null
+++ b/TODO
@@ -0,0 +1,5 @@
+medium	DP	re-read status if updated bit isn't set
+medium	DP	honor adjust requests also if training succeeded?
+unknown	DP	honor SINK_STATUS after training
+
+low	LVDS	sync polarity, 8bit colors, data format???
diff --git a/common/Makefile.inc b/common/Makefile.inc
new file mode 100644
index 0000000..8855e8a
--- /dev/null
+++ b/common/Makefile.inc
@@ -0,0 +1,57 @@
+gfxinit-y += hw-gfx-dp_aux_ch.adb
+gfxinit-y += hw-gfx-dp_aux_ch.ads
+gfxinit-y += hw-gfx-dp_defs.ads
+gfxinit-y += hw-gfx-dp_info.adb
+gfxinit-y += hw-gfx-dp_info.ads
+gfxinit-y += hw-gfx-dp_training.adb
+gfxinit-y += hw-gfx-dp_training.ads
+gfxinit-y += hw-gfx-edid.adb
+gfxinit-y += hw-gfx-edid.ads
+gfxinit-y += hw-gfx-gma-connector_info.adb
+gfxinit-y += hw-gfx-gma-connector_info.ads
+gfxinit-y += hw-gfx-gma-connectors.ads
+gfxinit-y += hw-gfx-gma-dp_aux_ch.ads
+gfxinit-y += hw-gfx-gma-dp_aux_request.adb
+gfxinit-y += hw-gfx-gma-dp_aux_request.ads
+gfxinit-y += hw-gfx-gma-dp_info.ads
+gfxinit-y += hw-gfx-gma-i2c.adb
+gfxinit-y += hw-gfx-gma-i2c.ads
+gfxinit-y += hw-gfx-gma-panel.adb
+gfxinit-y += hw-gfx-gma-panel.ads
+gfxinit-y += hw-gfx-gma-pch-fdi.adb
+gfxinit-y += hw-gfx-gma-pch-fdi.ads
+gfxinit-y += hw-gfx-gma-pch-sideband.adb
+gfxinit-y += hw-gfx-gma-pch-sideband.ads
+gfxinit-y += hw-gfx-gma-pch-transcoder.adb
+gfxinit-y += hw-gfx-gma-pch-transcoder.ads
+gfxinit-y += hw-gfx-gma-pch-vga.adb
+gfxinit-y += hw-gfx-gma-pch-vga.ads
+gfxinit-y += hw-gfx-gma-pch.ads
+gfxinit-y += hw-gfx-gma-pipe_setup.adb
+gfxinit-y += hw-gfx-gma-pipe_setup.ads
+gfxinit-y += hw-gfx-gma-port_detect.ads
+gfxinit-y += hw-gfx-gma-registers.adb
+gfxinit-y += hw-gfx-gma-registers.ads
+gfxinit-y += hw-gfx-gma.adb
+gfxinit-y += hw-gfx-gma.ads
+gfxinit-y += hw-gfx-i2c.ads
+gfxinit-y += hw-gfx.ads
+gfxinit-$(CONFIG_HWBASE_DYNAMIC_MMIO) += hw-gfx-framebuffer_filler.adb
+gfxinit-$(CONFIG_HWBASE_DYNAMIC_MMIO) += hw-gfx-framebuffer_filler.ads
+
+$(call src-to-obj,,$(dir)/hw-gfx-gma-config).ads: $(dir)/hw-gfx-gma-config.ads.template $(cnf)
+	printf "    GENERATE   $(patsubst /%,%,$(subst $(obj)/,,$@))\n"
+	sed -e's/<<CPU>>/$(CONFIG_GFX_GMA_CPU)/' \
+	    -e's/<<CPU_VARIANT>>/$(CONFIG_GFX_GMA_CPU_VARIANT)/' \
+	    -e's/<<INTERNAL_PORT>>/$(CONFIG_GFX_GMA_INTERNAL_PORT)/' \
+	    -e's/<<DEFAULT_MMIO_BASE>>/$(CONFIG_GFX_GMA_DEFAULT_MMIO)/' \
+	    $< >$@
+gfxinit-gen-y += $(call src-to-obj,,$(dir)/hw-gfx-gma-config).ads
+
+ifneq ($(filter Ironlake Sandybridge Ivybridge,$(CONFIG_GFX_GMA_CPU)),)
+subdirs-y += ironlake
+else ifneq ($(filter Haswell Broadwell,$(CONFIG_GFX_GMA_CPU)),)
+subdirs-y += haswell_shared haswell
+else
+subdirs-y += haswell_shared skylake
+endif
diff --git a/common/haswell/Makefile.inc b/common/haswell/Makefile.inc
new file mode 100644
index 0000000..682af33
--- /dev/null
+++ b/common/haswell/Makefile.inc
@@ -0,0 +1,8 @@
+gfxinit-y += hw-gfx-gma-plls-lcpll.ads
+gfxinit-y += hw-gfx-gma-plls-wrpll.adb
+gfxinit-y += hw-gfx-gma-plls-wrpll.ads
+gfxinit-y += hw-gfx-gma-plls.adb
+gfxinit-y += hw-gfx-gma-plls.ads
+gfxinit-y += hw-gfx-gma-power_and_clocks.ads
+gfxinit-y += hw-gfx-gma-spll.adb
+gfxinit-y += hw-gfx-gma-spll.ads
diff --git a/common/haswell/hw-gfx-gma-plls-lcpll.ads b/common/haswell/hw-gfx-gma-plls-lcpll.ads
new file mode 100644
index 0000000..7fa45ba
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-plls-lcpll.ads
@@ -0,0 +1,28 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs.LCPLL
+is
+
+   type Fixed_LCPLLs_Array is array (HW.GFX.DP_Bandwidth) of LCPLLs;
+
+   Fixed_LCPLLs : constant Fixed_LCPLLs_Array := Fixed_LCPLLs_Array'
+     (DP_Bandwidth_5_4  => LCPLL0,
+      DP_Bandwidth_2_7  => LCPLL1,
+      DP_Bandwidth_1_62 => LCPLL2);
+
+   type Value_Array is array (LCPLLs) of Word32;
+   Register_Value : constant Value_Array := Value_Array'
+     (LCPLL0 => 0 * 2 ** 29, LCPLL1 => 1 * 2 ** 29, LCPLL2 => 2 * 2 ** 29);
+
+end HW.GFX.GMA.PLLs.LCPLL;
diff --git a/common/haswell/hw-gfx-gma-plls-wrpll.adb b/common/haswell/hw-gfx-gma-plls-wrpll.adb
new file mode 100644
index 0000000..c9ab532
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-plls-wrpll.adb
@@ -0,0 +1,351 @@
+--
+-- Copyright (C) 2015 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.PLLs.WRPLL is
+
+   ----------------------------------------------------------------------------
+   --
+   --  Divider calculation as found in Linux' i915 driver
+   --
+   --  Copyright (C) 2012 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:
+   --     Eugeni Dodonov <eugeni.dodonov@intel.com>
+   --
+
+   LC_FREQ     : constant := 2700;           -- in MHz
+   LC_FREQ_2K  : constant := LC_FREQ * 2000; -- in 500Hz
+
+   P_MIN       : constant := 2;
+   P_MAX       : constant := 62; -- i915 says 64, but this would overflow 6-bit
+   P_INC       : constant := 2;
+
+   -- Constraints for PLL good behavior
+   REF_MIN     : constant := 48;
+   REF_MAX     : constant := 400;
+   VCO_MIN     : constant := 2400;
+   VCO_MAX     : constant := 4800;
+
+   type R2_Range is new Natural range 0 .. LC_FREQ * 2 / REF_MIN;
+   type N2_Range is new Natural range 0 .. VCO_MAX * Natural (R2_Range'Last) / LC_FREQ;
+   type P_Range is new Natural range 0 .. P_MAX;
+
+   type RNP is record
+      P  : P_Range;
+      N2 : N2_Range;
+      R2 : R2_Range;
+   end record;
+   Invalid_RNP : constant RNP := RNP'(0, 0, 0);
+
+   function Get_Budget_For_Freq
+     (Clock : HW.GFX.Frequency_Type)
+      return Word64
+   is
+      Result : Word64;
+   begin
+      case Clock is
+         when  25175000 |
+               25200000 |
+               27000000 |
+               27027000 |
+               37762500 |
+               37800000 |
+               40500000 |
+               40541000 |
+               54000000 |
+               54054000 |
+               59341000 |
+               59400000 |
+               72000000 |
+               74176000 |
+               74250000 |
+               81000000 |
+               81081000 |
+               89012000 |
+               89100000 |
+              108000000 |
+              108108000 |
+              111264000 |
+              111375000 |
+              148352000 |
+              148500000 |
+              162000000 |
+              162162000 |
+              222525000 |
+              222750000 |
+              296703000 |
+              297000000 =>
+            Result := 0;
+         when 233500000 |
+              245250000 |
+              247750000 |
+              253250000 |
+              298000000 =>
+            Result := 1500;
+         when 169128000 |
+              169500000 |
+              179500000 |
+              202000000 =>
+            Result := 2000;
+         when 256250000 |
+              262500000 |
+              270000000 |
+              272500000 |
+              273750000 |
+              280750000 |
+              281250000 |
+              286000000 |
+              291750000 =>
+            Result := 4000;
+         when 267250000 |
+              268500000 =>
+            Result := 5000;
+         when others =>
+            Result := 1000;
+      end case;
+      return Result;
+   end Get_Budget_For_Freq;
+
+   procedure Update_RNP
+     (Freq_2K  : in     Word64;
+      Budget   : in     Word64;
+      R2       : in     R2_Range;
+      N2       : in     N2_Range;
+      P        : in     P_Range;
+      Best     : in out RNP)
+   with
+      Depends => (Best =>+ (Freq_2K, Budget, R2, N2, P))
+   is
+      use type HW.Word64;
+
+      function Abs_Diff (A, B : Word64) return Word64
+      is
+         Result : Word64;
+      begin
+         if A > B then
+            Result := A - B;
+         else
+            Result := B - A;
+         end if;
+         return Result;
+      end Abs_Diff;
+
+      A, B, C, D, Diff, Diff_Best : Word64;
+   begin
+      -- No best (r,n,p) yet */
+      if Best.P = 0 then
+         Best.P   := P;
+         Best.N2  := N2;
+         Best.R2  := R2;
+      else
+         -- Config clock is (LC_FREQ_2K / 2000) * N / (P * R), which compares to
+         -- freq2k.
+         --
+         -- delta = 1e6 *
+         --         abs(freq2k - (LC_FREQ_2K * n2/(p * r2))) /
+         --         freq2k;
+         --
+         -- and we would like delta <= budget.
+         --
+         -- If the discrepancy is above the PPM-based budget, always prefer to
+         -- improve upon the previous solution.  However, if you're within the
+         -- budget, try to maximize Ref * VCO, that is N / (P * R^2).
+         A := Freq_2K * Budget * Word64 (P) * Word64 (R2);
+         B := Freq_2K * Budget * Word64 (Best.P) * Word64 (Best.R2);
+         Diff := Abs_Diff
+           (Freq_2K * Word64 (P) * Word64 (R2),
+            LC_FREQ_2K * Word64 (N2));
+         Diff_Best := Abs_Diff
+           (Freq_2K * Word64 (Best.P) * Word64 (Best.R2),
+            LC_FREQ_2K * Word64 (Best.N2));
+         C := 1000000 * Diff;
+         D := 1000000 * Diff_Best;
+
+         if A < C and B < D then
+            -- If both are above the Budget, pick the closer
+            if Word64 (Best.P) * Word64 (Best.R2) * Diff
+                  < Word64 (P) * Word64 (R2) * Diff_Best
+            then
+               Best.P := P;
+               Best.N2 := N2;
+               Best.R2 := R2;
+            end if;
+         elsif A >= C and B < D then
+            -- If A is below the threshold but B is above it?  Update.
+            Best.P := P;
+            Best.N2 := N2;
+            Best.R2 := R2;
+         elsif A >= C and B >= D then
+            -- Both are below the limit, so pick the higher N2/(R2*R2)
+            if Word64 (N2) * Word64 (Best.R2) * Word64 (Best.R2)
+                  > Word64 (Best.N2) * Word64 (R2) * Word64 (R2)
+            then
+               Best.P := P;
+               Best.N2 := N2;
+               Best.R2 := R2;
+            end if;
+         end if;
+         -- Otherwise A < C && B >= D, do nothing
+      end if;
+   end Update_RNP;
+
+   procedure Calculate_WRPLL
+     (Clock    : in     HW.GFX.Frequency_Type;
+      R2_Out   :    out R2_Range;
+      N2_Out   :    out N2_Range;
+      P_Out    :    out P_Range)
+   with
+      Global => null,
+      Pre => True,
+      Post => True
+   is
+      use type HW.Word64;
+
+      Freq_2K  : Word64;
+      Budget   : Word64;
+      Best     : RNP := Invalid_RNP;
+   begin
+      Freq_2K  := Word64 (Clock) / 100;   -- PLL output should be 5x
+                                                -- the pixel clock
+      Budget   := Get_Budget_For_Freq (Clock);
+
+      -- Special case handling for 540MHz pixel clock: bypass WR PLL entirely
+      -- and directly pass the LC PLL to it. */
+      if Freq_2K = 5400000 then
+         N2_Out   := 2;
+         P_Out    := 1;
+         R2_Out   := 2;
+      else
+         -- Ref = LC_FREQ / R, where Ref is the actual reference input seen by
+         -- the WR PLL.
+         --
+         -- We want R so that REF_MIN <= Ref <= REF_MAX.
+         -- Injecting R2 = 2 * R gives:
+         --   REF_MAX * r2 > LC_FREQ * 2 and
+         --   REF_MIN * r2 < LC_FREQ * 2
+         --
+         -- Which means the desired boundaries for r2 are:
+         --  LC_FREQ * 2 / REF_MAX < r2 < LC_FREQ * 2 / REF_MIN
+         --
+         for R2 in R2_Range range
+            LC_FREQ * 2 / REF_MAX + 1 .. LC_FREQ * 2 / REF_MIN
+         loop
+            -- VCO = N * Ref, that is: VCO = N * LC_FREQ / R
+            --
+            -- Once again we want VCO_MIN <= VCO <= VCO_MAX.
+            -- Injecting R2 = 2 * R and N2 = 2 * N, we get:
+            --   VCO_MAX * r2 > n2 * LC_FREQ and
+            --   VCO_MIN * r2 < n2 * LC_FREQ)
+            --
+            -- Which means the desired boundaries for n2 are:
+            -- VCO_MIN * r2 / LC_FREQ < n2 < VCO_MAX * r2 / LC_FREQ
+            for N2 in N2_Range range
+               N2_Range (VCO_MIN * Natural (R2) / LC_FREQ + 1)
+                  .. N2_Range (VCO_MAX * Natural (R2) / LC_FREQ)
+            loop
+               for P_Fract in Natural range P_MIN / P_INC .. P_MAX / P_INC
+               loop
+                  Update_RNP
+                    (Freq_2K, Budget, R2, N2, P_Range (P_Fract * P_INC), Best);
+               end loop;
+            end loop;
+         end loop;
+
+         N2_Out   := Best.N2;
+         P_Out    := Best.P;
+         R2_Out   := Best.R2;
+      end if;
+
+   end Calculate_WRPLL;
+
+   --
+   ----------------------------------------------------------------------------
+
+   type Regs is array (WRPLLs) of Registers.Registers_Index;
+
+   WRPLL_CTL : constant Regs := Regs'(Registers.WRPLL_CTL_1, Registers.WRPLL_CTL_2);
+   WRPLL_CTL_PLL_ENABLE    : constant := 1 * 2 ** 31;
+   WRPLL_CTL_SELECT_LCPLL  : constant := 3 * 2 ** 28;
+
+   function WRPLL_CTL_DIVIDER_FEEDBACK (N2 : N2_Range) return Word32
+   is
+   begin
+      return Word32 (N2) * 2 ** 16;
+   end WRPLL_CTL_DIVIDER_FEEDBACK;
+
+   function WRPLL_CTL_DIVIDER_POST (P : P_Range) return Word32
+   is
+   begin
+      return Word32 (P) * 2 ** 8;
+   end WRPLL_CTL_DIVIDER_POST;
+
+   function WRPLL_CTL_DIVIDER_REFERENCE (R2 : R2_Range) return Word32
+   is
+   begin
+      return Word32 (R2) * 2 ** 0;
+   end WRPLL_CTL_DIVIDER_REFERENCE;
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (PLL            : in     WRPLLs;
+      Target_Clock   : in     Frequency_Type;
+      Success        :    out Boolean)
+   is
+      R2 : R2_Range;
+      N2 : N2_Range;
+      P  : P_Range;
+   begin
+      Calculate_WRPLL (Target_Clock, R2, N2, P);
+      Registers.Write
+        (Register => WRPLL_CTL (PLL),
+         Value    => WRPLL_CTL_PLL_ENABLE or
+                     WRPLL_CTL_SELECT_LCPLL or
+                     WRPLL_CTL_DIVIDER_FEEDBACK (N2) or
+                     WRPLL_CTL_DIVIDER_POST (P) or
+                     WRPLL_CTL_DIVIDER_REFERENCE (R2));
+      Registers.Posting_Read (WRPLL_CTL (PLL));
+      Time.U_Delay (20);
+
+      Success := True;
+   end On;
+
+   procedure Off (PLL : WRPLLs)
+   is
+   begin
+      Registers.Unset_Mask (WRPLL_CTL (PLL), WRPLL_CTL_PLL_ENABLE);
+   end Off;
+
+end HW.GFX.GMA.PLLs.WRPLL;
diff --git a/common/haswell/hw-gfx-gma-plls-wrpll.ads b/common/haswell/hw-gfx-gma-plls-wrpll.ads
new file mode 100644
index 0000000..48f6850
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-plls-wrpll.ads
@@ -0,0 +1,28 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs.WRPLL
+is
+
+   type Value_Array is array (WRPLLs) of Word32;
+   Register_Value : constant Value_Array := Value_Array'
+     (WRPLL0 => 4 * 2 ** 29, WRPLL1 => 5 * 2 ** 29);
+
+   procedure On
+     (PLL            : in     WRPLLs;
+      Target_Clock   : in     Frequency_Type;
+      Success        :    out Boolean);
+
+   procedure Off (PLL : WRPLLs);
+
+end HW.GFX.GMA.PLLs.WRPLL;
diff --git a/common/haswell/hw-gfx-gma-plls.adb b/common/haswell/hw-gfx-gma-plls.adb
new file mode 100644
index 0000000..45faa66
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-plls.adb
@@ -0,0 +1,130 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.PLLs.LCPLL;
+with HW.GFX.GMA.PLLs.WRPLL;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PLLs
+with
+   Refined_State => (State => PLLs)
+is
+
+   type Count_Range is new Natural range 0 .. 2;
+
+   type PLL_State is record
+      Use_Count   : Count_Range;
+      Mode        : Mode_Type;
+   end record;
+
+   type PLL_State_Array is array (WRPLLs) of PLL_State;
+
+   PLLs : PLL_State_Array;
+
+   procedure Initialize
+   is
+   begin
+      PLLs := (WRPLLs => (Use_Count => 0, Mode => Invalid_Mode));
+   end Initialize;
+
+   procedure Alloc_Configurable
+     (Mode     : in     Mode_Type;
+      PLL      :    out T;
+      Success  :    out Boolean)
+   with
+      Pre => True
+   is
+   begin
+      -- try to find shareable PLL
+      for P in WRPLLs loop
+         Success := PLLs (P).Use_Count /= 0 and
+                     PLLs (P).Use_Count /= Count_Range'Last and
+                     PLLs (P).Mode = Mode;
+         if Success then
+            PLL := P;
+            PLLs (PLL).Use_Count := PLLs (PLL).Use_Count + 1;
+            return;
+         end if;
+      end loop;
+
+      -- try to find free PLL
+      for P in WRPLLs loop
+         if PLLs (P).Use_Count = 0 then
+            PLL := P;
+            WRPLL.On (PLL, Mode.Dotclock, Success);
+            if Success then
+               PLLs (PLL) := (Use_Count => 1, Mode => Mode);
+            end if;
+            return;
+         end if;
+      end loop;
+
+      PLL := Invalid;
+   end Alloc_Configurable;
+
+   procedure Alloc
+     (Port_Cfg : in     Port_Config;
+      PLL      :    out T;
+      Success  :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Port = DIGI_E then
+         PLL := Invalid;
+         Success := True;
+      elsif Port_Cfg.Display = DP then
+         PLL := LCPLL.Fixed_LCPLLs (Port_Cfg.DP.Bandwidth);
+         Success := True;
+      else
+         Alloc_Configurable (Port_Cfg.Mode, PLL, Success);
+      end if;
+   end Alloc;
+
+   procedure Free (PLL : T)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if PLL in WRPLLs then
+         if PLLs (PLL).Use_Count /= 0 then
+            PLLs (PLL).Use_Count := PLLs (PLL).Use_Count - 1;
+            if PLLs (PLL).Use_Count = 0 then
+               WRPLL.Off (PLL);
+            end if;
+         end if;
+      end if;
+   end Free;
+
+   procedure All_Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      for PLL in WRPLLs loop
+         WRPLL.Off (PLL);
+      end loop;
+   end All_Off;
+
+   function Register_Value (PLL : T) return Word32
+   is
+   begin
+      return
+        (if    PLL in LCPLLs then LCPLL.Register_Value (PLL)
+         elsif PLL in WRPLLs then WRPLL.Register_Value (PLL)
+         else  0);
+   end Register_Value;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/haswell/hw-gfx-gma-plls.ads b/common/haswell/hw-gfx-gma-plls.ads
new file mode 100644
index 0000000..9b158ae
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-plls.ads
@@ -0,0 +1,40 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs
+with
+   Abstract_State => (State with Part_Of => GMA.State)
+is
+
+   -- XXX: Types should be private (but that triggers a bug in SPARK GPL 2016)
+   type T is (Invalid_PLL, LCPLL0, LCPLL1, LCPLL2, WRPLL0, WRPLL1);
+   subtype LCPLLs is T range LCPLL0 .. LCPLL2;
+   subtype WRPLLs is T range WRPLL0 .. WRPLL1;
+   Invalid : constant T := Invalid_PLL;
+
+   procedure Initialize
+   with
+      Global => (Output => State);
+
+   procedure Alloc
+     (Port_Cfg : in     Port_Config;
+      PLL      :    out T;
+      Success  :    out Boolean);
+
+   procedure Free (PLL : T);
+
+   procedure All_Off;
+
+   function Register_Value (PLL : T) return Word32;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/haswell/hw-gfx-gma-power_and_clocks.ads b/common/haswell/hw-gfx-gma-power_and_clocks.ads
new file mode 100644
index 0000000..5e040ed
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-power_and_clocks.ads
@@ -0,0 +1,17 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Power_And_Clocks_Haswell;
+
+private package HW.GFX.GMA.Power_And_Clocks
+   renames HW.GFX.GMA.Power_And_Clocks_Haswell;
diff --git a/common/haswell/hw-gfx-gma-spll.adb b/common/haswell/hw-gfx-gma-spll.adb
new file mode 100644
index 0000000..71c9cd4
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-spll.adb
@@ -0,0 +1,43 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.SPLL is
+
+   SPLL_CTL_PLL_ENABLE        : constant := 1 * 2 ** 31;
+   SPLL_CTL_REF_SEL_MASK      : constant := 3 * 2 ** 28;
+   SPLL_CTL_REF_SEL_SSC       : constant := 1 * 2 ** 28;
+   SPLL_CTL_REF_SEL_NON_SSC   : constant := 2 * 2 ** 28;
+   SPLL_CTL_FREQ_SEL_MASK     : constant := 3 * 2 ** 26;
+   SPLL_CTL_FREQ_SEL_810      : constant := 0 * 2 ** 26;
+   SPLL_CTL_FREQ_SEL_1350     : constant := 1 * 2 ** 26;
+
+   procedure On is
+   begin
+      Registers.Write
+        (Register => Registers.SPLL_CTL,
+         Value    => SPLL_CTL_PLL_ENABLE or
+                     SPLL_CTL_REF_SEL_SSC or
+                     SPLL_CTL_FREQ_SEL_1350);
+      Registers.Posting_Read (Registers.SPLL_CTL);
+      Time.U_Delay (20);
+   end On;
+
+   procedure Off is
+   begin
+      Registers.Unset_Mask (Registers.SPLL_CTL, SPLL_CTL_PLL_ENABLE);
+   end Off;
+
+end HW.GFX.GMA.SPLL;
diff --git a/common/haswell/hw-gfx-gma-spll.ads b/common/haswell/hw-gfx-gma-spll.ads
new file mode 100644
index 0000000..223f856
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-spll.ads
@@ -0,0 +1,20 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.SPLL is
+
+   procedure On;
+
+   procedure Off;
+
+end HW.GFX.GMA.SPLL;
diff --git a/common/haswell_shared/Makefile.inc b/common/haswell_shared/Makefile.inc
new file mode 100644
index 0000000..45b505b
--- /dev/null
+++ b/common/haswell_shared/Makefile.inc
@@ -0,0 +1,6 @@
+gfxinit-y += hw-gfx-gma-connectors-ddi.adb
+gfxinit-y += hw-gfx-gma-connectors-ddi.ads
+gfxinit-y += hw-gfx-gma-connectors.adb
+gfxinit-y += hw-gfx-gma-port_detect.adb
+gfxinit-y += hw-gfx-gma-power_and_clocks_haswell.adb
+gfxinit-y += hw-gfx-gma-power_and_clocks_haswell.ads
diff --git a/common/haswell_shared/hw-gfx-gma-connectors-ddi.adb b/common/haswell_shared/hw-gfx-gma-connectors-ddi.adb
new file mode 100644
index 0000000..0459681
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-connectors-ddi.adb
@@ -0,0 +1,554 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+with HW.GFX.DP_Training;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.PCH.FDI;
+with HW.GFX.GMA.PCH.Transcoder;
+with HW.GFX.GMA.PCH.VGA;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.DP_Aux_Ch;
+with HW.GFX.GMA.SPLL;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors.DDI is
+
+   DDI_BUF_CTL_BUFFER_ENABLE        : constant :=  1 * 2 ** 31;
+   DDI_BUF_CTL_TRANS_SELECT_MASK    : constant := 15 * 2 ** 24;
+   DDI_BUF_CTL_PORT_REVERSAL        : constant :=  1 * 2 ** 16;
+   DDI_BUF_CTL_IDLE_STATUS          : constant :=  1 * 2 **  7;
+   DDI_BUF_CTL_DDI_A_LANE_CAP       : constant :=  1 * 2 **  4;
+   DDI_BUF_CTL_PORT_WIDTH_MASK      : constant :=  7 * 2 **  1;
+   DDI_BUF_CTL_PORT_WIDTH_1_LANE    : constant :=  0 * 2 **  1;
+   DDI_BUF_CTL_PORT_WIDTH_2_LANES   : constant :=  1 * 2 **  1;
+   DDI_BUF_CTL_PORT_WIDTH_4_LANES   : constant :=  3 * 2 **  1;
+   DDI_BUF_CTL_INIT_DISPLAY_DETECT  : constant :=  1 * 2 **  0;
+
+   subtype DDI_BUF_CTL_TRANS_SELECT_T is Natural range 0 .. 9;
+   function DDI_BUF_CTL_TRANS_SELECT
+     (Sel : DDI_BUF_CTL_TRANS_SELECT_T)
+      return Word32;
+
+   type DDI_BUF_CTL_PORT_WIDTH_T is array (HW.GFX.DP_Lane_Count) of Word32;
+   DDI_BUF_CTL_PORT_WIDTH : constant DDI_BUF_CTL_PORT_WIDTH_T :=
+      DDI_BUF_CTL_PORT_WIDTH_T'
+     (HW.GFX.DP_Lane_Count_1 => DDI_BUF_CTL_PORT_WIDTH_1_LANE,
+      HW.GFX.DP_Lane_Count_2 => DDI_BUF_CTL_PORT_WIDTH_2_LANES,
+      HW.GFX.DP_Lane_Count_4 => DDI_BUF_CTL_PORT_WIDTH_4_LANES);
+
+   DP_TP_CTL_TRANSPORT_ENABLE       : constant := 1 * 2 ** 31;
+   DP_TP_CTL_MODE_SST               : constant := 0 * 2 ** 27;
+   DP_TP_CTL_MODE_MST               : constant := 1 * 2 ** 27;
+   DP_TP_CTL_FORCE_ACT              : constant := 1 * 2 ** 25;
+   DP_TP_CTL_ENHANCED_FRAME_ENABLE  : constant := 1 * 2 ** 18;
+   DP_TP_CTL_FDI_AUTOTRAIN          : constant := 1 * 2 ** 15;
+   DP_TP_CTL_LINK_TRAIN_MASK        : constant := 7 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_PAT1        : constant := 0 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_PAT2        : constant := 1 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_PAT3        : constant := 4 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_IDLE        : constant := 2 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_NORMAL      : constant := 3 * 2 **  8;
+   DP_TP_CTL_SCRAMBLE_DISABLE       : constant := 1 * 2 **  7;
+   DP_TP_CTL_ALT_SCRAMBLER_RESET    : constant := 1 * 2 **  6;
+
+   type DP_TP_CTL_LINK_TRAIN_Array is
+      array (DP_Info.Training_Pattern) of Word32;
+   DP_TP_CTL_LINK_TRAIN : constant DP_TP_CTL_LINK_TRAIN_Array :=
+      DP_TP_CTL_LINK_TRAIN_Array'
+     (DP_Info.TP_1      => DP_TP_CTL_LINK_TRAIN_PAT1 or DP_TP_CTL_SCRAMBLE_DISABLE,
+      DP_Info.TP_2      => DP_TP_CTL_LINK_TRAIN_PAT2 or DP_TP_CTL_SCRAMBLE_DISABLE,
+      DP_Info.TP_3      => DP_TP_CTL_LINK_TRAIN_PAT3 or DP_TP_CTL_SCRAMBLE_DISABLE,
+      DP_Info.TP_Idle   => DP_TP_CTL_LINK_TRAIN_IDLE,
+      DP_Info.TP_None   => DP_TP_CTL_LINK_TRAIN_NORMAL);
+
+   DP_TP_STATUS_MIN_IDLES_SENT      : constant := 1 * 2 ** 25;
+   DP_TP_STATUS_FDI_AUTO_TRAIN_DONE : constant := 1 * 2 ** 12;
+
+   PORT_CLK_SEL_LCPLL2700  : constant := 0 * 2 ** 29; -- not on ULX
+   PORT_CLK_SEL_LCPLL1350  : constant := 1 * 2 ** 29;
+   PORT_CLK_SEL_LCPLL810   : constant := 2 * 2 ** 29;
+   PORT_CLK_SEL_SPLL       : constant := 3 * 2 ** 29;
+   PORT_CLK_SEL_WRPLL1     : constant := 4 * 2 ** 29;
+   PORT_CLK_SEL_WRPLL2     : constant := 5 * 2 ** 29;
+   PORT_CLK_SEL_NONE       : constant := 7 * 2 ** 29;
+
+   type PORT_CLK_SEL_LCPLL_T is array (HW.GFX.DP_Bandwidth) of Word32;
+   PORT_CLK_SEL_LCPLL : constant PORT_CLK_SEL_LCPLL_T :=
+      PORT_CLK_SEL_LCPLL_T'
+     (HW.GFX.DP_Bandwidth_1_62 => PORT_CLK_SEL_LCPLL810,
+      HW.GFX.DP_Bandwidth_2_7  => PORT_CLK_SEL_LCPLL1350,
+      HW.GFX.DP_Bandwidth_5_4  => PORT_CLK_SEL_LCPLL2700);
+
+   type DDI_Registers is record
+      BUF_CTL        : Registers.Registers_Index;
+      DP_TP_CTL      : Registers.Registers_Index;
+      DP_TP_STATUS   : Registers.Registers_Invalid_Index;
+      PORT_CLK_SEL   : Registers.Registers_Index;
+   end record;
+
+   type DDI_Registers_Array is array (Digital_Port) of DDI_Registers;
+
+   DDI_Regs : constant DDI_Registers_Array := DDI_Registers_Array'
+     (DIGI_A => DDI_Registers'
+        (BUF_CTL        => Registers.DDI_BUF_CTL_A,
+         DP_TP_CTL      => Registers.DP_TP_CTL_A,
+         DP_TP_STATUS   => Registers.Invalid_Register,
+         PORT_CLK_SEL   => Registers.PORT_CLK_SEL_DDIA),
+      DIGI_B => DDI_Registers'
+        (BUF_CTL        => Registers.DDI_BUF_CTL_B,
+         DP_TP_CTL      => Registers.DP_TP_CTL_B,
+         DP_TP_STATUS   => Registers.DP_TP_STATUS_B,
+         PORT_CLK_SEL   => Registers.PORT_CLK_SEL_DDIB),
+      DIGI_C => DDI_Registers'
+        (BUF_CTL        => Registers.DDI_BUF_CTL_C,
+         DP_TP_CTL      => Registers.DP_TP_CTL_C,
+         DP_TP_STATUS   => Registers.DP_TP_STATUS_C,
+         PORT_CLK_SEL   => Registers.PORT_CLK_SEL_DDIC),
+      DIGI_D => DDI_Registers'
+        (BUF_CTL        => Registers.DDI_BUF_CTL_D,
+         DP_TP_CTL      => Registers.DP_TP_CTL_D,
+         DP_TP_STATUS   => Registers.DP_TP_STATUS_D,
+         PORT_CLK_SEL   => Registers.PORT_CLK_SEL_DDID),
+      DIGI_E => DDI_Registers'
+        (BUF_CTL        => Registers.DDI_BUF_CTL_E,
+         DP_TP_CTL      => Registers.DP_TP_CTL_E,
+         DP_TP_STATUS   => Registers.DP_TP_STATUS_E,
+         PORT_CLK_SEL   => Registers.PORT_CLK_SEL_DDIE));
+
+   ----------------------------------------------------------------------------
+
+   type Values is array (Digital_Port) of Word32;
+   type Shifts is array (Digital_Port) of Natural;
+
+   DPLL_CTRL2_DDIx_CLOCK_OFF : constant Values := Values'
+     (DIGI_A   => 1 * 2 ** 15,
+      DIGI_B   => 1 * 2 ** 16,
+      DIGI_C   => 1 * 2 ** 17,
+      DIGI_D   => 1 * 2 ** 18,
+      DIGI_E   => 1 * 2 ** 19);
+
+   DPLL_CTRL2_DDIx_SELECT_MASK : constant Values := Values'
+     (DIGI_A   => 3 * 2 **  1,
+      DIGI_B   => 3 * 2 **  4,
+      DIGI_C   => 3 * 2 **  7,
+      DIGI_D   => 3 * 2 ** 10,
+      DIGI_E   => 3 * 2 ** 13);
+   DPLL_CTRL2_DDIx_SELECT_SHIFT : constant Shifts := Shifts'
+     (DIGI_A   =>  1,
+      DIGI_B   =>  4,
+      DIGI_C   =>  7,
+      DIGI_D   => 10,
+      DIGI_E   => 13);
+
+   DPLL_CTRL2_DDIx_SELECT_OVERRIDE : constant Values := Values'
+     (DIGI_A   => 1 * 2 **  0,
+      DIGI_B   => 1 * 2 **  3,
+      DIGI_C   => 1 * 2 **  6,
+      DIGI_D   => 1 * 2 **  9,
+      DIGI_E   => 1 * 2 ** 12);
+
+   ----------------------------------------------------------------------------
+
+   function DDI_BUF_CTL_TRANS_SELECT
+     (Sel : DDI_BUF_CTL_TRANS_SELECT_T)
+      return Word32
+   is
+   begin
+      return Word32 (Sel) * 2 ** 24;
+   end DDI_BUF_CTL_TRANS_SELECT;
+
+   ----------------------------------------------------------------------------
+
+   function Max_V_Swing
+     (Port : Digital_Port)
+      return DP_Info.DP_Voltage_Swing
+   is
+   begin
+      return
+        (if (Config.Has_Low_Voltage_Swing and Config.EDP_Low_Voltage_Swing)
+            and then Port = DIGI_A
+         then
+            DP_Info.VS_Level_3
+         else
+            DP_Info.VS_Level_2);
+   end Max_V_Swing;
+
+   pragma Warnings (GNATprove, Off, "unused variable ""Port""",
+                    Reason => "Needed for a common interface");
+   function Max_Pre_Emph
+     (Port        : Digital_Port;
+      Train_Set   : DP_Info.Train_Set)
+      return DP_Info.DP_Pre_Emph
+   is
+   begin
+      return
+        (case Train_Set.Voltage_Swing is
+            when DP_Info.VS_Level_0 => DP_Info.Emph_Level_3,
+            when DP_Info.VS_Level_1 => DP_Info.Emph_Level_2,
+            when DP_Info.VS_Level_2 => DP_Info.Emph_Level_1,
+            when others             => DP_Info.Emph_Level_0);
+   end Max_Pre_Emph;
+   pragma Warnings (GNATprove, On, "unused variable ""Port""");
+
+   ----------------------------------------------------------------------------
+
+   procedure Set_TP_CTL
+     (Port     : Digital_Port;
+      Link     : DP_Link;
+      Pattern  : DP_Info.Training_Pattern)
+   is
+      DP_TP_CTL_Enhanced_Frame : Word32 := 0;
+   begin
+      if Link.Enhanced_Framing then
+         DP_TP_CTL_Enhanced_Frame := DP_TP_CTL_ENHANCED_FRAME_ENABLE;
+      end if;
+
+      Registers.Write
+        (Register => DDI_Regs (Port).DP_TP_CTL,
+         Value    => DP_TP_CTL_TRANSPORT_ENABLE or
+                     DP_TP_CTL_Enhanced_Frame or
+                     DP_TP_CTL_LINK_TRAIN (Pattern));
+   end Set_TP_CTL;
+
+   procedure Set_Training_Pattern
+     (Port     : Digital_Port;
+      Link     : DP_Link;
+      Pattern  : DP_Info.Training_Pattern)
+   is
+      use type DP_Info.Training_Pattern;
+   begin
+      if Pattern < DP_Info.TP_Idle then
+         Set_TP_CTL (Port, Link, Pattern);
+      else
+         -- send at least 5 idle patterns
+         Set_TP_CTL (Port, Link, DP_Info.TP_Idle);
+
+         -- switch to normal frame delivery
+         if Config.End_EDP_Training_Late and then Port = DIGI_A then
+            null; -- do it later in Post_On procedure
+            -- TODO: if there are problems getting the pipe up,
+            --       wait here some time
+            -- Time.U_Delay (100);
+         else
+            if Port /= DIGI_A then
+               Registers.Wait_Set_Mask
+                 (Register    => DDI_Regs (Port).DP_TP_STATUS,
+                  Mask        => DP_TP_STATUS_MIN_IDLES_SENT);
+            end if;
+            Set_TP_CTL (Port, Link, DP_Info.TP_None);
+         end if;
+      end if;
+   end Set_Training_Pattern;
+
+   procedure Set_Signal_Levels
+     (Port        : Digital_Port;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set)
+   is
+      Was_Enabled : Boolean;
+      Trans_Select : DDI_BUF_CTL_TRANS_SELECT_T;
+   begin
+      case Train_Set.Voltage_Swing is
+         when DP_Info.VS_Level_0 =>
+            case Train_Set.Pre_Emph is
+               when DP_Info.Emph_Level_0  => Trans_Select := 0;
+               when DP_Info.Emph_Level_1  => Trans_Select := 1;
+               when DP_Info.Emph_Level_2  => Trans_Select := 2;
+               when DP_Info.Emph_Level_3  => Trans_Select := 3;
+            end case;
+         when DP_Info.VS_Level_1 =>
+            case Train_Set.Pre_Emph is
+               when DP_Info.Emph_Level_0  => Trans_Select := 4;
+               when DP_Info.Emph_Level_1  => Trans_Select := 5;
+               when DP_Info.Emph_Level_2  => Trans_Select := 6;
+               when others                => Trans_Select := 0;
+            end case;
+         when DP_Info.VS_Level_2 =>
+            case Train_Set.Pre_Emph is
+               when DP_Info.Emph_Level_0  => Trans_Select := 7;
+               when DP_Info.Emph_Level_1  => Trans_Select := 8;
+               when others                => Trans_Select := 0;
+            end case;
+         when DP_Info.VS_Level_3 =>
+            case Train_Set.Pre_Emph is
+               when DP_Info.Emph_Level_0  => Trans_Select := 9;
+               when others                => Trans_Select := 0;
+            end case;
+      end case;
+
+      Registers.Is_Set_Mask
+        (Register => DDI_Regs (Port).BUF_CTL,
+         Mask     => DDI_BUF_CTL_BUFFER_ENABLE,
+         Result   => Was_Enabled);
+
+      -- enable DDI buffer
+      Registers.Unset_And_Set_Mask
+        (Register    => DDI_Regs (Port).BUF_CTL,
+         Mask_Unset  => DDI_BUF_CTL_TRANS_SELECT_MASK or
+                        DDI_BUF_CTL_PORT_REVERSAL or
+                        DDI_BUF_CTL_PORT_WIDTH_MASK,
+         Mask_Set    => DDI_BUF_CTL_BUFFER_ENABLE or
+                        DDI_BUF_CTL_TRANS_SELECT (Trans_Select) or
+                        DDI_BUF_CTL_PORT_WIDTH (Link.Lane_Count));
+      Registers.Posting_Read (DDI_Regs (Port).BUF_CTL);
+
+      if not Was_Enabled then
+         Time.U_Delay (600);  -- wait >= 518us (intel spec)
+      end if;
+   end Set_Signal_Levels;
+
+   ----------------------------------------------------------------------------
+
+   procedure Digital_Off (Port : Digital_Port)
+   is
+      Enabled : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Is_Set_Mask
+        (Register => DDI_Regs (Port).BUF_CTL,
+         Mask     => DDI_BUF_CTL_BUFFER_ENABLE,
+         Result   => Enabled);
+
+      if Enabled then
+         Registers.Unset_Mask
+           (Register => DDI_Regs (Port).BUF_CTL,
+            Mask     => DDI_BUF_CTL_BUFFER_ENABLE);
+      end if;
+
+      Registers.Unset_Mask
+        (Register => DDI_Regs (Port).DP_TP_CTL,
+         Mask     => DP_TP_CTL_TRANSPORT_ENABLE);
+
+      if Enabled then
+         Registers.Wait_Set_Mask
+           (Register => DDI_Regs (Port).BUF_CTL,
+            Mask     => DDI_BUF_CTL_IDLE_STATUS);
+      end if;
+
+      if Config.Has_Per_DDI_Clock_Sel then
+         Registers.Write
+           (Register => DDI_Regs (Port).PORT_CLK_SEL,
+            Value    => PORT_CLK_SEL_NONE);
+      else
+         Registers.Set_Mask
+           (Register => Registers.DPLL_CTRL2,
+            Mask     => DPLL_CTRL2_DDIx_CLOCK_OFF (Port));
+      end if;
+   end Digital_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Train_FDI
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   is
+   begin
+      PCH.FDI.Pre_Train (PCH.FDI_A, Port_Cfg);
+
+      -- always use SPLL for FDI
+      SPLL.On;
+      Registers.Write
+        (Register => DDI_Regs (DIGI_E).PORT_CLK_SEL,
+         Value    => PORT_CLK_SEL_SPLL);
+
+      -- try each preemph/voltage pair twice
+      for Trans2 in Natural range 0 .. DDI_BUF_CTL_TRANS_SELECT_T'Last * 2 + 1
+      loop
+         Registers.Write
+           (Register => DDI_Regs (DIGI_E).DP_TP_CTL,
+            Value    => DP_TP_CTL_TRANSPORT_ENABLE or
+                        DP_TP_CTL_ENHANCED_FRAME_ENABLE or
+                        DP_TP_CTL_FDI_AUTOTRAIN or
+                        DP_TP_CTL_LINK_TRAIN_PAT1);
+
+         Registers.Unset_And_Set_Mask
+           (Register    => DDI_Regs (DIGI_E).BUF_CTL,
+            Mask_Unset  => DDI_BUF_CTL_TRANS_SELECT_MASK or
+                           DDI_BUF_CTL_PORT_REVERSAL or
+                           DDI_BUF_CTL_PORT_WIDTH_MASK,
+            Mask_Set    => DDI_BUF_CTL_BUFFER_ENABLE or
+                           DDI_BUF_CTL_TRANS_SELECT (Trans2 / 2) or
+                           DDI_BUF_CTL_PORT_WIDTH (Port_Cfg.FDI.Lane_Count));
+         Registers.Posting_Read (DDI_Regs (DIGI_E).BUF_CTL);
+         Time.U_Delay (600);  -- wait >= 518us (intel spec)
+
+         PCH.FDI.Auto_Train (PCH.FDI_A);
+         Registers.Is_Set_Mask
+           (Register => DDI_Regs (DIGI_E).DP_TP_STATUS,
+            Mask     => DP_TP_STATUS_FDI_AUTO_TRAIN_DONE,
+            Result   => Success);
+         exit when Success;
+
+         Registers.Unset_Mask
+           (Register => DDI_Regs (DIGI_E).BUF_CTL,
+            Mask     => DDI_BUF_CTL_BUFFER_ENABLE);
+         Registers.Posting_Read (DDI_Regs (DIGI_E).BUF_CTL);
+
+         Registers.Unset_And_Set_Mask
+           (Register    => DDI_Regs (DIGI_E).DP_TP_CTL,
+            Mask_Unset  => DP_TP_CTL_TRANSPORT_ENABLE or
+                           DP_TP_CTL_LINK_TRAIN_MASK,
+            Mask_Set    => DP_TP_CTL_LINK_TRAIN_PAT1);
+         Registers.Posting_Read (DDI_Regs (DIGI_E).DP_TP_CTL);
+
+         Registers.Wait_Set_Mask
+           (Register => DDI_Regs (DIGI_E).BUF_CTL,
+            Mask     => DDI_BUF_CTL_IDLE_STATUS);
+
+         PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Lanes_Off);
+      end loop;
+
+      if Success then
+         -- start normal frame delivery
+         Registers.Write
+           (Register => DDI_Regs (DIGI_E).DP_TP_CTL,
+            Value    => DP_TP_CTL_TRANSPORT_ENABLE or
+                        DP_TP_CTL_ENHANCED_FRAME_ENABLE or
+                        DP_TP_CTL_FDI_AUTOTRAIN or
+                        DP_TP_CTL_LINK_TRAIN_NORMAL);
+      else
+         Registers.Write
+           (Register => DDI_Regs (DIGI_E).PORT_CLK_SEL,
+            Value    => PORT_CLK_SEL_NONE);
+         SPLL.Off;
+
+         PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Clock_Off);
+      end if;
+   end Train_FDI;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_On
+     (Port_Cfg : in     Port_Config;
+      PLL_Hint : in     Word32;
+      Success  :    out Boolean)
+   is
+      function To_DP (Port : Digital_Port) return DP_Port
+      is
+      begin
+         return
+           (case Port is
+               when DIGI_A => DP_A,
+               when DIGI_B => DP_B,
+               when DIGI_C => DP_C,
+               when DIGI_D => DP_D,
+               when others => DP_Port'First);
+      end To_DP;
+      package Training is new DP_Training
+        (TPS3_Supported    => True,
+         T                 => Digital_Port,
+         Aux_T             => DP_Port,
+         Aux_Ch            => DP_Aux_Ch,
+         DP_Info           => DP_Info,
+         To_Aux            => To_DP,
+         Max_V_Swing       => Max_V_Swing,
+         Max_Pre_Emph      => Max_Pre_Emph,
+         Set_Pattern       => Set_Training_Pattern,
+         Set_Signal_Levels => Set_Signal_Levels,
+         Off               => Digital_Off);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Display = VGA then
+         Train_FDI (Port_Cfg, Success);
+      else
+         -- direct configured PLL output to this port
+         if Config.Has_Per_DDI_Clock_Sel then
+            Registers.Write
+              (Register    => DDI_Regs (Port_Cfg.Port).PORT_CLK_SEL,
+               Value       => PLL_Hint);
+         else
+            Registers.Unset_And_Set_Mask
+              (Register    => Registers.DPLL_CTRL2,
+               Mask_Unset  => DPLL_CTRL2_DDIx_CLOCK_OFF (Port_Cfg.Port) or
+                              DPLL_CTRL2_DDIx_SELECT_MASK (Port_Cfg.Port),
+               Mask_Set    => Shift_Left
+                                (PLL_Hint,
+                                 DPLL_CTRL2_DDIx_SELECT_SHIFT (Port_Cfg.Port))
+                              or
+                              DPLL_CTRL2_DDIx_SELECT_OVERRIDE (Port_Cfg.Port));
+         end if;
+
+         if Port_Cfg.Display = DP then
+            Training.Train_DP
+              (Port        => Port_Cfg.Port,
+               Link        => Port_Cfg.DP,
+               Success     => Success);
+         else
+            Success := True;
+         end if;
+      end if;
+   end Pre_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Post_On (Port_Cfg : Port_Config)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Port = DIGI_A then
+         if Config.End_EDP_Training_Late then
+            Registers.Unset_And_Set_Mask
+              (Register    => DDI_Regs (DIGI_A).DP_TP_CTL,
+               Mask_Unset  => DP_TP_CTL_LINK_TRAIN_MASK,
+               Mask_Set    => DP_TP_CTL_LINK_TRAIN_NORMAL);
+         end if;
+      end if;
+
+      case Port_Cfg.Display is
+         when HDMI =>
+            Registers.Unset_And_Set_Mask
+              (Register    => DDI_Regs (Port_Cfg.Port).BUF_CTL,
+               Mask_Unset  => DDI_BUF_CTL_TRANS_SELECT_MASK or
+                              DDI_BUF_CTL_PORT_REVERSAL,
+               Mask_Set    => DDI_BUF_CTL_BUFFER_ENABLE);
+            Time.U_Delay (600);  -- wait >= 518us (intel spec)
+         when VGA =>
+            PCH.VGA.Clock_On (Port_Cfg.Mode);
+            PCH.Transcoder.On (Port_Cfg, PCH.FDI_A, 0);
+            PCH.VGA.On
+              (Port => PCH.FDI_A,
+               Mode => Port_Cfg.Mode);
+         when others =>
+            null;
+      end case;
+   end Post_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off (Port : Digital_Port)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port = DIGI_E then
+         PCH.VGA.Off;
+         PCH.Transcoder.Off (PCH.FDI_A);
+         -- PCH.VGA.Clock_Off; -- Can't tell what Linux does, if anything.
+         PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Rx_Off);
+      end if;
+
+      Digital_Off (Port);
+
+      if Port = DIGI_E then
+         SPLL.Off;
+         PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Clock_Off);
+      end if;
+   end Off;
+
+end HW.GFX.GMA.Connectors.DDI;
diff --git a/common/haswell_shared/hw-gfx-gma-connectors-ddi.ads b/common/haswell_shared/hw-gfx-gma-connectors-ddi.ads
new file mode 100644
index 0000000..337e77b
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-connectors-ddi.ads
@@ -0,0 +1,28 @@
+--
+-- Copyright (C) 2015 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Registers;
+
+private package HW.GFX.GMA.Connectors.DDI
+is
+
+   procedure Pre_On
+     (Port_Cfg : in     Port_Config;
+      PLL_Hint : in     Word32;
+      Success  :    out Boolean);
+
+   procedure Post_On (Port_Cfg : Port_Config);
+
+   procedure Off (Port : Digital_Port);
+
+end HW.GFX.GMA.Connectors.DDI;
diff --git a/common/haswell_shared/hw-gfx-gma-connectors.adb b/common/haswell_shared/hw-gfx-gma-connectors.adb
new file mode 100644
index 0000000..e53902b
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-connectors.adb
@@ -0,0 +1,93 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.Connectors.DDI;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors is
+
+   procedure Pre_On
+     (Port_Cfg    : in     Port_Config;
+      PLL_Hint    : in     Word32;
+      Pipe_Hint   : in     Word32;
+      Success     :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      DDI.Pre_On (Port_Cfg, PLL_Hint, Success);
+   end Pre_On;
+
+   procedure Post_On
+     (Port_Cfg : in     Port_Config;
+      PLL_Hint : in     Word32;
+      Success  :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      DDI.Post_On (Port_Cfg);
+
+      if Port_Cfg.Port = DIGI_A then
+         Panel.Backlight_On;
+      end if;
+
+      Success := True;
+   end Post_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_Off (Port_Cfg : Port_Config)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Port = DIGI_A then
+         Panel.Backlight_Off;
+         Panel.Off;
+      end if;
+   end Pre_Off;
+
+   procedure Post_Off (Port_Cfg : Port_Config)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      DDI.Off (Port_Cfg.Port);
+   end Post_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_All_Off
+   is
+   begin
+      Panel.Backlight_Off;
+      Panel.Off;
+   end Pre_All_Off;
+
+   procedure Post_All_Off
+   is
+   begin
+      for Port in Digital_Port range DIGI_A .. DIGI_D loop
+         DDI.Off (Port);
+      end loop;
+      if Config.FDI_Port (DIGI_E) then
+         DDI.Off (DIGI_E);
+      end if;
+   end Post_All_Off;
+
+end HW.GFX.GMA.Connectors;
diff --git a/common/haswell_shared/hw-gfx-gma-port_detect.adb b/common/haswell_shared/hw-gfx-gma-port_detect.adb
new file mode 100644
index 0000000..0becd2c
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-port_detect.adb
@@ -0,0 +1,189 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Port_Detect
+is
+
+   PCH_ADPA_CRT_HPD_CHANNEL_MASK       : constant := 3 * 2 ** 24;
+   PCH_ADPA_CRT_HPD_ENABLE             : constant := 1 * 2 ** 23;
+
+   SFUSE_STRAP_CRT_DAC_CAP_DISABLE     : constant := 1 * 2 **  6;
+
+   HOTPLUG_CTL_DDI_A_HPD_INPUT_ENABLE  : constant := 1 * 2 **  4;
+   HOTPLUG_CTL_DDI_A_HPD_STATUS        : constant := 3 * 2 **  0;
+   HOTPLUG_CTL_DDI_A_HPD_LONG_DETECT   : constant := 1 * 2 **  1;
+
+   SHOTPLUG_CTL_DETECT_MASK            : constant := 16#0303_0303#;
+
+   type Digital_Port_Value is array (Digital_Port) of Word32;
+   DDI_PORT_DETECTED : constant Digital_Port_Value :=
+     (DIGI_B => 1 * 2 **  2,
+      DIGI_C => 1 * 2 **  1,
+      DIGI_D => 1 * 2 **  0,
+      DIGI_A => 1 * 2 **  0,
+      others => 0);
+   SHOTPLUG_CTL_HPD_INPUT_ENABLE : constant Digital_Port_Value :=
+     (DIGI_B => 1 * 2 **  4,
+      DIGI_C => 1 * 2 ** 12,
+      DIGI_D => 1 * 2 ** 20,
+      DIGI_A => 1 * 2 ** 28,
+      others => 0);
+   SHOTPLUG_CTL_HPD_STATUS : constant Digital_Port_Value :=
+     (DIGI_B => 3 * 2 **  0,
+      DIGI_C => 3 * 2 **  8,
+      DIGI_D => 3 * 2 ** 16,
+      DIGI_A => 3 * 2 ** 24,
+      others => 0);
+   SHOTPLUG_CTL_LONG_DETECT : constant Digital_Port_Value :=
+     (DIGI_B => 1 * 2 **  1,
+      DIGI_C => 1 * 2 **  9,
+      DIGI_D => 1 * 2 ** 17,
+      DIGI_A => 1 * 2 ** 25,
+      others => 0);
+
+   procedure Initialize
+   is
+      DAC_Disabled,
+      Internal_Detected,
+      DDI_Detected : Boolean;
+
+      Last_Digital_Port : constant Digital_Port :=
+        (if Config.Has_DDI_D then DIGI_D else DIGI_C);
+
+      subtype Ext_Digital_Port is
+         Digital_Port range DIGI_B .. DIGI_D;
+      type Digital_Port_To_GMA_Port is array (Ext_Digital_Port) of Port_Type;
+      To_HDMI_Port : constant Digital_Port_To_GMA_Port :=
+        (DIGI_B => Digital1,
+         DIGI_C => Digital2,
+         DIGI_D => Digital3);
+      To_DP_Port : constant Digital_Port_To_GMA_Port :=
+        (DIGI_B => DP1,
+         DIGI_C => DP2,
+         DIGI_D => DP3);
+   begin
+      if Config.Has_PCH_DAC then
+         -- PCH_DAC (_A)
+         Registers.Is_Set_Mask
+           (Register => Registers.SFUSE_STRAP,
+            Mask     => SFUSE_STRAP_CRT_DAC_CAP_DISABLE,
+            Result   => DAC_Disabled);
+         if not DAC_Disabled then
+            Registers.Set_Mask
+              (Register => Registers.PCH_ADPA,
+               Mask     => PCH_ADPA_CRT_HPD_CHANNEL_MASK or   -- clear status
+                           PCH_ADPA_CRT_HPD_ENABLE);
+         end if;
+         Config.Valid_Port (Analog) := not DAC_Disabled;
+      end if;
+
+      if Config.Internal_Is_EDP then
+         -- DDI_A
+         Registers.Is_Set_Mask
+           (Register => Registers.DDI_BUF_CTL_A,
+            Mask     => DDI_PORT_DETECTED (DIGI_A),
+            Result   => Internal_Detected);
+         if Internal_Detected then
+            if Config.Has_HOTPLUG_CTL then
+               Registers.Set_Mask
+                 (Register => Registers.HOTPLUG_CTL,
+                  Mask     => HOTPLUG_CTL_DDI_A_HPD_INPUT_ENABLE or
+                              HOTPLUG_CTL_DDI_A_HPD_STATUS);   -- clear status
+               if Config.Has_SHOTPLUG_CTL_A then
+                  -- Have to enable south hotplug too on SoCs.
+                  Registers.Unset_And_Set_Mask
+                    (Register    => Registers.SHOTPLUG_CTL,
+                     Mask_Unset  => SHOTPLUG_CTL_DETECT_MASK,
+                     Mask_Set    => SHOTPLUG_CTL_HPD_INPUT_ENABLE (DIGI_A));
+               end if;
+            else
+               Registers.Unset_And_Set_Mask
+                 (Register    => Registers.SHOTPLUG_CTL,
+                  Mask_Unset  => SHOTPLUG_CTL_DETECT_MASK,
+                  Mask_Set    => SHOTPLUG_CTL_HPD_INPUT_ENABLE (DIGI_A) or
+                                 SHOTPLUG_CTL_HPD_STATUS (DIGI_A));  -- clear
+            end if;
+         end if;
+      else
+         Internal_Detected := False;
+      end if;
+      Config.Valid_Port (Internal) := Internal_Detected;
+
+      -- DDI_[BCD]
+      for Port in Ext_Digital_Port range DIGI_B .. Last_Digital_Port loop
+         Registers.Is_Set_Mask
+           (Register => Registers.SFUSE_STRAP,
+            Mask     => DDI_PORT_DETECTED (Port),
+            Result   => DDI_Detected);
+         Config.Valid_Port (To_HDMI_Port (Port)) :=
+            Config.Valid_Port (To_HDMI_Port (Port)) and DDI_Detected;
+         Config.Valid_Port (To_DP_Port (Port)) :=
+            Config.Valid_Port (To_DP_Port (Port)) and DDI_Detected;
+
+         if DDI_Detected then
+            Registers.Unset_And_Set_Mask
+              (Register    => Registers.SHOTPLUG_CTL,
+               Mask_Unset  => SHOTPLUG_CTL_DETECT_MASK,
+               Mask_Set    => SHOTPLUG_CTL_HPD_INPUT_ENABLE (Port) or
+                              SHOTPLUG_CTL_HPD_STATUS (Port)); -- clear status
+         else
+            Registers.Unset_Mask
+              (Register => Registers.SHOTPLUG_CTL,
+               Mask     => SHOTPLUG_CTL_DETECT_MASK or
+                           SHOTPLUG_CTL_HPD_INPUT_ENABLE (Port));
+         end if;
+      end loop;
+   end Initialize;
+
+   procedure Hotplug_Detect (Port_Cfg : in Port_Config; Detected : out Boolean)
+   is
+      Ctl32 : Word32;
+   begin
+      if Port_Cfg.Display = VGA then
+         Registers.Read (Registers.PCH_ADPA, Ctl32, Verbose => False);
+         Ctl32 := Ctl32 and PCH_ADPA_CRT_HPD_CHANNEL_MASK;
+         Detected := Ctl32 = PCH_ADPA_CRT_HPD_CHANNEL_MASK;
+         if Ctl32 /= 0 then
+            Registers.Set_Mask
+              (Register => Registers.PCH_ADPA,
+               Mask     => PCH_ADPA_CRT_HPD_CHANNEL_MASK);
+         end if;
+      elsif Config.Has_HOTPLUG_CTL and then Port_Cfg.Port = DIGI_A then
+         Registers.Read (Registers.HOTPLUG_CTL, Ctl32, Verbose => False);
+         Detected := (Ctl32 and HOTPLUG_CTL_DDI_A_HPD_LONG_DETECT) /= 0;
+
+         if (Ctl32 and HOTPLUG_CTL_DDI_A_HPD_STATUS) /= 0 then
+            Registers.Set_Mask
+              (Register => Registers.HOTPLUG_CTL,
+               Mask     => HOTPLUG_CTL_DDI_A_HPD_STATUS);
+         end if;
+      elsif Port_Cfg.Port in DIGI_A .. DIGI_D then
+         Registers.Read (Registers.SHOTPLUG_CTL, Ctl32, Verbose => False);
+         Detected :=
+           (Ctl32 and SHOTPLUG_CTL_LONG_DETECT (Port_Cfg.Port)) /= 0;
+
+         if (Ctl32 and SHOTPLUG_CTL_HPD_STATUS (Port_Cfg.Port)) /= 0 then
+            Registers.Unset_And_Set_Mask
+              (Register    => Registers.SHOTPLUG_CTL,
+               Mask_Unset  => SHOTPLUG_CTL_DETECT_MASK,
+               Mask_Set    => SHOTPLUG_CTL_HPD_STATUS (Port_Cfg.Port));
+         end if;
+      else
+         Detected := False;
+      end if;
+   end Hotplug_Detect;
+
+end HW.GFX.GMA.Port_Detect;
diff --git a/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.adb b/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.adb
new file mode 100644
index 0000000..b46c29d
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.adb
@@ -0,0 +1,235 @@
+--
+-- Copyright (C) 2014-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with GNAT.Source_Info;
+
+with HW.Time;
+with HW.Debug;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Power_And_Clocks_Haswell is
+
+   PWR_WELL_CTL_ENABLE_REQUEST   : constant := 1 * 2 ** 31;
+   PWR_WELL_CTL_DISABLE_REQUEST  : constant := 0 * 2 ** 31;
+   PWR_WELL_CTL_STATE_ENABLED    : constant := 1 * 2 ** 30;
+
+   ----------------------------------------------------------------------------
+
+   SRD_CTL_ENABLE          : constant := 1 * 2 ** 31;
+   SRD_STATUS_STATE_MASK   : constant := 7 * 2 ** 29;
+
+   type Pipe is (EDP, A, B, C);
+   type SRD_Regs is record
+      CTL     : Registers.Registers_Index;
+      STATUS  : Registers.Registers_Index;
+   end record;
+   type SRD_Per_Pipe_Regs is array (Pipe) of SRD_Regs;
+   SRD : constant SRD_Per_Pipe_Regs := SRD_Per_Pipe_Regs'
+     (A     => SRD_Regs'
+        (CTL      => Registers.SRD_CTL_A,
+         STATUS   => Registers.SRD_STATUS_A),
+      B     => SRD_Regs'
+        (CTL      => Registers.SRD_CTL_B,
+         STATUS   => Registers.SRD_STATUS_B),
+      C     => SRD_Regs'
+        (CTL      => Registers.SRD_CTL_C,
+         STATUS   => Registers.SRD_STATUS_C),
+      EDP   => SRD_Regs'
+        (CTL      => Registers.SRD_CTL_EDP,
+         STATUS   => Registers.SRD_STATUS_EDP));
+
+   ----------------------------------------------------------------------------
+
+   IPS_CTL_ENABLE          : constant := 1 * 2 ** 31;
+   DISPLAY_IPS_CONTROL     : constant := 16#19#;
+
+   GT_MAILBOX_READY        : constant := 1 * 2 ** 31;
+
+   ----------------------------------------------------------------------------
+
+   procedure PSR_Off
+   is
+      Enabled : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.Has_Per_Pipe_SRD then
+         for P in Pipe loop
+            Registers.Is_Set_Mask (SRD (P).CTL, SRD_CTL_ENABLE, Enabled);
+            if Enabled then
+               Registers.Unset_Mask (SRD (P).CTL, SRD_CTL_ENABLE);
+               Registers.Wait_Unset_Mask (SRD (P).STATUS, SRD_STATUS_STATE_MASK);
+
+               pragma Debug (Debug.Put_Line ("Disabled PSR."));
+            end if;
+         end loop;
+      else
+         Registers.Is_Set_Mask (Registers.SRD_CTL, SRD_CTL_ENABLE, Enabled);
+         if Enabled then
+            Registers.Unset_Mask (Registers.SRD_CTL, SRD_CTL_ENABLE);
+            Registers.Wait_Unset_Mask (Registers.SRD_STATUS, SRD_STATUS_STATE_MASK);
+
+            pragma Debug (Debug.Put_Line ("Disabled PSR."));
+         end if;
+      end if;
+   end PSR_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure GT_Mailbox_Write (MBox : Word32; Value : Word32) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
+      Registers.Write (Registers.GT_MAILBOX_DATA, Value);
+      Registers.Write (Registers.GT_MAILBOX, GT_MAILBOX_READY or MBox);
+
+      Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
+      Registers.Write (Registers.GT_MAILBOX_DATA, 0);
+   end GT_Mailbox_Write;
+
+   procedure IPS_Off
+   is
+      Enabled : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.Has_IPS then
+         Registers.Is_Set_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE, Enabled);
+         if Enabled then
+            if Config.Has_IPS_CTL_Mailbox then
+               GT_Mailbox_Write (DISPLAY_IPS_CONTROL, 0);
+               -- May take up to 42ms.
+               Registers.Wait_Unset_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE);
+            else
+               Registers.Unset_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE);
+            end if;
+
+            pragma Debug (Debug.Put_Line ("Disabled IPS."));
+            -- We have to wait until the next vblank here.
+            -- 20ms should be enough.
+            Time.M_Delay (20);
+         end if;
+      end if;
+   end IPS_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure PDW_Off
+   is
+      Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+      Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+      Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+      Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); --  Result for debugging only
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); --  Result for debugging only
+
+      if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+          PWR_WELL_CTL_ENABLE_REQUEST) /= 0
+      then
+         Registers.Wait_Set_Mask
+           (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
+      end if;
+
+      if (Ctl1 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
+         Registers.Write (Registers.PWR_WELL_CTL_BIOS, PWR_WELL_CTL_DISABLE_REQUEST);
+      end if;
+
+      if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
+         Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_DISABLE_REQUEST);
+      end if;
+   end PDW_Off;
+
+   procedure PDW_On
+   is
+      Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+      Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+      Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+      Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); --  Result for debugging only
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); --  Result for debugging only
+
+      if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+          PWR_WELL_CTL_ENABLE_REQUEST) = 0
+      then
+         Registers.Wait_Unset_Mask
+           (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
+      end if;
+
+      if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) = 0 then
+         Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_ENABLE_REQUEST);
+         Registers.Wait_Set_Mask
+           (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
+      end if;
+   end PDW_On;
+
+   function Need_PDW (Checked_Configs : Configs_Type) return Boolean is
+   begin
+      return (Checked_Configs (Primary).Port /= Disabled and
+              Checked_Configs (Primary).Port /= Internal) or
+             Checked_Configs (Secondary).Port /= Disabled or
+             Checked_Configs (Tertiary).Port /= Disabled;
+   end Need_PDW;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_All_Off is
+   begin
+      -- HSW: disable panel self refresh (PSR) on eDP if enabled
+         -- wait for PSR idling
+      PSR_Off;
+      IPS_Off;
+   end Pre_All_Off;
+
+   procedure Initialize is
+   begin
+      -- HSW: disable power down well
+      PDW_Off;
+   end Initialize;
+
+   procedure Power_Set_To (Configs : Configs_Type) is
+   begin
+      if Need_PDW (Configs) then
+         PDW_On;
+      else
+         PDW_Off;
+      end if;
+   end Power_Set_To;
+
+   procedure Power_Up (Old_Configs, New_Configs : Configs_Type) is
+   begin
+      if not Need_PDW (Old_Configs) and Need_PDW (New_Configs) then
+         PDW_On;
+      end if;
+   end Power_Up;
+
+   procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type)
+   is
+   begin
+      if (Need_PDW (Old_Configs) or Need_PDW (Tmp_Configs)) and
+         not Need_PDW (New_Configs)
+      then
+         PDW_Off;
+      end if;
+   end Power_Down;
+
+end HW.GFX.GMA.Power_And_Clocks_Haswell;
diff --git a/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.ads b/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.ads
new file mode 100644
index 0000000..84cf889
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.ads
@@ -0,0 +1,27 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Power_And_Clocks_Haswell is
+
+   procedure PSR_Off;
+
+   procedure Pre_All_Off;
+   procedure Post_All_Off is null;
+
+   procedure Initialize;
+
+   procedure Power_Set_To (Configs : Configs_Type);
+   procedure Power_Up (Old_Configs, New_Configs : Configs_Type);
+   procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type);
+
+end HW.GFX.GMA.Power_And_Clocks_Haswell;
diff --git a/common/hw-gfx-dp_aux_ch.adb b/common/hw-gfx-dp_aux_ch.adb
new file mode 100644
index 0000000..2f8b982
--- /dev/null
+++ b/common/hw-gfx-dp_aux_ch.adb
@@ -0,0 +1,392 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+with HW.GFX.DP_Defs;
+
+use type HW.Word8;
+use type HW.GFX.DP_Defs.Aux_Message_Command;
+
+package body HW.GFX.DP_Aux_Ch is
+
+   DP_AUX_I2C_WRITE           : constant := 16#0#;
+   DP_AUX_I2C_READ            : constant := 16#1#;
+   DP_AUX_I2C_WR_STATUS_REQ   : constant := 16#2#;
+   DP_AUX_I2C_MOT             : constant := 16#4#;
+   DP_AUX_NATIVE_WRITE        : constant := 16#8#;
+   DP_AUX_NATIVE_READ         : constant := 16#9#;
+
+   procedure Fill_Aux_Request
+     (Request  :    out DP_Defs.Aux_Request;
+      Command  : in     DP_Defs.Aux_Message_Command;
+      Address  : in     DP_Defs.Aux_Message_Address;
+      Length   : in     DP_Defs.Aux_Payload_Length)
+   is
+   begin
+      Request :=
+        (0      => Shift_Left (Word8 (Command), 4) or
+                   Word8 (Shift_Right (Word32 (Address), 16)),
+         1      => Word8 (Shift_Right (Word32 (Address),  8) and 16#ff#),
+         2      => Word8 (Shift_Right (Word32 (Address),  0) and 16#ff#),
+         3      => Word8 (Length) - 1,
+         others => 0);  -- Don't care
+   end Fill_Aux_Request;
+
+   function Is_Empty (Request : DP_Defs.Aux_Request) return Boolean is
+   begin
+      return Request (3) = 16#ff#;
+   end Is_Empty;
+
+   function Is_Aux_Ack (Response : DP_Defs.Aux_Response) return Boolean
+   with
+      Depends => (Is_Aux_Ack'Result => Response)
+   is
+   begin
+      return (Response (0) and 16#30#) = 16#00#;
+   end Is_Aux_Ack;
+
+   function Is_Aux_Defer (Response : DP_Defs.Aux_Response) return Boolean is
+   begin
+      return (Response (0) and 16#30#) = 16#20#;
+   end Is_Aux_Defer;
+
+   function Is_I2C_Ack (Response : DP_Defs.Aux_Response) return Boolean
+   with
+      Depends => (Is_I2C_Ack'Result => Response)
+   is
+   begin
+      return (Response (0) and 16#c0#) = 16#00#;
+   end Is_I2C_Ack;
+
+   function Is_I2C_Defer (Response : DP_Defs.Aux_Response) return Boolean is
+   begin
+      return (Response (0) and 16#c0#) = 16#80#;
+   end Is_I2C_Defer;
+
+   ----------------------------------------------------------------------------
+
+   procedure Do_Aux_Request
+     (Port              : in     T;
+      Request           : in     DP_Defs.Aux_Request;
+      Request_Length    : in     DP_Defs.Aux_Request_Length;
+      Response          :    out DP_Defs.Aux_Response;
+      Response_Length   :    out DP_Defs.Aux_Response_Length;
+      Success           :    out Boolean)
+   is
+   begin
+      for Try in Positive range 1 .. 32 loop
+         Aux_Request
+           (Port              => Port,
+            Request           => Request,
+            Request_Length    => Request_Length,
+            Response          => Response,
+            Response_Length   => Response_Length,
+            Success           => Success);
+
+         exit when not (Success and Is_Aux_Defer (Response));
+         Time.U_Delay (500);
+      end loop;
+
+      Success := Success and then Is_Aux_Ack (Response);
+   end Do_Aux_Request;
+
+   ----------------------------------------------------------------------------
+
+   procedure Aux_Read
+     (Port     : in     T;
+      Address  : in     DP_Defs.Aux_Message_Address;
+      Length   : in out DP_Defs.Aux_Payload_Length;
+      Data     :    out DP_Defs.Aux_Payload;
+      Success  :    out Boolean)
+   is
+      Request : DP_Defs.Aux_Request;
+      Response : DP_Defs.Aux_Response;
+      Response_Length : DP_Defs.Aux_Response_Length;
+   begin
+      Data := (others => 0);  -- Initialize
+
+      Fill_Aux_Request
+        (Request  => Request,
+         Command  => DP_AUX_NATIVE_READ,
+         Address  => Address,
+         Length   => Length);
+
+      Do_Aux_Request
+        (Port              => Port,
+         Request           => Request,
+         Request_Length    => 4,
+         Response          => Response,
+         Response_Length   => Response_Length,
+         Success           => Success);
+
+      Success := Success and then Response_Length > 1;
+      if Success then
+         pragma Assert (Response_Length > 1);
+         Length := Response_Length - 1;
+         Data (0 .. Length - 1) :=  Response (1 .. Length);
+      end if;
+   end Aux_Read;
+
+   procedure Aux_Write
+     (Port     : in     T;
+      Address  : in     DP_Defs.Aux_Message_Address;
+      Length   : in     DP_Defs.Aux_Payload_Length;
+      Data     : in     DP_Defs.Aux_Payload;
+      Success  :    out Boolean)
+   is
+      Request : DP_Defs.Aux_Request;
+
+      Ignored_Response        : DP_Defs.Aux_Response;
+      Ignored_Response_Length : DP_Defs.Aux_Response_Length;
+   begin
+      Fill_Aux_Request
+        (Request  => Request,
+         Command  => DP_AUX_NATIVE_WRITE,
+         Address  => Address,
+         Length   => Length);
+      Request (4 .. Length + 4 - 1) := Data (0 .. Length - 1);
+
+      pragma Warnings (GNATprove, Off,
+                       "unused assignment to ""Ignored_Response*""",
+                       Reason => "No response expected here");
+      Do_Aux_Request
+        (Port              => Port,
+         Request           => Request,
+         Request_Length    => 4 + Length,
+         Response          => Ignored_Response,
+         Response_Length   => Ignored_Response_Length,
+         Success           => Success);
+   end Aux_Write;
+
+   ----------------------------------------------------------------------------
+
+   procedure I2C_Out_Packet
+     (Port              : in     T;
+      Command           : in     DP_Defs.Aux_Message_Command;
+      Address           : in     DP_Defs.Aux_Message_Address;
+      Length            : in     DP_Defs.Aux_Payload_Length;
+      Data              : in     DP_Defs.Aux_Payload;
+      Success           :    out Boolean)
+   is
+      Request : DP_Defs.Aux_Request;
+
+      Response : DP_Defs.Aux_Response;
+      Ignored_Response_Length : DP_Defs.Aux_Response_Length;
+   begin
+      Fill_Aux_Request
+        (Request  => Request,
+         Command  => Command,
+         Address  => Address,
+         Length   => Length);
+      Request (4 .. Length + 4 - 1) := Data (0 .. Length - 1);
+      for Try in Positive range 1 .. 7 loop
+         pragma Warnings (GNATprove, Off,
+                          "unused assignment to ""Ignored_Response_Length""",
+                          Reason => "No response expected here");
+         Do_Aux_Request
+           (Port              => Port,
+            Request           => Request,
+            Request_Length    => (if Is_Empty (Request) then 3 else 4 + Length),
+            Response          => Response,
+            Response_Length   => Ignored_Response_Length,
+            Success           => Success);
+         exit when not (Success and Is_I2C_Defer (Response));
+
+         -- Command was already AUX-acked. Thus, only query for
+         -- new status from now on until we get I2C-acked too.
+         Fill_Aux_Request
+           (Request  => Request,
+            Command  => (Command and DP_AUX_I2C_MOT) or DP_AUX_I2C_WR_STATUS_REQ,
+            Address  => Address,
+            Length   => 0);
+         Time.U_Delay (500);
+      end loop;
+      Success := Success and then Is_I2C_Ack (Response);
+   end I2C_Out_Packet;
+
+   procedure I2C_In_Packet
+     (Port              : in     T;
+      Command           : in     DP_Defs.Aux_Message_Command;
+      Address           : in     DP_Defs.Aux_Message_Address;
+      Length            : in     DP_Defs.Aux_Payload_Length;
+      Response          :    out DP_Defs.Aux_Response;
+      Response_Length   :    out DP_Defs.Aux_Response_Length;
+      Success           :    out Boolean)
+   is
+      Request : DP_Defs.Aux_Request;
+   begin
+      Fill_Aux_Request
+        (Request  => Request,
+         Command  => Command,
+         Address  => Address,
+         Length   => Length);
+      for Try in Positive range 1 .. 7 loop
+         Do_Aux_Request
+           (Port              => Port,
+            Request           => Request,
+            Request_Length    => (if Is_Empty (Request) then 3 else 4),
+            Response          => Response,
+            Response_Length   => Response_Length,
+            Success           => Success);
+         exit when not (Success and Is_I2C_Defer (Response));
+
+         Time.U_Delay (500);
+      end loop;
+      Success := Success and then Is_I2C_Ack (Response);
+   end I2C_In_Packet;
+
+   procedure I2C_Empty_Packet
+     (Port              : in     T;
+      Command           : in     DP_Defs.Aux_Message_Command;
+      Address           : in     DP_Defs.Aux_Message_Address;
+      Success           :    out Boolean)
+   is
+      Ignored_Response        : DP_Defs.Aux_Response;
+      Ignored_Response_Length : DP_Defs.Aux_Response_Length;
+   begin
+      pragma Warnings (GNATprove, Off,
+                       "unused assignment to ""Ignored_Response*""",
+                       Reason => "No response expected here");
+      I2C_In_Packet
+        (Port              => Port,
+         Command           => Command,
+         Address           => Address,
+         Length            => 0,
+         Response          => Ignored_Response,
+         Response_Length   => Ignored_Response_Length,
+         Success           => Success);
+   end I2C_Empty_Packet;
+
+   ----------------------------------------------------------------------------
+
+   procedure Do_I2C_Write
+     (Port     : in     T;
+      Address  : in     I2C.Transfer_Address;
+      Length   : in     DP_Defs.Aux_Payload_Length;
+      Data     : in     DP_Defs.Aux_Payload;
+      Success  :    out Boolean)
+   is
+      Ignored_Success : Boolean;
+   begin
+      -- I2C address "start" packet
+      I2C_Empty_Packet
+        (Port     => Port,
+         Command  => DP_AUX_I2C_WRITE or DP_AUX_I2C_MOT,
+         Address  => DP_Defs.Aux_Message_Address (Address),
+         Success  => Success);
+
+      if Success then
+         I2C_Out_Packet
+           (Port              => Port,
+            Command           => DP_AUX_I2C_WRITE or DP_AUX_I2C_MOT,
+            Address           => DP_Defs.Aux_Message_Address (Address),
+            Length            => Length,
+            Data              => Data,
+            Success           => Success);
+
+         pragma Warnings
+           (GNATprove, Off, "unused assignment to ""Ignored_Success""",
+            Reason => "Doesn't matter as long as the transfer itself succeeds");
+         -- I2C address "stop" packet
+         I2C_Empty_Packet
+           (Port     => Port,
+            Command  => DP_AUX_I2C_WRITE,
+            Address  => DP_Defs.Aux_Message_Address (Address),
+            Success  => Ignored_Success);
+      end if;
+   end Do_I2C_Write;
+
+   procedure Do_I2C_Read
+     (Port     : in     T;
+      Address  : in     I2C.Transfer_Address;
+      Length   : in out I2C.Transfer_Length;
+      Data     : in out I2C.Transfer_Data;
+      Success  :    out Boolean)
+   is
+      Xfered   : Natural := 0;
+      Chunk    : DP_Defs.Aux_Payload_Length := DP_Defs.Aux_Payload_Length'Last;
+
+      Tries       : Natural := 0;
+      Max_Tries   : constant := 4;
+
+      Response : DP_Defs.Aux_Response;
+      Response_Length : DP_Defs.Aux_Response_Length;
+
+      Ignored_Success : Boolean;
+   begin
+      -- I2C address "start" packet
+      I2C_Empty_Packet
+        (Port     => Port,
+         Command  => DP_AUX_I2C_READ or DP_AUX_I2C_MOT,
+         Address  => DP_Defs.Aux_Message_Address (Address),
+         Success  => Success);
+
+      while Success and then (Xfered < Length and Tries < Max_Tries) loop
+         I2C_In_Packet
+           (Port              => Port,
+            Command           => DP_AUX_I2C_READ or DP_AUX_I2C_MOT,
+            Address           => DP_Defs.Aux_Message_Address (Address),
+            Length            => Natural'Min (Chunk, Length - Xfered),
+            Response          => Response,
+            Response_Length   => Response_Length,
+            Success           => Success);
+
+         if Success and then Response_Length >= 2 then
+            Chunk := Natural'Min (Response_Length - 1, Length - Xfered);
+            Data (Xfered .. Xfered + Chunk - 1) := Response (1 .. Chunk);
+            Xfered := Xfered + Chunk;
+            Tries := 0;
+         else
+            Tries := Tries + 1;
+         end if;
+         pragma Loop_Invariant (Xfered <= Length);
+      end loop;
+
+      if Success then
+         pragma Warnings
+           (GNATprove, Off, "unused assignment to ""Ignored_Success""",
+            Reason => "Doesn't matter as long as the transfer itself succeeds");
+         -- I2C address "stop" packet
+         I2C_Empty_Packet
+           (Port     => Port,
+            Command  => DP_AUX_I2C_READ,
+            Address  => DP_Defs.Aux_Message_Address (Address),
+            Success  => Ignored_Success);
+      end if;
+
+      Success := Success and then Tries < Max_Tries;
+      Length := Xfered;
+   end Do_I2C_Read;
+
+   ----------------------------------------------------------------------------
+
+   procedure I2C_Read
+     (Port     : in     T;
+      Address  : in     I2C.Transfer_Address;
+      Length   : in out I2C.Transfer_Length;
+      Data     :    out I2C.Transfer_Data;
+      Success  :    out Boolean)
+   is
+      Index_Payload : DP_Defs.Aux_Payload;
+   begin
+      Data := (others => 16#00#);
+
+      Index_Payload := (others => 16#00#);   -- send index 0
+      Do_I2C_Write (Port, Address, 1, Index_Payload, Success);
+
+      if Success then
+         Do_I2C_Read (Port, Address, Length, Data, Success);
+      end if;
+   end I2C_Read;
+
+end HW.GFX.DP_Aux_Ch;
diff --git a/common/hw-gfx-dp_aux_ch.ads b/common/hw-gfx-dp_aux_ch.ads
new file mode 100644
index 0000000..c65f860
--- /dev/null
+++ b/common/hw-gfx-dp_aux_ch.ads
@@ -0,0 +1,54 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.I2C;
+with HW.GFX.DP_Defs;
+
+private generic
+
+   type T (<>) is limited private;
+
+   with procedure Aux_Request
+     (Port              : in     T;
+      Request           : in     DP_Defs.Aux_Request;
+      Request_Length    : in     DP_Defs.Aux_Request_Length;
+      Response          :    out DP_Defs.Aux_Response;
+      Response_Length   :    out DP_Defs.Aux_Response_Length;
+      Success           :    out Boolean);
+
+package HW.GFX.DP_Aux_Ch is
+
+   procedure Aux_Read
+     (Port     : in     T;
+      Address  : in     DP_Defs.Aux_Message_Address;
+      Length   : in out DP_Defs.Aux_Payload_Length;
+      Data     :    out DP_Defs.Aux_Payload;
+      Success  :    out Boolean);
+
+   procedure Aux_Write
+     (Port     : in     T;
+      Address  : in     DP_Defs.Aux_Message_Address;
+      Length   : in     DP_Defs.Aux_Payload_Length;
+      Data     : in     DP_Defs.Aux_Payload;
+      Success  :    out Boolean);
+
+   ----------------------------------------------------------------------------
+
+   procedure I2C_Read
+     (Port     : in     T;
+      Address  : in     I2C.Transfer_Address;
+      Length   : in out I2C.Transfer_Length;
+      Data     :    out I2C.Transfer_Data;
+      Success  :    out Boolean);
+
+end HW.GFX.DP_Aux_Ch;
diff --git a/common/hw-gfx-dp_defs.ads b/common/hw-gfx-dp_defs.ads
new file mode 100644
index 0000000..9332f9e
--- /dev/null
+++ b/common/hw-gfx-dp_defs.ads
@@ -0,0 +1,32 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.DP_Defs is
+
+   type Aux_Message_Command is mod 2 ** 4;
+   type Aux_Message_Address is mod 2 ** 20;
+
+   subtype Aux_Payload_Length is Natural range 0 .. 16;
+   subtype Aux_Payload_Index is Natural range 0 .. Aux_Payload_Length'Last - 1;
+   subtype Aux_Payload is Buffer (Aux_Payload_Index);
+
+   subtype Aux_Request_Length is Natural range 3 .. 20;
+   subtype Aux_Request_Index is Natural range 0 .. Aux_Request_Length'Last - 1;
+   subtype Aux_Request is Buffer (Aux_Request_Index);
+
+   subtype Aux_Response_Length is Natural range 1 .. 17;
+   subtype Aux_Response_Index is
+      Natural range 0 .. Aux_Response_Length'Last - 1;
+   subtype Aux_Response is Buffer (Aux_Response_Index);
+
+end HW.GFX.DP_Defs;
diff --git a/common/hw-gfx-dp_info.adb b/common/hw-gfx-dp_info.adb
new file mode 100644
index 0000000..7e47885
--- /dev/null
+++ b/common/hw-gfx-dp_info.adb
@@ -0,0 +1,380 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with Ada.Unchecked_Conversion;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+with HW.GFX.DP_Defs;
+
+use type HW.Word8;
+
+package body HW.GFX.DP_Info is
+
+   procedure Read_Caps
+     (Link     : in out DP_Link;
+      Port     : in     T;
+      Success  :    out Boolean)
+   is
+      Data : DP_Defs.Aux_Payload;
+      Length : DP_Defs.Aux_Payload_Length;
+
+      Caps_Size : constant := 15;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Length := Caps_Size;
+      Aux_Ch.Aux_Read
+        (Port     => Port,
+         Address  => 16#00000#,
+         Length   => Length,
+         Data     => Data,
+         Success  => Success);
+      Success := Success and Length = Caps_Size;
+
+      if Length = Caps_Size then
+         Link.Receiver_Caps.Rev := Data (0);
+         case Data (1) is
+            when 16#06# =>
+               Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_1_62;
+            when 16#0a# =>
+               Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_2_7;
+            when 16#14# =>
+               Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_5_4;
+            when others =>
+               if Data (1) > 16#14# then
+                  Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_5_4;
+               else
+                  Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_1_62;
+               end if;
+         end case;
+         case Data (2) and 16#1f# is
+            when 0 | 1  =>
+               Link.Receiver_Caps.Max_Lane_Count := DP_Lane_Count_1;
+            when 2 | 3  =>
+               Link.Receiver_Caps.Max_Lane_Count := DP_Lane_Count_2;
+            when others =>
+               Link.Receiver_Caps.Max_Lane_Count := DP_Lane_Count_4;
+         end case;
+         Link.Receiver_Caps.TPS3_Supported     := (Data (2) and 16#40#) /= 0;
+         Link.Receiver_Caps.Enhanced_Framing   := (Data (2) and 16#80#) /= 0;
+         Link.Receiver_Caps.No_Aux_Handshake   := (Data (3) and 16#40#) /= 0;
+         Link.Receiver_Caps.Aux_RD_Interval    := Data (14);
+
+         pragma Debug (Debug.New_Line);
+         pragma Debug (Debug.Put_Line ("DPCD:"));
+         pragma Debug (Debug.Put_Reg8 ("  Rev             ", Data (0)));
+         pragma Debug (Debug.Put_Reg8 ("  Max_Link_Rate   ", Data (1)));
+         pragma Debug (Debug.Put_Reg8 ("  Max_Lane_Count  ", Data (2) and 16#1f#));
+         pragma Debug (Debug.Put_Reg8 ("  TPS3_Supported  ", Data (2) and 16#40#));
+         pragma Debug (Debug.Put_Reg8 ("  Enhanced_Framing", Data (2) and 16#80#));
+         pragma Debug (Debug.Put_Reg8 ("  No_Aux_Handshake", Data (3) and 16#40#));
+         pragma Debug (Debug.Put_Reg8 ("  Aux_RD_Interval ", Data (14)));
+         pragma Debug (Debug.New_Line);
+      end if;
+   end Read_Caps;
+
+   procedure Minimum_Lane_Count
+     (Link        : in out DP_Link;
+      Mode        : in     Mode_Type;
+      Success     :    out Boolean)
+   with
+      Depends => ((Link, Success) => (Link, Mode))
+   is
+      function Link_Pixel_Per_Second
+        (Link_Rate : DP_Bandwidth)
+         return Positive
+      with
+         Post => Pos64 (Link_Pixel_Per_Second'Result) <=
+                  ((DP_Symbol_Rate_Type'Last * 8) / 3) / BPC_Type'First
+      is
+      begin
+         -- Link_Rate is brutto with 8/10 bit symbols; three colors
+         pragma Assert (Positive (DP_Symbol_Rate (Link_Rate)) <= (Positive'Last / 8) * 3);
+         pragma Assert ((Int64 (DP_Symbol_Rate (Link_Rate)) * 8) / 3
+                        >= Int64 (BPC_Type'Last));
+         return Positive
+           (((Int64 (DP_Symbol_Rate (Link_Rate)) * 8) / 3)
+            / Int64 (Mode.BPC));
+      end Link_Pixel_Per_Second;
+
+      Count : Natural;
+   begin
+      Count := Link_Pixel_Per_Second (Link.Bandwidth);
+      Count := (Positive (Mode.Dotclock) + Count - 1) / Count;
+
+      Success := True;
+      case Count is
+         when 1      => Link.Lane_Count := DP_Lane_Count_1;
+         when 2      => Link.Lane_Count := DP_Lane_Count_2;
+         when 3 | 4  => Link.Lane_Count := DP_Lane_Count_4;
+         when others => Success := False;
+      end case;
+   end Minimum_Lane_Count;
+
+   procedure Preferred_Link_Setting
+     (Link        : in out DP_Link;
+      Mode        : in     Mode_Type;
+      Success     :    out Boolean)
+   is
+   begin
+      Link.Bandwidth          := Link.Receiver_Caps.Max_Link_Rate;
+      Link.Enhanced_Framing   := Link.Receiver_Caps.Enhanced_Framing;
+
+      Minimum_Lane_Count (Link, Mode, Success);
+
+      Success := Success and
+                  Link.Lane_Count <= Link.Receiver_Caps.Max_Lane_Count;
+
+      pragma Debug (Success, Debug.Put ("Trying DP settings: Symbol Rate = "));
+      pragma Debug (Success, Debug.Put_Int32
+        (Int32 (DP_Symbol_Rate (Link.Bandwidth))));
+      pragma Debug (Success, Debug.Put ("; Lane Count = "));
+      pragma Debug (Success, Debug.Put_Int32
+        (Int32 (Lane_Count_As_Integer (Link.Lane_Count))));
+      pragma Debug (Success, Debug.New_Line);
+      pragma Debug (Success, Debug.New_Line);
+
+      pragma Debug (not Success, Debug.Put_Line
+        ("Mode requirements exceed available bandwidth!"));
+   end Preferred_Link_Setting;
+
+   procedure Next_Link_Setting
+     (Link        : in out DP_Link;
+      Mode        : in     Mode_Type;
+      Success     :    out Boolean)
+   is
+   begin
+      if Link.Bandwidth > DP_Bandwidth'First then
+         Link.Bandwidth := DP_Bandwidth'Pred (Link.Bandwidth);
+
+         Minimum_Lane_Count (Link, Mode, Success);
+
+         Success := Success and
+                    Link.Lane_Count <= Link.Receiver_Caps.Max_Lane_Count;
+      else
+         Success := False;
+      end if;
+
+      pragma Debug (Success, Debug.Put ("Trying DP settings: Symbol Rate = "));
+      pragma Debug (Success, Debug.Put_Int32
+        (Int32 (DP_Symbol_Rate (Link.Bandwidth))));
+      pragma Debug (Success, Debug.Put ("; Lane Count = "));
+      pragma Debug (Success, Debug.Put_Int32
+        (Int32 (Lane_Count_As_Integer (Link.Lane_Count))));
+      pragma Debug (Success, Debug.New_Line);
+      pragma Debug (Success, Debug.New_Line);
+   end Next_Link_Setting;
+
+   ----------------------------------------------------------------------------
+
+   procedure Calculate_M_N
+     (Link     : in     DP_Link;
+      Mode     : in     Mode_Type;
+      Data_M   :    out M_Type;
+      Data_N   :    out N_Type;
+      Link_M   :    out M_Type;
+      Link_N   :    out N_Type)
+   is
+      DATA_N_MAX : constant := 16#800000#;
+      LINK_N_MAX : constant := 16#100000#;
+
+      subtype Calc_M_Type is Int64 range 0 .. 2 ** 36;
+      subtype Calc_N_Type is Int64 range 0 .. 2 ** 36;
+      subtype N_Rounded_Type is Int64 range
+         0 .. Int64'Max (DATA_N_MAX, LINK_N_MAX);
+
+      M : Calc_M_Type;
+      N : Calc_N_Type;
+
+      procedure Cancel_M_N
+        (M     : in out Calc_M_Type;
+         N     : in out Calc_N_Type;
+         N_Max : in     N_Rounded_Type)
+      with
+         Depends => ((M, N) => (M, N, N_max)),
+         Pre => (N > 0 and M in 0 .. Calc_M_Type'Last / 2),
+         Post => (M <= M_N_Max and N <= M_N_Max)
+      is
+         Orig_N : constant Calc_N_Type := N;
+
+         function Round_N (N : Calc_N_Type) return N_Rounded_Type
+         with
+            Post => (Round_N'Result <= N * 2)
+         is
+            RN : Calc_N_Type;
+            RN2 : Calc_N_Type := N_Max;
+         begin
+            loop
+               RN := RN2;
+               RN2 := RN2 / 2;
+               exit when RN2 < N;
+               pragma Loop_Invariant (RN2 = RN / 2 and RN2 in N .. N_Max);
+            end loop;
+            return RN;
+         end Round_N;
+      begin
+         N := Round_N (N);
+
+         -- The automatic provers need a little nudge here.
+         pragma Assert
+            (if M <= Calc_M_Type'Last/2 and
+                N <= Orig_N * 2 and
+                Orig_N > 0 and
+                M > 0
+             then
+                M * N / Orig_N <= Calc_M_Type'Last);
+
+         pragma Annotate (GNATprove, False_Positive,
+            "assertion might fail",
+            "The property cannot be proven automatically. An Isabelle proof is included as an axiom");
+
+         M := M * N / Orig_N;
+
+         -- This loop is never hit for sane values (i.e. M <= N) but
+         -- we have to make sure returned values are always in range.
+         while M > M_N_Max loop
+            pragma Loop_Invariant (N <= M_N_Max);
+            M := M / 2;
+            N := N / 2;
+         end loop;
+      end Cancel_M_N;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      pragma Assert (3
+                     * Mode.BPC
+                     * Mode.Dotclock
+                     in Pos64);
+      M := 3
+           * Mode.BPC
+           * Mode.Dotclock;
+
+      pragma Assert (8
+                     * DP_Symbol_Rate (Link.Bandwidth)
+                     * Lane_Count_As_Integer (Link.Lane_Count)
+                     in Pos64);
+      N := 8
+           * DP_Symbol_Rate (Link.Bandwidth)
+           * Lane_Count_As_Integer (Link.Lane_Count);
+
+      Cancel_M_N (M, N, DATA_N_MAX);
+      Data_M := M;
+      Data_N := N;
+
+      -------------------------------------------------------------------
+
+      M := Pos64 (Mode.Dotclock);
+      N := Pos64 (DP_Symbol_Rate (Link.Bandwidth));
+
+      Cancel_M_N (M, N, LINK_N_MAX);
+      Link_M := M;
+      Link_N := N;
+   end Calculate_M_N;
+
+   ----------------------------------------------------------------------------
+
+   procedure Read_Link_Status
+     (Port     : in     T;
+      Status   :    out Link_Status;
+      Success  :    out Boolean)
+   is
+      subtype Status_Index is DP_Defs.Aux_Payload_Index range 0 .. 5;
+      subtype Status_Buffer is Buffer (Status_Index);
+      function Buffer_As_Status is new Ada.Unchecked_Conversion
+        (Source => Status_Buffer, Target => Link_Status);
+
+      Data : DP_Defs.Aux_Payload;
+      Length : DP_Defs.Aux_Payload_Length;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Length := Status_Index'Last + 1;
+      Aux_Ch.Aux_Read
+        (Port     => Port,
+         Address  => 16#00202#,
+         Length   => Length,
+         Data     => Data,
+         Success  => Success);
+      Success := Success and Length = Status_Index'Last + 1;
+      Status := Buffer_As_Status (Data (Status_Index));
+   end Read_Link_Status;
+
+   function All_CR_Done
+     (Status   : Link_Status;
+      Link     : DP_Link)
+      return Boolean
+   is
+      CR_Done : Boolean := True;
+   begin
+      for Lane in Lane_Index
+         range 0 .. Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
+      loop
+         CR_Done := CR_Done and Status.Lanes (Lane).CR_Done;
+      end loop;
+      return CR_Done;
+   end All_CR_Done;
+
+   function All_EQ_Done
+     (Status   : Link_Status;
+      Link     : DP_Link)
+      return Boolean
+   is
+      EQ_Done : Boolean := True;
+   begin
+      for Lane in Lane_Index
+         range 0 .. Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
+      loop
+         EQ_Done := EQ_Done and Status.Lanes (Lane).CR_Done
+                            and Status.Lanes (Lane).Channel_EQ_Done
+                            and Status.Lanes (Lane).Symbol_Locked;
+      end loop;
+      return EQ_Done and Status.Interlane_Align_Done;
+   end All_EQ_Done;
+
+   function Max_Requested_VS
+     (Status   : Link_Status;
+      Link     : DP_Link)
+      return DP_Voltage_Swing
+   is
+      VS : DP_Voltage_Swing := DP_Voltage_Swing'First;
+   begin
+      for Lane in Lane_Index
+         range 0 .. Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
+      loop
+         if Status.Adjust_Requests (Lane).Voltage_Swing > VS then
+            VS := Status.Adjust_Requests (Lane).Voltage_Swing;
+         end if;
+      end loop;
+      return VS;
+   end Max_Requested_VS;
+
+   function Max_Requested_Emph
+     (Status   : Link_Status;
+      Link     : DP_Link)
+      return DP_Pre_Emph
+   is
+      Emph : DP_Pre_Emph := DP_Pre_Emph'First;
+   begin
+      for Lane in Lane_Index
+         range 0 .. Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
+      loop
+         if Status.Adjust_Requests (Lane).Pre_Emph > Emph then
+            Emph := Status.Adjust_Requests (Lane).Pre_Emph;
+         end if;
+      end loop;
+      return Emph;
+   end Max_Requested_Emph;
+
+end HW.GFX.DP_Info;
diff --git a/common/hw-gfx-dp_info.ads b/common/hw-gfx-dp_info.ads
new file mode 100644
index 0000000..d793dff
--- /dev/null
+++ b/common/hw-gfx-dp_info.ads
@@ -0,0 +1,135 @@
+--
+-- Copyright (C) 2015 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.DP_Aux_Ch;
+
+private generic
+
+   type T (<>) is limited private;
+
+   with package Aux_Ch is new DP_Aux_Ch (T => T, others => <>);
+
+package HW.GFX.DP_Info is
+
+   type DP_Voltage_Swing is (VS_Level_0, VS_Level_1, VS_Level_2, VS_Level_3);
+
+   type DP_Pre_Emph is (Emph_Level_0, Emph_Level_1, Emph_Level_2, Emph_Level_3);
+
+   type Train_Set is record
+      Voltage_Swing  : DP_Voltage_Swing;
+      Pre_Emph       : DP_Pre_Emph;
+   end record;
+
+   type Training_Pattern is (TP_1, TP_2, TP_3, TP_Idle, TP_None);
+
+   ----------------------------------------------------------------------------
+
+   type Lane_Index is new Natural range 0 .. 3;
+
+   type Lane_Status is record
+      CR_Done           : Boolean;
+      Channel_EQ_Done   : Boolean;
+      Symbol_Locked     : Boolean;
+      Reserved          : Boolean;
+   end record;
+   for Lane_Status use record
+      CR_Done           at 16#00# range 0 .. 0;
+      Channel_EQ_Done   at 16#00# range 1 .. 1;
+      Symbol_Locked     at 16#00# range 2 .. 2;
+      Reserved          at 16#00# range 3 .. 3;
+   end record;
+   type Lanes_Status is array (Lane_Index) of Lane_Status;
+   pragma Pack (Lanes_Status);
+
+   type Adjust_Request is record
+      Voltage_Swing  : DP_Voltage_Swing;
+      Pre_Emph       : DP_Pre_Emph;
+   end record;
+   for Adjust_Request use record
+      Voltage_Swing  at 16#00# range 0 .. 1;
+      Pre_Emph       at 16#00# range 2 .. 3;
+   end record;
+   type Adjust_Requests_Array is array (Lane_Index) of Adjust_Request;
+   pragma Pack (Adjust_Requests_Array);
+
+   type Link_Status is record
+      Lanes                : Lanes_Status;
+      Interlane_Align_Done : Boolean;
+      Adjust_Requests      : Adjust_Requests_Array;
+   end record;
+   for Link_Status use record
+      Lanes                at 16#00# range 0 .. 15;
+      Interlane_Align_Done at 16#02# range 0 ..  0;
+      Adjust_Requests      at 16#04# range 0 .. 15;
+   end record;
+
+   ----------------------------------------------------------------------------
+
+   procedure Read_Caps
+     (Link     : in out DP_Link;
+      Port     : in     T;
+      Success  :    out Boolean);
+
+   procedure Preferred_Link_Setting
+     (Link        : in out DP_Link;
+      Mode        : in     Mode_Type;
+      Success     :    out Boolean);
+
+   procedure Next_Link_Setting
+     (Link        : in out DP_Link;
+      Mode        : in     Mode_Type;
+      Success     :    out Boolean);
+
+   ----------------------------------------------------------------------------
+
+   M_N_Max : constant := 2 ** 24 - 1;
+
+   subtype M_Type is Int64 range 0 .. M_N_Max;
+   subtype N_Type is Int64 range 0 .. M_N_Max;
+
+   procedure Calculate_M_N
+     (Link     : in     DP_Link;
+      Mode     : in     Mode_Type;
+      Data_M   :    out M_Type;
+      Data_N   :    out N_Type;
+      Link_M   :    out M_Type;
+      Link_N   :    out N_Type);
+
+   ----------------------------------------------------------------------------
+
+   procedure Read_Link_Status
+     (Port     : in     T;
+      Status   :    out Link_Status;
+      Success  :    out Boolean);
+
+   function All_CR_Done
+     (Status   : Link_Status;
+      Link     : DP_Link)
+      return Boolean;
+
+   function All_EQ_Done
+     (Status   : Link_Status;
+      Link     : DP_Link)
+      return Boolean;
+
+   function Max_Requested_VS
+     (Status   : Link_Status;
+      Link     : DP_Link)
+      return DP_Voltage_Swing;
+
+   function Max_Requested_Emph
+     (Status   : Link_Status;
+      Link     : DP_Link)
+      return DP_Pre_Emph;
+
+end HW.GFX.DP_Info;
diff --git a/common/hw-gfx-dp_training.adb b/common/hw-gfx-dp_training.adb
new file mode 100644
index 0000000..4ba548a
--- /dev/null
+++ b/common/hw-gfx-dp_training.adb
@@ -0,0 +1,412 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with Ada.Unchecked_Conversion;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+with HW.Time;
+with HW.GFX.DP_Defs;
+
+package body HW.GFX.DP_Training is
+
+   pragma Warnings (GNATprove, Off, "unused initial value of ""Port""*",
+                    Reason => "Needed for a common interface");
+   function Training_Set
+     (Port        : T;
+      Train_Set   : DP_Info.Train_Set)
+      return Word8
+   is
+      use type DP_Info.DP_Voltage_Swing;
+      use type DP_Info.DP_Pre_Emph;
+      use type Word8;
+      Value : Word8;
+   begin
+      case Train_Set.Voltage_Swing is
+         when DP_Info.VS_Level_0   => Value := 16#00#;
+         when DP_Info.VS_Level_1   => Value := 16#01#;
+         when DP_Info.VS_Level_2   => Value := 16#02#;
+         when DP_Info.VS_Level_3   => Value := 16#03#;
+      end case;
+      if Train_Set.Voltage_Swing = Max_V_Swing (Port) then
+         Value := Value or 16#04#;
+      end if;
+
+      case Train_Set.Pre_Emph is
+         when DP_Info.Emph_Level_0 => Value := Value or 16#00#;
+         when DP_Info.Emph_Level_1 => Value := Value or 16#08#;
+         when DP_Info.Emph_Level_2 => Value := Value or 16#10#;
+         when DP_Info.Emph_Level_3 => Value := Value or 16#18#;
+      end case;
+      if Train_Set.Pre_Emph = Max_Pre_Emph (Port, Train_Set) then
+         Value := Value or 16#20#;
+      end if;
+
+      return Value;
+   end Training_Set;
+   pragma Warnings (GNATprove, On, "unused initial value of ""Port""*");
+
+   ----------------------------------------------------------------------------
+
+   function Lane_Count (Link : DP_Link) return Positive
+   with
+      Post => Lane_Count'Result <= 4
+   is
+   begin
+      return Positive (Lane_Count_As_Integer (Link.Lane_Count));
+   end Lane_Count;
+
+   procedure Sink_Init
+     (Port        : in     Aux_T;
+      Link        : in     DP_Link;
+      Success     :    out Boolean)
+   is
+      use type Word8;
+      function Link_Rate_As_Word8 is new Ada.Unchecked_Conversion
+        (Source => DP_Bandwidth, Target => Word8);
+      Data : DP_Defs.Aux_Payload;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Data :=
+        (0      => Link_Rate_As_Word8 (Link.Bandwidth),
+         1      => Word8 (Lane_Count (Link)),
+         others => 0);  -- Don't care
+
+      if Link.Enhanced_Framing then
+         Data (1) := Data (1) or 16#80#;
+      end if;
+
+      Aux_Ch.Aux_Write
+        (Port     => Port,
+         Address  => 16#00100#,     -- LINK_BW_SET, LANE_COUNT_SET
+         Length   => 2,
+         Data     => Data,
+         Success  => Success);
+      Success := Success or Link.Opportunistic_Training;
+
+      if Success then
+         Data (0) := 16#00#;  -- no downspread
+         Data (1) := 16#01#;  -- ANSI8B10B coding
+
+         Aux_Ch.Aux_Write
+           (Port     => Port,
+            Address  => 16#00107#,     -- DOWNSPREAD_CTRL,
+            Length   => 2,             -- MAIN_LINK_CHANNEL_CODING_SET
+            Data     => Data,
+            Success  => Success);
+         Success := Success or Link.Opportunistic_Training;
+      end if;
+   end Sink_Init;
+
+   procedure Sink_Set_Training_Pattern
+     (DP          : in     Aux_T;
+      Link        : in     DP_Link;
+      Pattern     : in     DP_Info.Training_Pattern;
+      Success     :    out Boolean)
+   is
+      use type DP_Info.Training_Pattern;
+
+      type TP_Array is array (DP_Info.Training_Pattern) of Word8;
+      TP : constant TP_Array := TP_Array'
+        (DP_Info.TP_1 => 16#21#, DP_Info.TP_2 => 16#22#, DP_Info.TP_3 => 16#23#,
+         DP_Info.TP_Idle => 16#00#, DP_Info.TP_None => 16#00#);
+
+      Data : DP_Defs.Aux_Payload;
+      Length : Positive := 1;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Data :=
+        (0      => TP (Pattern),
+         others => 0);  -- Don't care
+
+      if Pattern < DP_Info.TP_Idle then
+         Length := Length + Lane_Count (Link);
+      end if;
+      Aux_Ch.Aux_Write
+        (Port     => DP,
+         Address  => 16#00102#,     -- TRAINING_PATTERN_SET
+         Length   => Length,
+         Data     => Data,
+         Success  => Success);
+   end Sink_Set_Training_Pattern;
+
+   procedure Sink_Set_Signal_Levels
+     (Port        : in     T;
+      DP          : in     Aux_T;
+      Link        : in     DP_Link;
+      Train_Set   : in     DP_Info.Train_Set;
+      Success     :    out Boolean)
+   is
+      Data  : DP_Defs.Aux_Payload;
+      T_Set : constant Word8 := Training_Set (Port, Train_Set);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Data                                := (others => 0); -- Initialize
+      Data (0 .. Lane_Count (Link) - 1)   := (others => T_Set);
+
+      Aux_Ch.Aux_Write
+        (Port     => DP,
+         Address  => 16#00103#,     -- TRAINING_LANEx_SET
+         Length   => Lane_Count (Link),
+         Data     => Data,
+         Success  => Success);
+   end Sink_Set_Signal_Levels;
+
+   pragma Warnings (GNATprove, Off, "unused initial value of ""Port""*",
+                    Reason => "Needed for a common interface");
+   procedure Sink_Adjust_Training
+     (Port        : in     T;
+      DP          : in     Aux_T;
+      Link        : in     DP_Link;
+      Train_Set   : in out DP_Info.Train_Set;
+      CR_Done     : in out Boolean;
+      EQ_Done     :    out Boolean;
+      Success     :    out Boolean)
+   is
+      use type DP_Info.DP_Voltage_Swing;
+      use type DP_Info.DP_Pre_Emph;
+
+      Status : DP_Info.Link_Status;
+      CR_Was_Done : constant Boolean := CR_Done;
+
+      pragma Warnings
+        (GNATprove, Off, "subprogram ""Dump_Link_Status"" has no effect*",
+         Reason => "It's only used for debugging");
+      procedure Dump_Link_Status
+      is
+      begin
+         Debug.New_Line;
+         Debug.Put_Line ("Link Status:");
+
+         for Lane in DP_Info.Lane_Index range 0
+            .. DP_Info.Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
+         loop
+            Debug.Put ("  Lane");
+            Debug.Put_Int8 (Int8 (Lane));
+            Debug.Put_Line (":");
+
+            Debug.Put_Line ("    CR_Done        : " &
+              (if Status.Lanes (Lane).CR_Done         then "1" else "0"));
+            Debug.Put_Line ("    Channel_EQ_Done: " &
+              (if Status.Lanes (Lane).Channel_EQ_Done then "1" else "0"));
+            Debug.Put_Line ("    Symbol_Locked  : " &
+              (if Status.Lanes (Lane).Symbol_Locked   then "1" else "0"));
+         end loop;
+
+         Debug.Put_Line ("  Interlane_Align_Done: " &
+           (if Status.Interlane_Align_Done then "1" else "0"));
+
+         for Lane in DP_Info.Lane_Index range 0
+            .. DP_Info.Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
+         loop
+            Debug.Put ("  Adjust");
+            Debug.Put_Int8 (Int8 (Lane));
+            Debug.Put_Line (":");
+
+            Debug.Put ("    Voltage_Swing: ");
+            Debug.Put_Int8 (Int8 (DP_Info.DP_Voltage_Swing'Pos
+              (Status.Adjust_Requests (Lane).Voltage_Swing)));
+            Debug.New_Line;
+            Debug.Put ("    Pre_Emph     : ");
+            Debug.Put_Int8 (Int8 (DP_Info.DP_Pre_Emph'Pos
+              (Status.Adjust_Requests (Lane).Pre_Emph)));
+            Debug.New_Line;
+         end loop;
+
+         Debug.New_Line;
+      end Dump_Link_Status;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      DP_Info.Read_Link_Status
+        (Port     => DP,
+         Status   => Status,
+         Success  => Success);
+
+      pragma Debug (Success, Dump_Link_Status);
+
+      CR_Done := Success and then DP_Info.All_CR_Done (Status, Link);
+      EQ_Done := Success and then DP_Info.All_EQ_Done (Status, Link);
+      Success := Success and then (CR_Done or not CR_Was_Done);
+
+      if Success and not CR_Done then
+         Train_Set.Voltage_Swing :=
+            DP_Info.Max_Requested_VS (Status, Link);
+         if Train_Set.Voltage_Swing > Max_V_Swing (Port)
+         then
+            Train_Set.Voltage_Swing := Max_V_Swing (Port);
+         end if;
+      end if;
+
+      -- According to DP spec, only change preemphasis during channel
+      -- equalization. What to do if sink requests it during clock recovery?
+      -- Linux always accepts new values from the sink, we don't, for now.
+      if Success and then (CR_Was_Done and not EQ_Done) then
+         Train_Set.Pre_Emph :=
+            DP_Info.Max_Requested_Emph (Status, Link);
+         if Train_Set.Pre_Emph > Max_Pre_Emph (Port, Train_Set)
+         then
+            Train_Set.Pre_Emph := Max_Pre_Emph (Port, Train_Set);
+         end if;
+      end if;
+   end Sink_Adjust_Training;
+   pragma Warnings (GNATprove, On, "unused initial value of ""Port""*");
+
+   ----------------------------------------------------------------------------
+
+   procedure Train_DP
+     (Port     : in     T;
+      Link     : in     DP_Link;
+      Success  :    out Boolean)
+   is
+      use type DP_Info.DP_Voltage_Swing;
+      use type DP_Info.DP_Pre_Emph;
+      use type Word8;
+
+      DP : constant Aux_T := To_Aux (Port);
+
+      Retries : Natural;
+      Max_Retry : constant := 4;
+      CR_Done, EQ_Done : Boolean := False;
+
+      EQ_Pattern : constant DP_Info.Training_Pattern :=
+        (if TPS3_Supported and Link.Receiver_Caps.TPS3_Supported then
+            DP_Info.TP_3
+         else
+            DP_Info.TP_2);
+
+      Train_Set, Last_Train_Set : DP_Info.Train_Set;
+
+      function CR_Delay return Natural is
+         Result : Natural := 100; -- DP spec: 100us
+      begin
+         if Link.Bandwidth = DP_Bandwidth_5_4 and
+            Link.Receiver_Caps.Aux_RD_Interval /= 0
+         then
+            Result := Natural (Link.Receiver_Caps.Aux_RD_Interval) * 4_000;
+         end if;
+         return Result;
+      end CR_Delay;
+
+      function EQ_Delay return Natural is
+         Result : Natural := 400; -- DP spec: 400us
+      begin
+         if Link.Bandwidth = DP_Bandwidth_5_4 and
+            Link.Receiver_Caps.Aux_RD_Interval /= 0
+         then
+            Result := Natural (Link.Receiver_Caps.Aux_RD_Interval) * 4_000;
+         end if;
+         return Result;
+      end EQ_Delay;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Train_Set.Voltage_Swing := DP_Info.DP_Voltage_Swing'First;
+      Train_Set.Pre_Emph      := DP_Info.DP_Pre_Emph'First;
+
+      Set_Pattern (Port, Link, DP_Info.TP_1);
+      Set_Signal_Levels (Port, Link, Train_Set);
+
+      pragma Warnings
+        (GNATprove, Off, """Success"" modified by call, but value overwritten*",
+         Reason => "Read first, then overwritten, looks like a false positive");
+      Sink_Init (DP, Link, Success);
+      pragma Warnings
+        (GNATprove, On, """Success"" modified by call, but value overwritten*");
+      if Success then
+         Sink_Set_Training_Pattern (DP, Link, DP_Info.TP_1, Success);
+      end if;
+
+      if Success then
+         Retries := 0;
+         for Tries in 1 .. 32 loop
+            pragma Loop_Invariant (Retries <= Max_Retry);
+
+            Time.U_Delay (CR_Delay);
+
+            Last_Train_Set := Train_Set;
+            Sink_Adjust_Training
+              (Port, DP, Link, Train_Set, CR_Done, EQ_Done, Success);
+            exit when CR_Done or not Success;
+
+            if Train_Set.Voltage_Swing = Last_Train_Set.Voltage_Swing then
+               exit when Retries = Max_Retry;
+               Retries := Retries + 1;
+            else
+               exit when Last_Train_Set.Voltage_Swing = Max_V_Swing (Port);
+               Retries := 0;
+            end if;
+
+            Set_Signal_Levels (Port, Link, Train_Set);
+            Sink_Set_Signal_Levels (Port, DP, Link, Train_Set, Success);
+            exit when not Success;
+         end loop;
+      end if;
+
+      Success := Success and CR_Done;
+
+      if Success then
+         Set_Pattern (Port, Link, EQ_Pattern);
+         Sink_Set_Training_Pattern (DP, Link, EQ_Pattern, Success);
+      end if;
+
+      if Success then
+         Retries := 0;
+         for Tries in 1 .. 32 loop
+            pragma Loop_Invariant (Retries <= Max_Retry);
+
+            Time.U_Delay (EQ_Delay);
+
+            Last_Train_Set := Train_Set;
+            Sink_Adjust_Training
+              (Port, DP, Link, Train_Set, CR_Done, EQ_Done, Success);
+            exit when EQ_Done or not Success;
+
+            if Train_Set.Pre_Emph = Last_Train_Set.Pre_Emph then
+               exit when Retries = Max_Retry;
+               Retries := Retries + 1;
+            else
+               exit when Last_Train_Set.Pre_Emph =
+                  Max_Pre_Emph (Port, Last_Train_Set);
+               Retries := 0;
+            end if;
+
+            Set_Signal_Levels (Port, Link, Train_Set);
+            Sink_Set_Signal_Levels (Port, DP, Link, Train_Set, Success);
+            exit when not Success;
+         end loop;
+      end if;
+
+      if Success then
+         if EQ_Done then
+            -- Set_Pattern (TP_None) includes sending the Idle Pattern,
+            -- so tell sink first.
+            Sink_Set_Training_Pattern
+              (DP, Link, DP_Info.TP_None, Success);
+         else
+            Success := False;
+         end if;
+      end if;
+
+      if Success then
+         Set_Pattern (Port, Link, DP_Info.TP_None);
+      else
+         Off (Port);
+      end if;
+   end Train_DP;
+
+end HW.GFX.DP_Training;
diff --git a/common/hw-gfx-dp_training.ads b/common/hw-gfx-dp_training.ads
new file mode 100644
index 0000000..188b9c6
--- /dev/null
+++ b/common/hw-gfx-dp_training.ads
@@ -0,0 +1,57 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.DP_Info;
+with HW.GFX.DP_Aux_Ch;
+
+private generic
+
+   TPS3_Supported : Boolean;
+
+   type T (<>) is limited private;
+   type Aux_T (<>) is limited private;
+
+   with package Aux_Ch is new DP_Aux_Ch (T => Aux_T, others => <>);
+
+   with package DP_Info is new GFX.DP_Info (T => Aux_T, Aux_Ch => Aux_Ch);
+
+   with function To_Aux (Port : T) return Aux_T;
+
+   with function Max_V_Swing (Port : T) return DP_Info.DP_Voltage_Swing;
+
+   with function Max_Pre_Emph
+     (Port        : T;
+      Train_Set   : DP_Info.Train_Set)
+      return DP_Info.DP_Pre_Emph;
+
+   with procedure Set_Pattern
+     (Port        : T;
+      Link        : DP_Link;
+      Pattern     : DP_Info.Training_Pattern);
+
+   with procedure Set_Signal_Levels
+     (Port        : T;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set);
+
+   with procedure Off (Connector : T);
+
+package HW.GFX.DP_Training
+is
+
+   procedure Train_DP
+     (Port     : in     T;
+      Link     : in     DP_Link;
+      Success  :    out Boolean);
+
+end HW.GFX.DP_Training;
diff --git a/common/hw-gfx-edid.adb b/common/hw-gfx-edid.adb
new file mode 100644
index 0000000..da60d54
--- /dev/null
+++ b/common/hw-gfx-edid.adb
@@ -0,0 +1,180 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+use type HW.Byte;
+use type HW.Pos16;
+use type HW.Word16;
+
+package body HW.GFX.EDID is
+
+   function Checksum_Valid (Raw_EDID : Raw_EDID_Data) return Boolean
+   with
+      Pre => True
+   is
+      Sum : Byte := 16#00#;
+   begin
+      for I in Raw_EDID_Index loop
+         Sum := Sum + Raw_EDID (I);
+      end loop;
+      pragma Debug (Sum /= 16#00#, Debug.Put_Line
+        (GNAT.Source_Info.Enclosing_Entity & ": EDID checksum invalid!"));
+      return Sum = 16#00#;
+   end Checksum_Valid;
+
+   function Valid (Raw_EDID : Raw_EDID_Data) return Boolean
+   is
+      Header_Valid : Boolean;
+   begin
+      Header_Valid :=
+         Raw_EDID (0) = 16#00# and
+         Raw_EDID (1) = 16#ff# and
+         Raw_EDID (2) = 16#ff# and
+         Raw_EDID (3) = 16#ff# and
+         Raw_EDID (4) = 16#ff# and
+         Raw_EDID (5) = 16#ff# and
+         Raw_EDID (6) = 16#ff# and
+         Raw_EDID (7) = 16#00#;
+      pragma Debug (not Header_Valid, Debug.Put_Line
+        (GNAT.Source_Info.Enclosing_Entity & ": EDID header pattern mismatch!"));
+
+      return Header_Valid and then Checksum_Valid (Raw_EDID);
+   end Valid;
+
+   ----------------------------------------------------------------------------
+
+   REVISION                            : constant :=         19;
+   INPUT                               : constant :=         20;
+   INPUT_DIGITAL                       : constant := 1 * 2 ** 7;
+   INPUT_DIGITAL_DEPTH_SHIFT           : constant :=          4;
+   INPUT_DIGITAL_DEPTH_MASK            : constant := 7 * 2 ** 4;
+   INPUT_DIGITAL_DEPTH_UNDEF           : constant := 0 * 2 ** 4;
+   INPUT_DIGITAL_DEPTH_RESERVED        : constant := 7 * 2 ** 4;
+
+   ----------------------------------------------------------------------------
+
+   function Read_LE16
+     (Raw_EDID : Raw_EDID_Data;
+      Offset   : Raw_EDID_Index)
+      return Word16
+   is
+   begin
+      return Shift_Left (Word16 (Raw_EDID (Offset + 1)), 8) or
+             Word16 (Raw_EDID (Offset));
+   end Read_LE16;
+
+   function Has_Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Boolean
+   is
+   begin
+      return
+         Int64 (Read_LE16 (Raw_EDID, DESCRIPTOR_1)) * 10_000
+            in Frequency_Type and
+         ( Raw_EDID (DESCRIPTOR_1 +  2) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 +  4) and 16#f0#) /= 0) and
+         ( Raw_EDID (DESCRIPTOR_1 +  8) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 + 11) and 16#c0#) /= 0) and
+         ( Raw_EDID (DESCRIPTOR_1 +  9) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 + 11) and 16#30#) /= 0) and
+         ( Raw_EDID (DESCRIPTOR_1 +  3) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 +  4) and 16#0f#) /= 0) and
+         ( Raw_EDID (DESCRIPTOR_1 +  5) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 +  7) and 16#f0#) /= 0) and
+         ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#f0#) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 + 11) and 16#0c#) /= 0) and
+         ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#0f#) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 + 11) and 16#03#) /= 0) and
+         ( Raw_EDID (DESCRIPTOR_1 +  6) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 +  7) and 16#0f#) /= 0);
+   end Has_Preferred_Mode;
+
+   function Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Mode_Type
+   is
+      Base : constant := DESCRIPTOR_1;
+      Mode : Mode_Type;
+
+      function Read_12
+        (Lower_8, Upper_4  : Raw_EDID_Index;
+         Shift             : Natural)
+         return Word16
+      is
+      begin
+         return
+            Word16 (Raw_EDID (Lower_8)) or
+            (Shift_Left (Word16 (Raw_EDID (Upper_4)), Shift) and 16#0f00#);
+      end Read_12;
+
+      function Read_10
+        (Lower_8, Upper_2  : Raw_EDID_Index;
+         Shift             : Natural)
+         return Word16
+      is
+      begin
+         return
+            Word16 (Raw_EDID (Lower_8)) or
+            (Shift_Left (Word16 (Raw_EDID (Upper_2)), Shift) and 16#0300#);
+      end Read_10;
+
+      function Read_6
+        (Lower_4     : Raw_EDID_Index;
+         Lower_Shift : Natural;
+         Upper_2     : Raw_EDID_Index;
+         Upper_Shift : Natural)
+         return Word8
+      is
+      begin
+         return
+            (Shift_Right (Word8 (Raw_EDID (Lower_4)), Lower_Shift) and 16#0f#)
+            or
+            (Shift_Left (Word8 (Raw_EDID (Upper_2)), Upper_Shift) and 16#30#);
+      end Read_6;
+   begin
+      Mode := Mode_Type'
+        (Dotclock             => Pos64 (Read_LE16 (Raw_EDID, Base)) * 10_000,
+         H_Visible            => Pos16 (Read_12 (Base +  2, Base +  4, 4)),
+         H_Sync_Begin         => Pos16 (Read_10 (Base +  8, Base + 11, 2)),
+         H_Sync_End           => Pos16 (Read_10 (Base +  9, Base + 11, 4)),
+         H_Total              => Pos16 (Read_12 (Base +  3, Base +  4, 8)),
+         V_Visible            => Pos16 (Read_12 (Base +  5, Base +  7, 4)),
+         V_Sync_Begin         => Pos16 (Read_6  (Base + 10, 4, Base + 11, 2)),
+         V_Sync_End           => Pos16 (Read_6  (Base + 10, 0, Base + 11, 4)),
+         V_Total              => Pos16 (Read_12 (Base +  6, Base +  7, 8)),
+         H_Sync_Active_High   => (Raw_EDID (Base + 17) and 16#02#) /= 0,
+         V_Sync_Active_High   => (Raw_EDID (Base + 17) and 16#04#) /= 0,
+         BPC                  =>
+           (if Raw_EDID (REVISION) < 4 or
+               (Raw_EDID (INPUT) and INPUT_DIGITAL) = 16#00# or
+               (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK) = INPUT_DIGITAL_DEPTH_UNDEF or
+               (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK) = INPUT_DIGITAL_DEPTH_RESERVED
+            then
+               0
+            else
+               4 + 2 * Pos64 (Shift_Right
+                 (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK,
+                  INPUT_DIGITAL_DEPTH_SHIFT))));
+
+      -- Calculate absolute values from EDID relative values.
+      Mode.H_Sync_Begin := Mode.H_Visible    + Mode.H_Sync_Begin;
+      Mode.H_Sync_End   := Mode.H_Sync_Begin + Mode.H_Sync_End;
+      Mode.H_Total      := Mode.H_Visible    + Mode.H_Total;
+      Mode.V_Sync_Begin := Mode.V_Visible    + Mode.V_Sync_Begin;
+      Mode.V_Sync_End   := Mode.V_Sync_Begin + Mode.V_Sync_End;
+      Mode.V_Total      := Mode.V_Visible    + Mode.V_Total;
+
+      return Mode;
+   end Preferred_Mode;
+
+end HW.GFX.EDID;
diff --git a/common/hw-gfx-edid.ads b/common/hw-gfx-edid.ads
new file mode 100644
index 0000000..28dc2db
--- /dev/null
+++ b/common/hw-gfx-edid.ads
@@ -0,0 +1,79 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.EDID
+is
+
+   use type Word8;
+   use type Word16;
+
+   subtype Raw_EDID_Index is Natural range 0 .. 127;
+   subtype Raw_EDID_Data is Buffer (Raw_EDID_Index);
+
+   function Valid (Raw_EDID : Raw_EDID_Data) return Boolean;
+
+   DESCRIPTOR_1 : constant := 54;
+
+   function Read_LE16
+     (Raw_EDID : Raw_EDID_Data;
+      Offset   : Raw_EDID_Index)
+      return Word16
+   with
+      Pre => Offset < Raw_EDID_Index'Last;
+
+   function Has_Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Boolean
+   with
+      Pre => Valid (Raw_EDID),
+      Post =>
+        (Has_Preferred_Mode'Result =
+           (Int64 (Read_LE16 (Raw_EDID, DESCRIPTOR_1)) * 10_000
+               in Frequency_Type and
+            ( Raw_EDID (DESCRIPTOR_1 +  2) /= 0 or
+             (Raw_EDID (DESCRIPTOR_1 +  4) and 16#f0#) /= 0) and
+            ( Raw_EDID (DESCRIPTOR_1 +  8) /= 0 or
+             (Raw_EDID (DESCRIPTOR_1 + 11) and 16#c0#) /= 0) and
+            ( Raw_EDID (DESCRIPTOR_1 +  9) /= 0 or
+             (Raw_EDID (DESCRIPTOR_1 + 11) and 16#30#) /= 0) and
+            ( Raw_EDID (DESCRIPTOR_1 +  3) /= 0 or
+             (Raw_EDID (DESCRIPTOR_1 +  4) and 16#0f#) /= 0) and
+            ( Raw_EDID (DESCRIPTOR_1 +  5) /= 0 or
+             (Raw_EDID (DESCRIPTOR_1 +  7) and 16#f0#) /= 0) and
+            ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#f0#) /= 0 or
+             (Raw_EDID (DESCRIPTOR_1 + 11) and 16#0c#) /= 0) and
+            ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#0f#) /= 0 or
+             (Raw_EDID (DESCRIPTOR_1 + 11) and 16#03#) /= 0) and
+            ( Raw_EDID (DESCRIPTOR_1 +  6) /= 0 or
+             (Raw_EDID (DESCRIPTOR_1 +  7) and 16#0f#) /= 0)));
+   function Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Mode_Type
+   with
+      Pre =>
+         Int64 (Read_LE16 (Raw_EDID, DESCRIPTOR_1)) * 10_000
+            in Frequency_Type and
+         ( Raw_EDID (DESCRIPTOR_1 +  2) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 +  4) and 16#f0#) /= 0) and
+         ( Raw_EDID (DESCRIPTOR_1 +  8) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 + 11) and 16#c0#) /= 0) and
+         ( Raw_EDID (DESCRIPTOR_1 +  9) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 + 11) and 16#30#) /= 0) and
+         ( Raw_EDID (DESCRIPTOR_1 +  3) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 +  4) and 16#0f#) /= 0) and
+         ( Raw_EDID (DESCRIPTOR_1 +  5) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 +  7) and 16#f0#) /= 0) and
+         ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#f0#) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 + 11) and 16#0c#) /= 0) and
+         ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#0f#) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 + 11) and 16#03#) /= 0) and
+         ( Raw_EDID (DESCRIPTOR_1 +  6) /= 0 or
+          (Raw_EDID (DESCRIPTOR_1 +  7) and 16#0f#) /= 0);
+
+end HW.GFX.EDID;
diff --git a/common/hw-gfx-framebuffer_filler.adb b/common/hw-gfx-framebuffer_filler.adb
new file mode 100644
index 0000000..8c1ffdf
--- /dev/null
+++ b/common/hw-gfx-framebuffer_filler.adb
@@ -0,0 +1,40 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.MMIO_Range;
+pragma Elaborate_All (HW.MMIO_Range);
+
+package body HW.GFX.Framebuffer_Filler
+is
+
+   type FB_Index is new Natural range
+      0 .. Natural (Width_Type'Last * Height_Type'Last) - 1;
+   type FB_Range is array (FB_Index) of Word32 with Pack;
+   package FB is new MMIO_Range (0, Word32, FB_Index, FB_Range);
+
+   procedure Fill (Linear_FB : Word64; Framebuffer : Framebuffer_Type)
+   is
+      Line_Start : Int32 := 0;
+   begin
+      FB.Set_Base_Address (Linear_FB);
+      for Line in 0 .. Framebuffer.Height - 1 loop
+         pragma Loop_Invariant (Line_Start = Line * Framebuffer.Stride);
+         for Col in 0 .. Framebuffer.Width - 1 loop
+            pragma Loop_Invariant (Line_Start = Line * Framebuffer.Stride);
+            FB.Write (FB_Index (Line_Start + Col), 16#ff000000#);
+         end loop;
+         Line_Start := Line_Start + Framebuffer.Stride;
+      end loop;
+   end Fill;
+
+end HW.GFX.Framebuffer_Filler;
diff --git a/common/hw-gfx-framebuffer_filler.ads b/common/hw-gfx-framebuffer_filler.ads
new file mode 100644
index 0000000..b728a39
--- /dev/null
+++ b/common/hw-gfx-framebuffer_filler.ads
@@ -0,0 +1,28 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with System;
+
+with HW;
+
+use type HW.Int32;
+
+package HW.GFX.Framebuffer_Filler
+is
+
+   procedure Fill (Linear_FB : Word64; Framebuffer : Framebuffer_Type)
+   with
+      Pre =>
+         Framebuffer.Width <= Framebuffer.Stride;
+
+end HW.GFX.Framebuffer_Filler;
diff --git a/common/hw-gfx-gma-config.ads.template b/common/hw-gfx-gma-config.ads.template
new file mode 100644
index 0000000..00418cd
--- /dev/null
+++ b/common/hw-gfx-gma-config.ads.template
@@ -0,0 +1,205 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Config
+with
+   Initializes => Valid_Port_GPU
+is
+
+   CPU : constant CPU_Type := <<CPU>>;
+
+   CPU_Var : constant CPU_Variant := <<CPU_VARIANT>>;
+
+   Internal_Display : constant Internal_Type := <<INTERNAL_PORT>>;
+
+   EDP_Low_Voltage_Swing : constant Boolean := False;
+
+   Default_MMIO_Base : constant := <<DEFAULT_MMIO_BASE>>;
+
+   LVDS_Dual_Threshold : constant := 95_000_000;
+
+   ----------------------------------------------------------------------------
+
+   Has_Internal_Display    : constant Boolean := Internal_Display /= None;
+   Internal_Is_EDP         : constant Boolean := Internal_Display = DP;
+
+   ----- CPU pipe: --------
+   Disable_Trickle_Feed    : constant Boolean := not
+                                                (CPU in Haswell .. Broadwell);
+   Pipe_Enabled_Workaround : constant Boolean := CPU = Broadwell;
+   Has_EDP_Pipe            : constant Boolean := CPU >= Haswell;
+   Has_Pipe_DDI_Func       : constant Boolean := CPU >= Haswell;
+   Has_Trans_Clk_Sel       : constant Boolean := CPU >= Haswell;
+   Has_Pipe_MSA_Misc       : constant Boolean := CPU >= Haswell;
+   Has_Pipeconf_Misc       : constant Boolean := CPU >= Broadwell;
+   Has_Pipeconf_BPC        : constant Boolean := CPU /= Haswell;
+   Has_Plane_Control       : constant Boolean := CPU >= Skylake;
+   Has_DSP_Linoff          : constant Boolean := CPU <= Ivybridge;
+
+   ----- Panel power: -----
+   Has_PP_Write_Protection : constant Boolean := CPU <= Ivybridge;
+   Has_PP_Port_Select      : constant Boolean := CPU <= Ivybridge;
+   Use_PP_VDD_Override     : constant Boolean := CPU <= Ivybridge;
+
+   ----- PCH/FDI: ---------
+   Has_PCH_DAC             : constant Boolean := CPU in Ironlake .. Ivybridge or
+                                                 (CPU in Broadwell .. Haswell
+                                                  and CPU_Var = Normal);
+
+   Has_PCH_Aux_Channels    : constant Boolean := CPU in Ironlake .. Broadwell;
+
+   VGA_Has_Sync_Disable    : constant Boolean := CPU <= Ivybridge;
+
+   Has_Trans_Timing_Ovrrde : constant Boolean := CPU >= Sandybridge;
+
+   Has_DPLL_SEL            : constant Boolean := CPU in Ironlake .. Ivybridge;
+   Has_FDI_BPC             : constant Boolean := CPU in Ironlake .. Ivybridge;
+   Has_FDI_Composite_Sel   : constant Boolean := CPU = Ivybridge;
+   Has_Trans_DP_Ctl        : constant Boolean := CPU in
+                                                 Sandybridge .. Ivybridge;
+   Has_FDI_C               : constant Boolean := CPU = Ivybridge;
+
+   Has_FDI_RX_Power_Down   : constant Boolean := CPU in Haswell .. Broadwell;
+
+   ----- DDI: -------------
+   End_EDP_Training_Late   : constant Boolean := CPU in Haswell .. Broadwell;
+   Has_Per_DDI_Clock_Sel   : constant Boolean := CPU in Haswell .. Broadwell;
+   Has_HOTPLUG_CTL         : constant Boolean := CPU in Haswell .. Broadwell;
+   Has_SHOTPLUG_CTL_A      : constant Boolean := (CPU in Haswell .. Broadwell
+                                                  and CPU_Var = ULT) or
+                                                 CPU >= Skylake;
+
+   Has_DDI_D               : constant Boolean := (CPU in Haswell .. Broadwell
+                                                  and CPU_Var = Normal)
+                                                 or CPU >= Skylake;
+
+   Has_Low_Voltage_Swing   : constant Boolean := CPU >= Skylake;
+
+   Need_DP_Aux_Mutex       : constant Boolean := False; -- Skylake & (PSR | GTC)
+
+   Ungate_GMBUS_Unit_Level : constant Boolean := CPU >= Skylake;
+
+   ----- Power: -----------
+   Has_IPS                 : constant Boolean := (CPU = Haswell and
+                                                  CPU_Var = ULT) or
+                                                 CPU = Broadwell;
+   Has_IPS_CTL_Mailbox     : constant Boolean := CPU = Broadwell;
+
+   Has_Per_Pipe_SRD        : constant Boolean := CPU >= Broadwell;
+
+   ----- GTT:         -----
+   Fold_39Bit_GTT_PTE      : constant Boolean := CPU <= Haswell;
+
+   ----------------------------------------------------------------------------
+
+   type Supported_Pipe_Array is array (Config_Index) of Boolean;
+   Supported_Pipe : constant Supported_Pipe_Array :=
+     (Primary     => True,
+      Secondary   => True,
+      Tertiary    => CPU >= Ivybridge);
+
+   type Valid_Per_Port is array (Port_Type) of Boolean;
+   type Valid_Per_GPU is array (CPU_Type) of Valid_Per_Port;
+   Valid_Port_GPU : Valid_Per_GPU :=
+     (Ironlake    => Valid_Per_Port'
+        (Disabled => False,
+         Internal => Config.Internal_Display = LVDS,
+         others   => True),
+      Sandybridge => Valid_Per_Port'
+        (Disabled => False,
+         Internal => Config.Internal_Display = LVDS,
+         others   => True),
+      Ivybridge   => Valid_Per_Port'
+        (Disabled => False,
+         Internal => Config.Internal_Display /= None,
+         others   => True),
+      Haswell     => Valid_Per_Port'
+        (Disabled => False,
+         Internal => Config.Internal_Display = DP,
+         Digital3 => CPU_Var = Normal,
+         DP3      => CPU_Var = Normal,
+         Analog   => CPU_Var = Normal,
+         others   => True),
+      Broadwell   => Valid_Per_Port'
+        (Disabled => False,
+         Internal => Config.Internal_Display = DP,
+         Digital3 => CPU_Var = Normal,
+         DP3      => CPU_Var = Normal,
+         Analog   => CPU_Var = Normal,
+         others   => True),
+      Skylake     => Valid_Per_Port'
+        (Disabled => False,
+         Internal => Config.Internal_Display = DP,
+         Analog   => False,
+         others   => True))
+   with
+      Part_Of => GMA.Config_State;
+   Valid_Port : Valid_Per_Port renames Valid_Port_GPU (CPU);
+
+   ----------------------------------------------------------------------------
+
+   type FDI_Per_Port is array (GPU_Port) of Boolean;
+   type FDI_Per_GPU is array (CPU_Type) of FDI_Per_Port;
+   FDI_GPU : constant FDI_Per_GPU :=
+     (Ironlake => FDI_Per_Port'
+        (DIGI_A   => False,            -- directly connected eDP
+         DIGI_B   => True,
+         DIGI_C   => True,
+         DIGI_D   => True,
+         others   => False),
+      Sandybridge => FDI_Per_Port'
+        (DIGI_A   => False,            -- directly connected eDP
+         DIGI_B   => True,
+         DIGI_C   => True,
+         DIGI_D   => True,
+         others   => False),
+      Ivybridge => FDI_Per_Port'
+        (DIGI_A   => False,            -- directly connected eDP
+         DIGI_B   => True,
+         DIGI_C   => True,
+         DIGI_D   => True,
+         others   => False),
+      Haswell => FDI_Per_Port'
+        (DIGI_A   => False,
+         DIGI_B   => False,
+         DIGI_C   => False,
+         DIGI_D   => False,
+         DIGI_E   => True,             -- VGA option through FDI
+         others   => False),
+      Broadwell => FDI_Per_Port'
+        (DIGI_A   => False,
+         DIGI_B   => False,
+         DIGI_C   => False,
+         DIGI_D   => False,
+         DIGI_E   => CPU_Var = Normal, -- VGA option through FDI
+         others   => False),
+      Skylake => FDI_Per_Port'
+        (others   => False));
+   FDI_Port : FDI_Per_Port renames FDI_GPU (CPU);
+
+   type FDI_Lanes_Per_Port is array (GPU_Port) of DP_Lane_Count;
+   FDI_Lane_Count : constant FDI_Lanes_Per_Port :=
+     (DIGI_D => DP_Lane_Count_2,
+      others =>
+        (if CPU in Ironlake .. Ivybridge then
+            DP_Lane_Count_4
+         else
+            DP_Lane_Count_2));
+
+   FDI_Training : constant FDI_Training_Type :=
+     (case CPU is
+         when Ironlake     => Simple_Training,
+         when Sandybridge  => Full_Training,
+         when others       => Auto_Training);
+
+end HW.GFX.GMA.Config;
diff --git a/common/hw-gfx-gma-connector_info.adb b/common/hw-gfx-gma-connector_info.adb
new file mode 100644
index 0000000..bec59fa
--- /dev/null
+++ b/common/hw-gfx-gma-connector_info.adb
@@ -0,0 +1,136 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.I2C;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.I2C;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.DP_Aux_Ch;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connector_Info is
+
+   function To_DP (Port_Cfg : Port_Config) return DP_Port
+   is
+   begin
+      return
+        (if Port_Cfg.Port = DIGI_A then
+            DP_A
+         else
+           (case Port_Cfg.PCH_Port is
+               when PCH_DP_B  => DP_B,
+               when PCH_DP_C  => DP_C,
+               when PCH_DP_D  => DP_D,
+               when others    => DP_Port'First));
+   end To_DP;
+
+   ----------------------------------------------------------------------------
+
+   procedure Read_EDID
+     (Raw_EDID    :    out EDID.Raw_EDID_Data;
+      Port_Cfg    : in     Port_Config;
+      Success     :    out Boolean)
+   is
+      Raw_EDID_Length : GFX.I2C.Transfer_Length := Raw_EDID'Length;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      for I in 1 .. 2 loop
+         if Port_Cfg.Display = DP then
+            DP_Aux_Ch.I2C_Read
+              (Port     => To_DP (Port_Cfg),
+               Address  => 16#50#,
+               Length   => Raw_EDID_Length,
+               Data     => Raw_EDID,
+               Success  => Success);
+         else
+            I2C.I2C_Read
+              (Port     => Port_Cfg.PCH_Port,
+               Address  => 16#50#,
+               Length   => Raw_EDID_Length,
+               Data     => Raw_EDID,
+               Success  => Success);
+         end if;
+         exit when not Success;  -- don't retry if reading itself failed
+
+         pragma Debug (Debug.Put_Buffer ("EDID", Raw_EDID, Raw_EDID_Length));
+         Success := EDID.Valid (Raw_EDID);
+         exit when Success;
+      end loop;
+   end Read_EDID;
+
+   ----------------------------------------------------------------------------
+
+   procedure Preferred_Link_Setting
+     (Port_Cfg    : in out Port_Config;
+      Success     :    out Boolean) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Display = DP then
+         if Port_Cfg.Port = DIGI_A then
+            if GMA.Config.Use_PP_VDD_Override then
+               Panel.VDD_Override;
+            else
+               Panel.On;
+            end if;
+         end if;
+
+         DP_Info.Read_Caps
+           (Link     => Port_Cfg.DP,
+            Port     => To_DP (Port_Cfg),
+            Success  => Success);
+         if Success then
+            DP_Info.Preferred_Link_Setting
+              (Link     => Port_Cfg.DP,
+               Mode     => Port_Cfg.Mode,
+               Success  => Success);
+         end if;
+      else
+         Success := True;
+      end if;
+   end Preferred_Link_Setting;
+
+   procedure Next_Link_Setting
+     (Port_Cfg : in out Port_Config;
+      Success  :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Display = DP then
+         DP_Info.Next_Link_Setting
+           (Link     => Port_Cfg.DP,
+            Mode     => Port_Cfg.Mode,
+            Success  => Success);
+      else
+         Success := False;
+      end if;
+   end Next_Link_Setting;
+
+   ----------------------------------------------------------------------------
+
+   function Default_BPC (Port_Cfg : Port_Config) return HW.GFX.BPC_Type
+   is
+   begin
+      return
+        (if Port_Cfg.Port = DIGI_A or
+            (Port_Cfg.Is_FDI and Port_Cfg.PCH_Port = PCH_LVDS)
+         then 6
+         else 8);
+   end Default_BPC;
+
+end HW.GFX.GMA.Connector_Info;
diff --git a/common/hw-gfx-gma-connector_info.ads b/common/hw-gfx-gma-connector_info.ads
new file mode 100644
index 0000000..a02f7c0
--- /dev/null
+++ b/common/hw-gfx-gma-connector_info.ads
@@ -0,0 +1,39 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.EDID;
+
+private package HW.GFX.GMA.Connector_Info is
+
+   procedure Read_EDID
+     (Raw_EDID :    out EDID.Raw_EDID_Data;
+      Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   with
+      Post => (if Success then EDID.Valid (Raw_EDID));
+
+   procedure Preferred_Link_Setting
+     (Port_Cfg : in out Port_Config;
+      Success  :    out Boolean)
+      with
+         Post => (Port_Cfg.Port = Port_Cfg.Port'Old);
+
+   procedure Next_Link_Setting
+     (Port_Cfg : in out Port_Config;
+      Success  :    out Boolean)
+      with
+         Post => (Port_Cfg.Port = Port_Cfg.Port'Old);
+
+   function Default_BPC (Port_Cfg : Port_Config) return BPC_Type;
+
+end HW.GFX.GMA.Connector_Info;
diff --git a/common/hw-gfx-gma-connectors.ads b/common/hw-gfx-gma-connectors.ads
new file mode 100644
index 0000000..91c2801
--- /dev/null
+++ b/common/hw-gfx-gma-connectors.ads
@@ -0,0 +1,36 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Connectors is
+
+   pragma Warnings (GNATprove, Off, "unused variable ""P*""",
+                    Reason => "Needed for a common interface");
+   procedure Pre_On
+     (Port_Cfg    : in     Port_Config;
+      PLL_Hint    : in     Word32;
+      Pipe_Hint   : in     Word32;
+      Success     :    out Boolean);
+
+   procedure Post_On
+     (Port_Cfg : in     Port_Config;
+      PLL_Hint : in     Word32;
+      Success  :    out Boolean);
+   pragma Warnings (GNATprove, On, "unused variable ""P*""");
+
+   procedure Pre_Off (Port_Cfg : Port_Config);
+   procedure Post_Off (Port_Cfg : Port_Config);
+
+   procedure Pre_All_Off;
+   procedure Post_All_Off;
+
+end HW.GFX.GMA.Connectors;
diff --git a/common/hw-gfx-gma-dp_aux_ch.ads b/common/hw-gfx-gma-dp_aux_ch.ads
new file mode 100644
index 0000000..94ef345
--- /dev/null
+++ b/common/hw-gfx-gma-dp_aux_ch.ads
@@ -0,0 +1,21 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.DP_Aux_Ch;
+pragma Elaborate_All (HW.GFX.DP_Aux_Ch);
+with HW.GFX.GMA.DP_Aux_Request;
+
+private package HW.GFX.GMA.DP_Aux_Ch
+   is new HW.GFX.DP_Aux_Ch
+     (T           => DP_Port,
+      Aux_Request => DP_Aux_Request.Do_Aux_Request);
diff --git a/common/hw-gfx-gma-dp_aux_request.adb b/common/hw-gfx-gma-dp_aux_request.adb
new file mode 100644
index 0000000..df4e048
--- /dev/null
+++ b/common/hw-gfx-gma-dp_aux_request.adb
@@ -0,0 +1,330 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+use type HW.Word8;
+use type HW.GFX.GMA.Registers.Registers_Invalid_Index;
+
+package body HW.GFX.GMA.DP_Aux_Request is
+
+   DP_AUX_CTL_SEND_BUSY             : constant :=    1 * 2 ** 31;
+   DP_AUX_CTL_DONE                  : constant :=    1 * 2 ** 30;
+   DP_AUX_CTL_INTERRUPT_ON_DONE     : constant :=    1 * 2 ** 29;
+   DP_AUX_CTL_TIME_OUT_ERROR        : constant :=    1 * 2 ** 28;
+   DP_AUX_CTL_TIME_OUT_TIMER_MASK   : constant :=    3 * 2 ** 26;
+   DP_AUX_CTL_TIME_OUT_TIMER_400US  : constant :=    0 * 2 ** 26;
+   DP_AUX_CTL_TIME_OUT_TIMER_600US  : constant :=    1 * 2 ** 26;
+   DP_AUX_CTL_TIME_OUT_TIMER_800US  : constant :=    2 * 2 ** 26;
+   DP_AUX_CTL_TIME_OUT_TIMER_1600US : constant :=    3 * 2 ** 26;
+   DP_AUX_CTL_RECEIVE_ERROR         : constant :=    1 * 2 ** 25;
+   DP_AUX_CTL_MESSAGE_SIZE_MASK     : constant :=   31 * 2 ** 20;
+   DP_AUX_CTL_MESSAGE_SIZE_SHIFT    : constant :=        2 ** 20;
+   DP_AUX_CTL_PRECHARGE_TIME_MASK   : constant :=   15 * 2 ** 16;
+   DP_AUX_CTL_PRECHARGE_TIME_SHIFT  : constant :=        2 ** 16;
+   DP_AUX_CTL_2X_BIT_CLOCK_DIV_MASK : constant := 2047 * 2 **  0;
+   -- TODO: 2x bit clock divider should be programmed once before any training.
+
+   subtype DP_AUX_CTL_MESSAGE_SIZE_T is Natural range 1 .. 20;
+   function DP_AUX_CTL_MESSAGE_SIZE
+     (Message_Length : DP_AUX_CTL_MESSAGE_SIZE_T)
+      return Word32;
+
+   DDI_AUX_MUTEX_MUTEX_ENABLE : constant := 1 * 2 ** 31;
+   DDI_AUX_MUTEX_MUTEX_STATUS : constant := 1 * 2 ** 30;
+
+   type AUX_CH_Data_Regs is new Positive range 1 .. 5;
+
+   type AUX_CH_Data_Regs_Array is
+      array (AUX_CH_Data_Regs) of Registers.Registers_Index;
+
+   type AUX_CH_Registers is record
+      CTL   : Registers.Registers_Index;
+      DATA  : AUX_CH_Data_Regs_Array;
+      MUTEX : Registers.Registers_Invalid_Index;
+   end record;
+
+   type AUX_CH_Registers_Array is array (DP_Port) of AUX_CH_Registers;
+
+   AUX_CH : constant AUX_CH_Registers_Array :=
+     (if Config.Has_PCH_Aux_Channels then
+         AUX_CH_Registers_Array'
+        (DP_A => AUX_CH_Registers'
+           (CTL   => Registers.DP_AUX_CTL_A,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.DP_AUX_DATA_A_1,
+               2  => Registers.DP_AUX_DATA_A_2,
+               3  => Registers.DP_AUX_DATA_A_3,
+               4  => Registers.DP_AUX_DATA_A_4,
+               5  => Registers.DP_AUX_DATA_A_5),
+            MUTEX => Registers.Invalid_Register),
+         DP_B => AUX_CH_Registers'
+           (CTL   => Registers.PCH_DP_AUX_CTL_B,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.PCH_DP_AUX_DATA_B_1,
+               2  => Registers.PCH_DP_AUX_DATA_B_2,
+               3  => Registers.PCH_DP_AUX_DATA_B_3,
+               4  => Registers.PCH_DP_AUX_DATA_B_4,
+               5  => Registers.PCH_DP_AUX_DATA_B_5),
+            MUTEX => Registers.Invalid_Register),
+         DP_C => AUX_CH_Registers'
+           (CTL   => Registers.PCH_DP_AUX_CTL_C,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.PCH_DP_AUX_DATA_C_1,
+               2  => Registers.PCH_DP_AUX_DATA_C_2,
+               3  => Registers.PCH_DP_AUX_DATA_C_3,
+               4  => Registers.PCH_DP_AUX_DATA_C_4,
+               5  => Registers.PCH_DP_AUX_DATA_C_5),
+            MUTEX => Registers.Invalid_Register),
+         DP_D => AUX_CH_Registers'
+           (CTL   => Registers.PCH_DP_AUX_CTL_D,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.PCH_DP_AUX_DATA_D_1,
+               2  => Registers.PCH_DP_AUX_DATA_D_2,
+               3  => Registers.PCH_DP_AUX_DATA_D_3,
+               4  => Registers.PCH_DP_AUX_DATA_D_4,
+               5  => Registers.PCH_DP_AUX_DATA_D_5),
+            MUTEX => Registers.Invalid_Register))
+      else
+         AUX_CH_Registers_Array'
+        (DP_A => AUX_CH_Registers'
+           (CTL   => Registers.DDI_AUX_CTL_A,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.DDI_AUX_DATA_A_1,
+               2  => Registers.DDI_AUX_DATA_A_2,
+               3  => Registers.DDI_AUX_DATA_A_3,
+               4  => Registers.DDI_AUX_DATA_A_4,
+               5  => Registers.DDI_AUX_DATA_A_5),
+            MUTEX => Registers.DDI_AUX_MUTEX_A),
+         DP_B => AUX_CH_Registers'
+           (CTL   => Registers.DDI_AUX_CTL_B,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.DDI_AUX_DATA_B_1,
+               2  => Registers.DDI_AUX_DATA_B_2,
+               3  => Registers.DDI_AUX_DATA_B_3,
+               4  => Registers.DDI_AUX_DATA_B_4,
+               5  => Registers.DDI_AUX_DATA_B_5),
+            MUTEX => Registers.DDI_AUX_MUTEX_B),
+         DP_C => AUX_CH_Registers'
+           (CTL   => Registers.DDI_AUX_CTL_C,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.DDI_AUX_DATA_C_1,
+               2  => Registers.DDI_AUX_DATA_C_2,
+               3  => Registers.DDI_AUX_DATA_C_3,
+               4  => Registers.DDI_AUX_DATA_C_4,
+               5  => Registers.DDI_AUX_DATA_C_5),
+            MUTEX => Registers.DDI_AUX_MUTEX_C),
+         DP_D => AUX_CH_Registers'
+           (CTL   => Registers.DDI_AUX_CTL_D,
+            DATA  => AUX_CH_Data_Regs_Array'
+              (1  => Registers.DDI_AUX_DATA_D_1,
+               2  => Registers.DDI_AUX_DATA_D_2,
+               3  => Registers.DDI_AUX_DATA_D_3,
+               4  => Registers.DDI_AUX_DATA_D_4,
+               5  => Registers.DDI_AUX_DATA_D_5),
+            MUTEX => Registers.DDI_AUX_MUTEX_D)));
+
+   ----------------------------------------------------------------------------
+
+   function DP_AUX_CTL_MESSAGE_SIZE
+     (Message_Length : DP_AUX_CTL_MESSAGE_SIZE_T)
+      return Word32
+   is
+   begin
+      return Word32 (Message_Length) * DP_AUX_CTL_MESSAGE_SIZE_SHIFT;
+   end DP_AUX_CTL_MESSAGE_SIZE;
+
+   ----------------------------------------------------------------------------
+
+   procedure Aux_Request_Low
+     (Port              : in     DP_Port;
+      Request           : in     DP_Defs.Aux_Request;
+      Request_Length    : in     DP_Defs.Aux_Request_Length;
+      Response          :    out DP_Defs.Aux_Response;
+      Response_Length   :    out DP_Defs.Aux_Response_Length;
+      Success           :    out Boolean)
+   with
+      Global => (In_Out => Registers.Register_State,
+                 Input  => Time.State),
+      Depends =>
+        ((Registers.Register_State,
+          Response,
+          Response_Length,
+          Success)
+             =>
+               (Registers.Register_State,
+                Time.State,
+                Port,
+                Request,
+                Request_Length))
+   is
+      procedure Write_Data_Reg
+        (Register : in     Registers.Registers_Index;
+         Buf      : in     DP_Defs.Aux_Request;
+         Length   : in     DP_Defs.Aux_Request_Length;
+         Offset   : in     DP_Defs.Aux_Request_Index)
+      is
+         Value : Word32;
+         Count : Natural;
+      begin
+         if Offset < Length then
+            if Length - Offset > 4 then
+               Count := 4;
+            else
+               Count := Length - Offset;
+            end if;
+
+            Value := 0;
+            for Idx in DP_Defs.Aux_Request_Index range 0 .. Count - 1 loop
+               Value := Value or
+                  Shift_Left (Word32 (Buf (Offset + Idx)), (3 - Idx) * 8);
+            end loop;
+            Registers.Write (Register => Register, Value => Value);
+         end if;
+      end Write_Data_Reg;
+
+      procedure Read_Data_Reg
+        (Register : in     Registers.Registers_Index;
+         Buf      : in out DP_Defs.Aux_Response;
+         Length   : in     DP_Defs.Aux_Response_Length;
+         Offset   : in     DP_Defs.Aux_Response_Index)
+      is
+         Value : Word32;
+         Count : DP_Defs.Aux_Response_Length;
+      begin
+         if Offset < Length then
+            if Length - Offset > 4 then
+               Count := 4;
+            else
+               Count := Length - Offset;
+            end if;
+
+            Registers.Read (Register => Register, Value => Value);
+            for Idx in 0 .. Count - 1 loop
+               Buf (Offset + Idx) :=
+                  Word8 (Shift_Right (Value, (3 - Idx) * 8) and 16#ff#);
+            end loop;
+         end if;
+      end Read_Data_Reg;
+
+      Busy : Boolean;
+      Status : Word32;
+   begin
+      Response := (others => 0); -- Don't care
+      Response_Length := DP_Defs.Aux_Response_Length'First;
+
+      if Config.Need_DP_Aux_Mutex then
+         Registers.Set_Mask
+           (Register => AUX_CH (Port).MUTEX,
+            Mask     => DDI_AUX_MUTEX_MUTEX_ENABLE);
+         Registers.Wait_Set_Mask
+           (Register => AUX_CH (Port).MUTEX,
+            Mask     => DDI_AUX_MUTEX_MUTEX_STATUS);
+      end if;
+
+      Registers.Is_Set_Mask
+        (Register => AUX_CH (Port).CTL,
+         Mask     => DP_AUX_CTL_SEND_BUSY,
+         Result   => Busy);
+      if Busy then
+         Success := False;
+      else
+         for Idx in AUX_CH_Data_Regs loop
+            Write_Data_Reg
+              (Register => AUX_CH (Port).DATA (Idx),
+               Buf      => Request,
+               Length   => Request_Length,
+               Offset   => (Natural (Idx) - 1) * 4);
+         end loop;
+
+         Registers.Unset_And_Set_Mask
+           (Register    => AUX_CH (Port).CTL,
+            Mask_Unset  => DP_AUX_CTL_INTERRUPT_ON_DONE or
+                           DP_AUX_CTL_TIME_OUT_TIMER_MASK or
+                           DP_AUX_CTL_MESSAGE_SIZE_MASK,
+            Mask_Set    => DP_AUX_CTL_SEND_BUSY or          -- starts transfer
+                           DP_AUX_CTL_DONE or               -- clears the status
+                           DP_AUX_CTL_TIME_OUT_ERROR or     -- clears the status
+                           DP_AUX_CTL_RECEIVE_ERROR or      -- clears the status
+                           DP_AUX_CTL_TIME_OUT_TIMER_600US or
+                           DP_AUX_CTL_MESSAGE_SIZE (Request_Length));
+
+         Registers.Wait_Unset_Mask
+           (Register => AUX_CH (Port).CTL,
+            Mask     => DP_AUX_CTL_SEND_BUSY);
+         Registers.Read (Register => AUX_CH (Port).CTL, Value => Status);
+         Success := (Status and
+                     (DP_AUX_CTL_TIME_OUT_ERROR or DP_AUX_CTL_RECEIVE_ERROR))
+                    = 0;
+
+         if Success then
+            Status := (Status and DP_AUX_CTL_MESSAGE_SIZE_MASK)
+                      / DP_AUX_CTL_MESSAGE_SIZE_SHIFT;
+            if Natural (Status) < DP_Defs.Aux_Response_Length'First then
+               Success := False;
+            elsif Natural (Status) > DP_Defs.Aux_Response_Length'Last then
+               Response_Length := DP_Defs.Aux_Response_Length'Last;
+            else
+               Response_Length := Natural (Status);
+            end if;
+         end if;
+
+         if Success then
+            for Idx in AUX_CH_Data_Regs loop
+               Read_Data_Reg
+                 (Register => AUX_CH (Port).DATA (Idx),
+                  Buf      => Response,
+                  Length   => Response_Length,
+                  Offset   => (Natural (Idx) - 1) * 4);
+            end loop;
+         end if;
+      end if;
+
+      if Config.Need_DP_Aux_Mutex then
+         Registers.Unset_And_Set_Mask
+           (Register    => AUX_CH (Port).MUTEX,
+            Mask_Unset  => DDI_AUX_MUTEX_MUTEX_ENABLE,
+            Mask_Set    => DDI_AUX_MUTEX_MUTEX_STATUS);  -- frees the mutex
+      end if;
+   end Aux_Request_Low;
+
+   ----------------------------------------------------------------------------
+
+   procedure Do_Aux_Request
+     (Port              : in     DP_Port;
+      Request           : in     DP_Defs.Aux_Request;
+      Request_Length    : in     DP_Defs.Aux_Request_Length;
+      Response          :    out DP_Defs.Aux_Response;
+      Response_Length   :    out DP_Defs.Aux_Response_Length;
+      Success           :    out Boolean)
+   is
+   begin
+      for Try in Positive range 1 .. 3 loop
+         Aux_Request_Low
+           (Port              => Port,
+            Request           => Request,
+            Request_Length    => Request_Length,
+            Response          => Response,
+            Response_Length   => Response_Length,
+            Success           => Success);
+         exit when Success;
+      end loop;
+   end Do_Aux_Request;
+
+end HW.GFX.GMA.DP_Aux_Request;
diff --git a/common/hw-gfx-gma-dp_aux_request.ads b/common/hw-gfx-gma-dp_aux_request.ads
new file mode 100644
index 0000000..e3f76ab
--- /dev/null
+++ b/common/hw-gfx-gma-dp_aux_request.ads
@@ -0,0 +1,26 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.DP_Defs;
+
+private package HW.GFX.GMA.DP_Aux_Request is
+
+   procedure Do_Aux_Request
+     (Port              : in     DP_Port;
+      Request           : in     DP_Defs.Aux_Request;
+      Request_Length    : in     DP_Defs.Aux_Request_Length;
+      Response          :    out DP_Defs.Aux_Response;
+      Response_Length   :    out DP_Defs.Aux_Response_Length;
+      Success           :    out Boolean);
+
+end HW.GFX.GMA.DP_Aux_Request;
diff --git a/common/hw-gfx-gma-dp_info.ads b/common/hw-gfx-gma-dp_info.ads
new file mode 100644
index 0000000..148599c
--- /dev/null
+++ b/common/hw-gfx-gma-dp_info.ads
@@ -0,0 +1,21 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.DP_Info;
+pragma Elaborate_All (HW.GFX.DP_Info);
+with HW.GFX.GMA.DP_Aux_Ch;
+
+private package HW.GFX.GMA.DP_Info
+   is new HW.GFX.DP_Info
+     (T        => DP_Port,
+      Aux_Ch   => DP_Aux_Ch);
diff --git a/common/hw-gfx-gma-i2c.adb b/common/hw-gfx-gma-i2c.adb
new file mode 100644
index 0000000..ec3afc2
--- /dev/null
+++ b/common/hw-gfx-gma-i2c.adb
@@ -0,0 +1,234 @@
+--
+-- Copyright (C) 2015 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+use type HW.Word8;
+
+package body HW.GFX.GMA.I2C is
+
+   PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL : constant :=   1 * 2 ** 31;
+
+   GMBUS0_GMBUS_RATE_SELECT_MASK    : constant :=   7 * 2 **  8;
+   GMBUS0_GMBUS_RATE_SELECT_100KHZ  : constant :=   0 * 2 **  8;
+   GMBUS0_GMBUS_RATE_SELECT_50KHZ   : constant :=   1 * 2 **  8;
+   GMBUS0_PIN_PAIR_SELECT_MASK      : constant :=   7 * 2 **  0;
+   GMBUS0_PIN_PAIR_SELECT_NONE      : constant :=   0 * 2 **  0;
+   GMBUS0_PIN_PAIR_SELECT_DAC       : constant :=   2 * 2 **  0;
+   GMBUS0_PIN_PAIR_SELECT_LVDS      : constant :=   3 * 2 **  0;
+   -- Order is C, B, D: no typo!
+   GMBUS0_PIN_PAIR_SELECT_DIGI_C    : constant :=   4 * 2 **  0;
+   GMBUS0_PIN_PAIR_SELECT_DIGI_B    : constant :=   5 * 2 **  0;
+   GMBUS0_PIN_PAIR_SELECT_DIGI_D    : constant :=   6 * 2 **  0;
+
+   GMBUS1_SOFTWARE_CLEAR_INTERRUPT  : constant :=   1 * 2 ** 31;
+   GMBUS1_SOFTWARE_READY            : constant :=   1 * 2 ** 30;
+   GMBUS1_ENABLE_TIMEOUT            : constant :=   1 * 2 ** 29;
+   GMBUS1_BUS_CYCLE_SELECT_MASK     : constant :=   7 * 2 ** 25;
+   GMBUS1_BUS_CYCLE_STOP            : constant :=   1 * 2 ** 27;
+   GMBUS1_BUS_CYCLE_INDEX           : constant :=   1 * 2 ** 26;
+   GMBUS1_BUS_CYCLE_WAIT            : constant :=   1 * 2 ** 25;
+   GMBUS1_TOTAL_BYTE_COUNT_MASK     : constant := 511 * 2 ** 16;
+   GMBUS1_TOTAL_BYTE_COUNT_SHIFT    : constant :=            16;
+   GMBUS1_8BIT_SLAVE_INDEX_MASK     : constant := 255 * 2 **  8;
+   GMBUS1_8BIT_SLAVE_INDEX_SHIFT    : constant :=             8;
+   GMBUS1_SLAVE_ADDRESS_MASK        : constant := 127 * 2 **  1;
+   GMBUS1_SLAVE_ADDRESS_SHIFT       : constant :=             1;
+   GMBUS1_DIRECTION_MASK            : constant :=   1 * 2 **  0;
+   GMBUS1_DIRECTION_WRITE           : constant :=   0 * 2 **  0;
+   GMBUS1_DIRECTION_READ            : constant :=   1 * 2 **  0;
+
+   GMBUS2_INUSE                     : constant :=   1 * 2 ** 15;
+   GMBUS2_HARDWARE_WAIT_PHASE       : constant :=   1 * 2 ** 14;
+   GMBUS2_SLAVE_STALL_TIMEOUT_ERROR : constant :=   1 * 2 ** 13;
+   GMBUS2_GMBUS_INTERRUPT_STATUS    : constant :=   1 * 2 ** 12;
+   GMBUS2_HARDWARE_READY            : constant :=   1 * 2 ** 11;
+   GMBUS2_NAK_INDICATOR             : constant :=   1 * 2 ** 10;
+   GMBUS2_GMBUS_ACTIVE              : constant :=   1 * 2 **  9;
+   GMBUS2_CURRENT_BYTE_COUNT_MASK   : constant := 511 * 2 **  0;
+
+   GMBUS4_INTERRUPT_MASK            : constant :=  31 * 2 **  0;
+
+   GMBUS5_2BYTE_INDEX_ENABLE        : constant :=   1 * 2 ** 31;
+
+   function GMBUS1_TOTAL_BYTE_COUNT
+     (Count : HW.GFX.I2C.Transfer_Length)
+      return Word32 is
+   begin
+      return Shift_Left (Word32 (Count), GMBUS1_TOTAL_BYTE_COUNT_SHIFT);
+   end GMBUS1_TOTAL_BYTE_COUNT;
+
+   function GMBUS1_SLAVE_ADDRESS
+     (Address : HW.GFX.I2C.Transfer_Address)
+      return Word32 is
+   begin
+      return Shift_Left (Word32 (Address), GMBUS1_SLAVE_ADDRESS_SHIFT);
+   end GMBUS1_SLAVE_ADDRESS;
+
+   function GMBUS0_PIN_PAIR_SELECT (Port : PCH_Port) return Word32 is
+   begin
+      return
+        (case Port is
+            when PCH_DAC      => GMBUS0_PIN_PAIR_SELECT_DAC,
+            when PCH_LVDS     => GMBUS0_PIN_PAIR_SELECT_LVDS,
+            when PCH_HDMI_B   => GMBUS0_PIN_PAIR_SELECT_DIGI_B,
+            when PCH_HDMI_C   => GMBUS0_PIN_PAIR_SELECT_DIGI_C,
+            when PCH_HDMI_D   => GMBUS0_PIN_PAIR_SELECT_DIGI_D,
+            when others       => GMBUS0_PIN_PAIR_SELECT_NONE);
+   end GMBUS0_PIN_PAIR_SELECT;
+
+   ----------------------------------------------------------------------------
+
+   procedure GMBUS_Ready (Result : out Boolean)
+   is
+      GMBUS2 : Word32;
+   begin
+      Registers.Read (Registers.PCH_GMBUS2, GMBUS2);
+      Result := (GMBUS2 and (GMBUS2_HARDWARE_WAIT_PHASE or
+                              GMBUS2_SLAVE_STALL_TIMEOUT_ERROR or
+                              GMBUS2_GMBUS_INTERRUPT_STATUS or
+                              GMBUS2_NAK_INDICATOR)) = 0;
+   end GMBUS_Ready;
+
+   procedure Reset_GMBUS (Success : out Boolean) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Write (Registers.PCH_GMBUS1, GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
+      Registers.Write (Registers.PCH_GMBUS1, 0);
+      Registers.Write (Registers.PCH_GMBUS0, GMBUS0_PIN_PAIR_SELECT_NONE);
+
+      GMBUS_Ready (Success);
+   end Reset_GMBUS;
+
+   procedure Init_GMBUS (Port : PCH_Port; Success : out Boolean) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.Ungate_GMBUS_Unit_Level then
+         Registers.Set_Mask
+           (Register => Registers.PCH_DSPCLK_GATE_D,
+            Mask     => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
+      end if;
+
+      -- TODO: Refactor + check for timeout.
+      Registers.Wait_Unset_Mask (Registers.PCH_GMBUS2, GMBUS2_INUSE);
+
+      GMBUS_Ready (Success);
+      if not Success then
+         Reset_GMBUS (Success);
+      end if;
+
+      if Success then
+         Registers.Write
+           (Register => Registers.PCH_GMBUS0,
+            Value    => GMBUS0_GMBUS_RATE_SELECT_100KHZ or
+                        GMBUS0_PIN_PAIR_SELECT (Port));
+         Registers.Write
+           (Register => Registers.PCH_GMBUS4,
+            Value    => 0);
+         Registers.Write
+           (Register => Registers.PCH_GMBUS5,
+            Value    => 0);
+      end if;
+   end Init_GMBUS;
+
+   procedure Release_GMBUS
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Write (Registers.PCH_GMBUS0, GMBUS0_PIN_PAIR_SELECT_NONE);
+
+      -- Clear INUSE. TODO: Don't do it, if timeout occured (see above).
+      Registers.Write (Registers.PCH_GMBUS2, GMBUS2_INUSE);
+
+      if Config.Ungate_GMBUS_Unit_Level then
+         Registers.Unset_Mask
+           (Register => Registers.PCH_DSPCLK_GATE_D,
+            Mask     => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
+      end if;
+   end Release_GMBUS;
+
+   procedure I2C_Read
+     (Port     : in     PCH_Port;
+      Address  : in     HW.GFX.I2C.Transfer_Address;
+      Length   : in out HW.GFX.I2C.Transfer_Length;
+      Data     :    out HW.GFX.I2C.Transfer_Data;
+      Success  :    out Boolean)
+   is
+      GMBUS2,
+      GMBUS3 : Word32;
+
+      Current     : HW.GFX.I2C.Transfer_Length;
+      Transfered  : HW.GFX.I2C.Transfer_Length := 0;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Data := (others => 0);
+
+      Init_GMBUS (Port, Success);
+      if Success then
+         Registers.Write
+           (Register => Registers.PCH_GMBUS1,
+            Value    => GMBUS1_SOFTWARE_READY or
+                        GMBUS1_BUS_CYCLE_INDEX or
+                        GMBUS1_BUS_CYCLE_WAIT or
+                        GMBUS1_TOTAL_BYTE_COUNT (Length) or
+                        GMBUS1_SLAVE_ADDRESS (Address) or
+                        GMBUS1_DIRECTION_READ);
+
+         while Success and then Transfered < Length loop
+            Registers.Wait_Set_Mask
+              (Register => Registers.PCH_GMBUS2,
+               Mask     => GMBUS2_HARDWARE_READY,
+               TOut_MS  => 55);
+
+            Registers.Read (Registers.PCH_GMBUS2, GMBUS2);
+            Success :=  (GMBUS2 and GMBUS2_HARDWARE_READY) /= 0 and
+                        (GMBUS2 and GMBUS2_NAK_INDICATOR) = 0;
+            if Success then
+               Current := GFX.I2C.Transfer_Length'Min (Length, Transfered + 4);
+
+               Registers.Read (Registers.PCH_GMBUS3, GMBUS3);
+               for I in Transfered .. Current - 1 loop
+                  Data (I) := Byte (GMBUS3 and 16#ff#);
+                  GMBUS3 := Shift_Right (GMBUS3, 8);
+               end loop;
+               Transfered := Current;
+            end if;
+         end loop;
+         if Success then
+            Registers.Wait_Set_Mask
+              (Register => Registers.PCH_GMBUS2,
+               Mask     => GMBUS2_HARDWARE_WAIT_PHASE);
+            Registers.Write
+              (Register => Registers.PCH_GMBUS1,
+               Value    => GMBUS1_SOFTWARE_READY or GMBUS1_BUS_CYCLE_STOP);
+            Registers.Wait_Unset_Mask
+              (Register => Registers.PCH_GMBUS2,
+               Mask     => GMBUS2_GMBUS_ACTIVE);
+         end if;
+      end if;
+      Length := Transfered;
+
+      Release_GMBUS;
+   end I2C_Read;
+
+end HW.GFX.GMA.I2C;
diff --git a/common/hw-gfx-gma-i2c.ads b/common/hw-gfx-gma-i2c.ads
new file mode 100644
index 0000000..85f90dc
--- /dev/null
+++ b/common/hw-gfx-gma-i2c.ads
@@ -0,0 +1,25 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.I2C;
+
+private package HW.GFX.GMA.I2C is
+
+   procedure I2C_Read
+     (Port     : in     PCH_Port;
+      Address  : in     HW.GFX.I2C.Transfer_Address;
+      Length   : in out HW.GFX.I2C.Transfer_Length;
+      Data     :    out HW.GFX.I2C.Transfer_Data;
+      Success  :    out Boolean);
+
+end HW.GFX.GMA.I2C;
diff --git a/common/hw-gfx-gma-panel.adb b/common/hw-gfx-gma-panel.adb
new file mode 100644
index 0000000..1fc043d
--- /dev/null
+++ b/common/hw-gfx-gma-panel.adb
@@ -0,0 +1,358 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Config;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Panel
+with
+   Refined_State =>
+     (Panel_State =>
+        (Delays_US, Power_Cycle_Timer, Power_Up_Timer))
+is
+   type Delays_Enum is
+     (Power_Up_Delay,
+      Power_Up_To_BL_On,
+      Power_Down_Delay,
+      BL_Off_To_Power_Down,
+      Power_Cycle_Delay);
+
+   type Panel_Power_Delays is array (Delays_Enum) of Natural;
+   Default_EDP_Delays_US : constant Panel_Power_Delays := Panel_Power_Delays'
+     (Power_Up_Delay       => 210_000,
+      Power_Up_To_BL_On    =>  50_000,
+      Power_Down_Delay     => 500_000,
+      BL_Off_To_Power_Down =>  50_000,
+      Power_Cycle_Delay    => 510_000);
+
+   Delays_US : Panel_Power_Delays;
+
+   ----------------------------------------------------------------------------
+
+   -- And here the mess starts: We have this pretty hardware power sequencer
+   -- that should ensure the panel's timing constraints are satisfied. But
+   -- (at least on some generations) it doesn't do it's job. On Haswell, it
+   -- seems to ignore the Power_Cycle_Delay, so we ensure the delay in soft-
+   -- ware. On at least Ivy Bridge and Broadwell Power_Up_Delay is ignored.
+   --
+   -- If we ever do all delays in software, there are two ways: Either confi-
+   -- gure the hardware to zero delays or wait for both the software timeout
+   -- and the hardware power sequencer. The latter option would be less error
+   -- prone, as the hardware might just don't work as expected.
+
+   Power_Cycle_Timer : Time.T;
+   Power_Up_Timer    : Time.T;
+
+   ----------------------------------------------------------------------------
+
+   function Div_Round_Up32 (Numerator, Denominator : Natural) return Word32 is
+   begin
+      return (Word32 (Numerator) + Word32 (Denominator) - 1)
+               / Word32 (Denominator);
+   end Div_Round_Up32;
+
+   PCH_PP_STATUS_ENABLED               : constant := 16#00_0001# * 2 ** 31;
+   PCH_PP_STATUS_REQUIRE_ASSET         : constant := 16#00_0001# * 2 ** 30;
+   PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK : constant := 16#00_0003# * 2 ** 28;
+   PCH_PP_STATUS_PWR_SEQ_PROGRESS_NONE : constant := 16#00_0000# * 2 ** 28;
+   PCH_PP_STATUS_PWR_SEQ_PROGRESS_UP   : constant := 16#00_0001# * 2 ** 28;
+   PCH_PP_STATUS_PWR_SEQ_PROGRESS_DOWN : constant := 16#00_0002# * 2 ** 28;
+   PCH_PP_STATUS_PWR_CYC_DELAY_ACTIVE  : constant := 16#00_0001# * 2 ** 27;
+
+   PCH_PP_CONTROL_WRITE_PROTECT_MASK   : constant := 16#00_ffff# * 2 ** 16;
+   PCH_PP_CONTROL_WRITE_PROTECT_KEY    : constant := 16#00_abcd# * 2 ** 16;
+   PCH_PP_CONTROL_VDD_OVERRIDE         : constant := 16#00_0001# * 2 **  3;
+   PCH_PP_CONTROL_BACKLIGHT_ENABLE     : constant := 16#00_0001# * 2 **  2;
+   PCH_PP_CONTROL_POWER_DOWN_ON_RESET  : constant := 16#00_0001# * 2 **  1;
+   PCH_PP_CONTROL_TARGET_ON            : constant := 16#00_0001# * 2 **  0;
+
+   PCH_PP_ON_DELAYS_PORT_SELECT_MASK   : constant := 16#00_0003# * 2 ** 30;
+   PCH_PP_ON_DELAYS_PORT_SELECT_LVDS   : constant := 16#00_0000# * 2 ** 30;
+   PCH_PP_ON_DELAYS_PORT_SELECT_DP_A   : constant := 16#00_0001# * 2 ** 30;
+   PCH_PP_ON_DELAYS_PORT_SELECT_DP_C   : constant := 16#00_0002# * 2 ** 30;
+   PCH_PP_ON_DELAYS_PORT_SELECT_DP_D   : constant := 16#00_0003# * 2 ** 30;
+   PCH_PP_ON_DELAYS_PWR_UP_MASK        : constant := 16#00_1fff# * 2 ** 16;
+   PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK  : constant := 16#00_1fff# * 2 **  0;
+   function PCH_PP_ON_DELAYS_PWR_UP (US : Natural) return Word32 is
+   begin
+      return Shift_Left (Div_Round_Up32 (US, 100), 16);
+   end PCH_PP_ON_DELAYS_PWR_UP;
+   function PCH_PP_ON_DELAYS_PWR_UP_BL_ON (US : Natural) return Word32 is
+   begin
+      return Div_Round_Up32 (US, 100);
+   end PCH_PP_ON_DELAYS_PWR_UP_BL_ON;
+
+   PCH_PP_OFF_DELAYS_PWR_DOWN_MASK        : constant := 16#1fff# * 2 ** 16;
+   PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK : constant := 16#1fff# * 2 **  0;
+   function PCH_PP_OFF_DELAYS_PWR_DOWN (US : Natural) return Word32 is
+   begin
+      return Shift_Left (Div_Round_Up32 (US, 100), 16);
+   end PCH_PP_OFF_DELAYS_PWR_DOWN;
+   function PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN (US : Natural) return Word32 is
+   begin
+      return Div_Round_Up32 (US, 100);
+   end PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN;
+
+   PCH_PP_DIVISOR_REF_DIVIDER_MASK     : constant := 16#ff_ffff# * 2 **  8;
+   PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK   : constant := 16#00_001f# * 2 **  0;
+   function PCH_PP_DIVISOR_PWR_CYC_DELAY (US : Natural) return Word32 is
+   begin
+      return Div_Round_Up32 (US, 100_000) + 1;
+   end PCH_PP_DIVISOR_PWR_CYC_DELAY;
+
+   CPU_BLC_PWM_CTL_ENABLE              : constant := 16#00_0001# * 2 ** 31;
+   CPU_BLC_PWM_CTL_PIPE_SELECT_MASK    : constant := 16#00_0003# * 2 ** 29;
+   CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_A  : constant := 16#00_0000# * 2 ** 29;
+   CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_B  : constant := 16#00_0001# * 2 ** 29;
+   CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_C  : constant := 16#00_0002# * 2 ** 29;
+
+   CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK   : constant := 16#00_ffff# * 2 **  0;
+
+   PCH_BLC_PWM_CTL1_ENABLE             : constant := 16#00_0001# * 2 ** 31;
+   PCH_BLC_PWM_CTL1_BL_POLARITY_MASK   : constant := 16#00_0001# * 2 ** 29;
+   PCH_BLC_PWM_CTL1_PHASE_IN_INTR_STAT : constant := 16#00_0001# * 2 ** 26;
+   PCH_BLC_PWM_CTL1_PHASE_IN_ENABLE    : constant := 16#00_0001# * 2 ** 25;
+   PCH_BLC_PWM_CTL1_PHASE_IN_INTR_EN   : constant := 16#00_0001# * 2 ** 24;
+   PCH_BLC_PWM_CTL1_PHASE_IN_TIME_BASE : constant := 16#00_00ff# * 2 ** 16;
+   PCH_BLC_PWM_CTL1_PHASE_IN_COUNT     : constant := 16#00_00ff# * 2 **  8;
+   PCH_BLC_PWM_CTL1_PHASE_IN_INCREMENT : constant := 16#00_00ff# * 2 **  0;
+
+   PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK   : constant := 16#00_ffff# * 2 ** 16;
+   PCH_BLC_PWM_CTL2_BL_DUTY_CYC_MASK   : constant := 16#00_ffff# * 2 **  0;
+
+   ----------------------------------------------------------------------------
+
+   procedure Static_Init
+   with
+      Refined_Global =>
+        (Output => (Power_Cycle_Timer, Power_Up_Timer, Delays_US),
+         Input  => (Time.State))
+   is
+   begin
+      Power_Cycle_Timer := Time.Now;
+      Power_Up_Timer    := Power_Cycle_Timer;
+
+      Delays_US := Default_EDP_Delays_US;
+   end Static_Init;
+
+   ----------------------------------------------------------------------------
+
+   procedure Check_PP_Delays
+     (Delays   : in out Panel_Power_Delays;
+      Override : in out Boolean) is
+   begin
+      for D in Delays_Enum loop
+         if Delays (D) = 0 then
+            Delays (D) := Default_EDP_Delays_US (D);
+            Override := True;
+         end if;
+      end loop;
+   end Check_PP_Delays;
+
+   procedure Setup_PP_Sequencer (Default_Delays : Boolean := False)
+   is
+      Power_Delay, Port_Select : Word32;
+
+      Override_Delays : Boolean := False;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Static_Init;
+
+      if Default_Delays then
+         Override_Delays := True;
+      else
+         Registers.Read (Registers.PCH_PP_ON_DELAYS, Power_Delay);
+         Delays_US (Power_Up_Delay) := 100 * Natural
+           (Shift_Right (Power_Delay and PCH_PP_ON_DELAYS_PWR_UP_MASK, 16));
+         Delays_US (Power_Up_To_BL_On) := 100 * Natural
+           (Power_Delay and PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK);
+
+         Registers.Read (Registers.PCH_PP_OFF_DELAYS, Power_Delay);
+         Delays_US (Power_Down_Delay) := 100 * Natural
+           (Shift_Right (Power_Delay and PCH_PP_OFF_DELAYS_PWR_DOWN_MASK, 16));
+         Delays_US (BL_Off_To_Power_Down) := 100 * Natural
+           (Power_Delay and PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK);
+
+         Registers.Read (Registers.PCH_PP_DIVISOR, Power_Delay);
+         if (Power_Delay and PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK) > 1 then
+            Delays_US (Power_Cycle_Delay) := 100_000 * (Natural
+              (Power_Delay and PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK) - 1);
+         end if;
+
+         Check_PP_Delays (Delays_US, Override_Delays);
+      end if;
+
+      if Override_Delays then
+         if Config.Has_PP_Port_Select then
+            if Config.Internal_Is_EDP then
+               Port_Select := PCH_PP_ON_DELAYS_PORT_SELECT_DP_A;
+            else
+               Port_Select := PCH_PP_ON_DELAYS_PORT_SELECT_LVDS;
+            end if;
+         else
+            Port_Select := 0;
+         end if;
+
+         -- Force power-up to backlight-on delay to 100us as recommended by PRM.
+         Registers.Unset_And_Set_Mask
+           (Register    => Registers.PCH_PP_ON_DELAYS,
+            Mask_Unset  => PCH_PP_ON_DELAYS_PORT_SELECT_MASK or
+                           PCH_PP_ON_DELAYS_PWR_UP_MASK or
+                           PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK,
+            Mask_Set    => Port_Select or
+                           PCH_PP_ON_DELAYS_PWR_UP (Delays_US (Power_Up_Delay))
+                           or PCH_PP_ON_DELAYS_PWR_UP_BL_ON (100));
+
+         Registers.Unset_And_Set_Mask
+           (Register    => Registers.PCH_PP_OFF_DELAYS,
+            Mask_Unset  => PCH_PP_OFF_DELAYS_PWR_DOWN_MASK or
+                           PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK,
+            Mask_Set    => PCH_PP_OFF_DELAYS_PWR_DOWN
+                             (Delays_US (Power_Down_Delay)) or
+                           PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN
+                             (Delays_US (BL_Off_To_Power_Down)));
+
+         Registers.Unset_And_Set_Mask
+           (Register    => Registers.PCH_PP_DIVISOR,
+            Mask_Unset  => PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK,
+            Mask_Set    => PCH_PP_DIVISOR_PWR_CYC_DELAY
+                             (Delays_US (Power_Cycle_Delay)));
+      end if;
+
+      if Config.Has_PP_Write_Protection then
+         Registers.Unset_And_Set_Mask
+           (Register    => Registers.PCH_PP_CONTROL,
+            Mask_Unset  => PCH_PP_CONTROL_WRITE_PROTECT_MASK,
+            Mask_Set    => PCH_PP_CONTROL_WRITE_PROTECT_KEY or
+                           PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
+      else
+         Registers.Set_Mask
+           (Register => Registers.PCH_PP_CONTROL,
+            Mask     => PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
+      end if;
+   end Setup_PP_Sequencer;
+
+   ----------------------------------------------------------------------------
+
+   procedure VDD_Override is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      -- Yeah, We could do, what we are supposed to do here. But OTOH, we
+      -- are should wait for the full Power Up Delay, which we would have
+      -- to do later again. And just powering on the display seems to work
+      -- too. Also this function vanished on newer hardware.
+      On;
+   end VDD_Override;
+
+   procedure On (Wait : Boolean := True)
+   is
+      Was_On : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Is_Set_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
+      if not Was_On then
+         Time.Delay_Until (Power_Cycle_Timer);
+      end if;
+
+      Registers.Set_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_TARGET_ON);
+      if not Was_On then
+         Power_Up_Timer := Time.US_From_Now (Delays_US (Power_Up_Delay));
+      end if;
+      if Wait then
+         Wait_On;
+      end if;
+   end On;
+
+   procedure Wait_On is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Time.Delay_Until (Power_Up_Timer);
+      Registers.Wait_Unset_Mask
+        (Register => Registers.PCH_PP_STATUS,
+         Mask     => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
+         TOut_MS  => 300);
+
+      Registers.Unset_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_VDD_OVERRIDE);
+   end Wait_On;
+
+   procedure Off
+   is
+      Was_On : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Is_Set_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
+      Registers.Unset_Mask
+        (Register => Registers.PCH_PP_CONTROL,
+         Mask     => PCH_PP_CONTROL_TARGET_ON or
+                     PCH_PP_CONTROL_VDD_OVERRIDE);
+      if Was_On then
+         Time.U_Delay (Delays_US (Power_Down_Delay));
+      end if;
+      Registers.Wait_Unset_Mask
+        (Register => Registers.PCH_PP_STATUS,
+         Mask     => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
+         TOut_MS  => 600);
+      if Was_On then
+         Power_Cycle_Timer := Time.US_From_Now (Delays_US (Power_Cycle_Delay));
+      end if;
+   end Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Backlight_On is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Set_Mask
+         (Register   => Registers.PCH_PP_CONTROL,
+          Mask       => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
+   end Backlight_On;
+
+   procedure Backlight_Off is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_Mask
+        (Register   => Registers.PCH_PP_CONTROL,
+         Mask       => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
+   end Backlight_Off;
+
+   procedure Set_Backlight (Level : Word16) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+        (Register    => Registers.BLC_PWM_CPU_CTL,
+         Mask_Unset  => CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK,
+         Mask_Set    => Word32 (Level));
+   end Set_Backlight;
+
+   procedure Get_Max_Backlight (Level : out Word16)
+   is
+      Reg : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Read (Registers.BLC_PWM_PCH_CTL2, Reg);
+      Level := Word16
+        (Shift_Right (Reg and PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK, 16));
+   end Get_Max_Backlight;
+
+end HW.GFX.GMA.Panel;
diff --git a/common/hw-gfx-gma-panel.ads b/common/hw-gfx-gma-panel.ads
new file mode 100644
index 0000000..98dc93f
--- /dev/null
+++ b/common/hw-gfx-gma-panel.ads
@@ -0,0 +1,60 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+with HW.GFX.GMA.Registers;
+
+private package HW.GFX.GMA.Panel
+with
+   Abstract_State => (Panel_State with Part_Of => GMA.State)
+is
+
+   procedure Static_Init
+   with
+      Global =>
+        (Output => Panel_State,
+         Input  => Time.State);
+
+   procedure Setup_PP_Sequencer (Default_Delays : Boolean := False)
+   with
+      Global =>
+        (Input  => Time.State,
+         In_Out => Registers.Register_State,
+         Output => Panel_State),
+      Depends =>
+        ((Panel_State, Registers.Register_State) =>
+           (Time.State, Registers.Register_State, Default_Delays)),
+      Pre      => True,
+      Post     => True;
+
+   ----------------------------------------------------------------------------
+
+   procedure VDD_Override;
+
+   procedure On (Wait : Boolean := True);
+
+   procedure Wait_On;
+
+   procedure Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Backlight_On;
+
+   procedure Backlight_Off;
+
+   procedure Set_Backlight (Level : Word16);
+
+   procedure Get_Max_Backlight (Level : out Word16);
+
+end HW.GFX.GMA.Panel;
diff --git a/common/hw-gfx-gma-pch-fdi.adb b/common/hw-gfx-gma-pch-fdi.adb
new file mode 100644
index 0000000..7b7b3dd
--- /dev/null
+++ b/common/hw-gfx-gma-pch-fdi.adb
@@ -0,0 +1,293 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.PCH.FDI is
+
+   FDI_RX_CTL_FDI_RX_ENABLE               : constant := 1 * 2 ** 31;
+   FDI_RX_CTL_FS_ERROR_CORRECTION_ENABLE  : constant := 1 * 2 ** 27;
+   FDI_RX_CTL_FE_ERROR_CORRECTION_ENABLE  : constant := 1 * 2 ** 26;
+   FDI_RX_CTL_PORT_WIDTH_SEL_SHIFT        : constant :=          19;
+   FDI_RX_CTL_FDI_PLL_ENABLE              : constant := 1 * 2 ** 13;
+   FDI_RX_CTL_COMPOSITE_SYNC_SELECT       : constant := 1 * 2 ** 11;
+   FDI_RX_CTL_FDI_AUTO_TRAIN              : constant := 1 * 2 ** 10;
+   FDI_RX_CTL_ENHANCED_FRAMING_ENABLE     : constant := 1 * 2 **  6;
+   FDI_RX_CTL_RAWCLK_TO_PCDCLK_SEL_MASK   : constant := 1 * 2 **  4;
+   FDI_RX_CTL_RAWCLK_TO_PCDCLK_SEL_RAWCLK : constant := 0 * 2 **  4;
+   FDI_RX_CTL_RAWCLK_TO_PCDCLK_SEL_PCDCLK : constant := 1 * 2 **  4;
+
+   TP_SHIFT : constant := (if Config.CPU = Ironlake then 28 else 8);
+   FDI_RX_CTL_TRAINING_PATTERN_MASK       : constant := 3 * 2 ** TP_SHIFT;
+
+   type TP_Array is array (Training_Pattern) of Word32;
+   FDI_RX_CTL_TRAINING_PATTERN : constant TP_Array :=
+     (TP_1     => 0 * 2 ** TP_SHIFT,
+      TP_2     => 1 * 2 ** TP_SHIFT,
+      TP_Idle  => 2 * 2 ** TP_SHIFT,
+      TP_None  => 3 * 2 ** TP_SHIFT);
+
+   function FDI_RX_CTL_PORT_WIDTH_SEL (Lane_Count : DP_Lane_Count) return Word32
+   is
+   begin
+      return Shift_Left
+        (Word32 (Lane_Count_As_Integer (Lane_Count)) - 1,
+         FDI_RX_CTL_PORT_WIDTH_SEL_SHIFT);
+   end FDI_RX_CTL_PORT_WIDTH_SEL;
+
+   function FDI_RX_CTL_BPC (BPC : BPC_Type) return Word32
+   with Pre => True
+   is
+   begin
+      return
+        (case BPC is
+            when      6 => 2 * 2 ** 16,
+            when     10 => 1 * 2 ** 16,
+            when     12 => 3 * 2 ** 16,
+            when others => 0 * 2 ** 16);
+   end FDI_RX_CTL_BPC;
+
+   FDI_RX_MISC_FDI_RX_PWRDN_LANE1_SHIFT   : constant :=               26;
+   FDI_RX_MISC_FDI_RX_PWRDN_LANE1_MASK    : constant :=      3 * 2 ** 26;
+   FDI_RX_MISC_FDI_RX_PWRDN_LANE0_SHIFT   : constant :=               24;
+   FDI_RX_MISC_FDI_RX_PWRDN_LANE0_MASK    : constant :=      3 * 2 ** 24;
+   FDI_RX_MISC_TP1_TO_TP2_TIME_48         : constant :=      2 * 2 ** 20;
+   FDI_RX_MISC_FDI_DELAY_90               : constant := 16#90# * 2 **  0;
+
+   function FDI_RX_MISC_FDI_RX_PWRDN_LANE1 (Value : Word32) return Word32
+   with Pre => True
+   is
+   begin
+      return Shift_Left (Value, FDI_RX_MISC_FDI_RX_PWRDN_LANE1_SHIFT);
+   end FDI_RX_MISC_FDI_RX_PWRDN_LANE1;
+
+   function FDI_RX_MISC_FDI_RX_PWRDN_LANE0 (Value : Word32) return Word32
+   with Pre => True
+   is
+   begin
+      return Shift_Left (Value, FDI_RX_MISC_FDI_RX_PWRDN_LANE0_SHIFT);
+   end FDI_RX_MISC_FDI_RX_PWRDN_LANE0;
+
+   FDI_RX_TUSIZE_SHIFT                    : constant :=          25;
+
+   function FDI_RX_TUSIZE (Value : Word32) return Word32 is
+   begin
+      return Shift_Left (Value - 1, FDI_RX_TUSIZE_SHIFT);
+   end FDI_RX_TUSIZE;
+
+   FDI_RX_INTERLANE_ALIGNMENT             : constant := 1 * 2 ** 10;
+   FDI_RX_SYMBOL_LOCK                     : constant := 1 * 2 **  9;
+   FDI_RX_BIT_LOCK                        : constant := 1 * 2 **  8;
+
+   ----------------------------------------------------------------------------
+
+   type FDI_Registers is record
+      RX_CTL      : Registers.Registers_Index;
+      RX_MISC     : Registers.Registers_Index;
+      RX_TUSIZE   : Registers.Registers_Index;
+      RX_IMR      : Registers.Registers_Index;
+      RX_IIR      : Registers.Registers_Index;
+   end record;
+   type FDI_Registers_Array is array (PCH.FDI_Port_Type) of FDI_Registers;
+   FDI_Regs : constant FDI_Registers_Array := FDI_Registers_Array'
+     (PCH.FDI_A => FDI_Registers'
+        (RX_CTL      => Registers.FDI_RXA_CTL,
+         RX_MISC     => Registers.FDI_RX_MISC_A,
+         RX_TUSIZE   => Registers.FDI_RXA_TUSIZE1,
+         RX_IMR      => Registers.FDI_RXA_IMR,
+         RX_IIR      => Registers.FDI_RXA_IIR),
+      PCH.FDI_B => FDI_Registers'
+        (RX_CTL      => Registers.FDI_RXB_CTL,
+         RX_MISC     => Registers.FDI_RX_MISC_B,
+         RX_TUSIZE   => Registers.FDI_RXB_TUSIZE1,
+         RX_IMR      => Registers.FDI_RXB_IMR,
+         RX_IIR      => Registers.FDI_RXB_IIR),
+      PCH.FDI_C => FDI_Registers'
+        (RX_CTL      => Registers.FDI_RXC_CTL,
+         RX_MISC     => Registers.FDI_RX_MISC_C,
+         RX_TUSIZE   => Registers.FDI_RXC_TUSIZE1,
+         RX_IMR      => Registers.FDI_RXC_IMR,
+         RX_IIR      => Registers.FDI_RXC_IIR));
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_Train (Port : PCH.FDI_Port_Type; Port_Cfg : Port_Config)
+   is
+      Power_Down_Lane_Bits : constant Word32 :=
+        (if Config.Has_FDI_RX_Power_Down then
+            FDI_RX_MISC_FDI_RX_PWRDN_LANE1 (2) or
+            FDI_RX_MISC_FDI_RX_PWRDN_LANE0 (2)
+         else 0);
+      RX_CTL_Settings : constant Word32 :=
+         FDI_RX_CTL_PORT_WIDTH_SEL (Port_Cfg.FDI.Lane_Count) or
+        (if Config.Has_FDI_BPC then
+            FDI_RX_CTL_BPC (Port_Cfg.Mode.BPC) else 0) or
+        (if Config.Has_FDI_Composite_Sel then
+            FDI_RX_CTL_COMPOSITE_SYNC_SELECT else 0) or
+        (if Port_Cfg.FDI.Enhanced_Framing then
+            FDI_RX_CTL_ENHANCED_FRAMING_ENABLE else 0);
+   begin
+      -- TODO: HSW: check DISPIO_CR_TX_BMU_CR4, seems Linux doesn't know it
+
+      Registers.Write
+        (Register => FDI_Regs (Port).RX_MISC,
+         Value    => Power_Down_Lane_Bits or
+                     FDI_RX_MISC_TP1_TO_TP2_TIME_48 or
+                     FDI_RX_MISC_FDI_DELAY_90);
+
+      Registers.Write
+        (Register => FDI_Regs (Port).RX_TUSIZE,
+         Value    => FDI_RX_TUSIZE (64));
+
+      Registers.Unset_Mask
+        (Register => FDI_Regs (Port).RX_IMR,
+         Mask     => FDI_RX_INTERLANE_ALIGNMENT or
+                     FDI_RX_SYMBOL_LOCK or
+                     FDI_RX_BIT_LOCK);
+      Registers.Posting_Read (FDI_Regs (Port).RX_IMR);
+      -- clear stale lock bits
+      Registers.Write
+        (Register => FDI_Regs (Port).RX_IIR,
+         Value    => FDI_RX_INTERLANE_ALIGNMENT or
+                     FDI_RX_SYMBOL_LOCK or
+                     FDI_RX_BIT_LOCK);
+
+      Registers.Write
+        (Register => FDI_Regs (Port).RX_CTL,
+         Value    => FDI_RX_CTL_FDI_PLL_ENABLE or
+                     RX_CTL_Settings);
+      Registers.Posting_Read (FDI_Regs (Port).RX_CTL);
+      Time.U_Delay (220);
+
+      Registers.Set_Mask
+        (Register => FDI_Regs (Port).RX_CTL,
+         Mask     => FDI_RX_CTL_RAWCLK_TO_PCDCLK_SEL_PCDCLK);
+   end Pre_Train;
+
+   procedure Train
+     (Port     : in     PCH.FDI_Port_Type;
+      TP       : in     Training_Pattern;
+      Success  :    out Boolean)
+   is
+      Lock_Bit : constant Word32 :=
+        (if TP = TP_1 then FDI_RX_BIT_LOCK else FDI_RX_SYMBOL_LOCK);
+
+      procedure Check_Lock (Lock_Bit : Word32)
+      is
+      begin
+         for I in 1 .. 5 loop
+            Registers.Is_Set_Mask
+              (Register => FDI_Regs (Port).RX_IIR,
+               Mask     => Lock_Bit,
+               Result   => Success);
+            if Success then
+               -- clear the lock bit
+               Registers.Write
+                 (Register => FDI_Regs (Port).RX_IIR,
+                  Value    => Lock_Bit);
+            end if;
+            exit when Success;
+            Time.U_Delay (1);
+         end loop;
+      end Check_Lock;
+   begin
+      Registers.Unset_And_Set_Mask
+        (Register    => FDI_Regs (Port).RX_CTL,
+         Mask_Unset  => FDI_RX_CTL_TRAINING_PATTERN_MASK,
+         Mask_Set    => FDI_RX_CTL_FDI_RX_ENABLE or
+                        FDI_RX_CTL_TRAINING_PATTERN (TP));
+      Registers.Posting_Read (FDI_Regs (Port).RX_CTL);
+
+      if TP <= TP_2 then
+         Time.U_Delay (1);
+         if TP = TP_1 then
+            Check_Lock (FDI_RX_BIT_LOCK);
+         else
+            Check_Lock (FDI_RX_SYMBOL_LOCK);
+            if Success then
+               Check_Lock (FDI_RX_INTERLANE_ALIGNMENT);
+            end if;
+         end if;
+      else
+         Time.U_Delay (31);
+         Success := True;
+      end if;
+   end Train;
+
+   procedure Auto_Train (Port : PCH.FDI_Port_Type)
+   is
+   begin
+      Registers.Set_Mask
+        (Register => FDI_Regs (Port).RX_CTL,
+         Mask     => FDI_RX_CTL_FDI_RX_ENABLE or
+                     FDI_RX_CTL_FDI_AUTO_TRAIN);
+      Registers.Posting_Read (FDI_Regs (Port).RX_CTL);
+
+      if Config.Has_FDI_RX_Power_Down then
+         Time.U_Delay (30);
+         Registers.Unset_And_Set_Mask
+           (Register    => FDI_Regs (Port).RX_MISC,
+            Mask_Unset  => FDI_RX_MISC_FDI_RX_PWRDN_LANE1_MASK or
+                           FDI_RX_MISC_FDI_RX_PWRDN_LANE0_MASK,
+            Mask_Set    => FDI_RX_MISC_FDI_RX_PWRDN_LANE1 (0) or
+                           FDI_RX_MISC_FDI_RX_PWRDN_LANE0 (0));
+         Registers.Posting_Read (FDI_Regs (Port).RX_MISC);
+      end if;
+
+      Time.U_Delay (5);
+   end Auto_Train;
+
+   procedure Enable_EC (Port : PCH.FDI_Port_Type)
+   is
+   begin
+      Registers.Set_Mask
+        (Register => FDI_Regs (Port).RX_CTL,
+         Mask     => FDI_RX_CTL_FS_ERROR_CORRECTION_ENABLE or
+                     FDI_RX_CTL_FE_ERROR_CORRECTION_ENABLE);
+   end Enable_EC;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off (Port : PCH.FDI_Port_Type; OT : Off_Type)
+   is
+   begin
+      Registers.Unset_Mask
+        (Register => FDI_Regs (Port).RX_CTL,
+         Mask     => FDI_RX_CTL_FDI_RX_ENABLE or
+                     FDI_RX_CTL_FDI_AUTO_TRAIN);
+
+      if Config.Has_FDI_RX_Power_Down and then OT >= Lanes_Off then
+         Registers.Unset_And_Set_Mask
+           (Register    => FDI_Regs (Port).RX_MISC,
+            Mask_Unset  => FDI_RX_MISC_FDI_RX_PWRDN_LANE1_MASK or
+                           FDI_RX_MISC_FDI_RX_PWRDN_LANE0_MASK,
+            Mask_Set    => FDI_RX_MISC_FDI_RX_PWRDN_LANE1 (2) or
+                           FDI_RX_MISC_FDI_RX_PWRDN_LANE0 (2));
+         Registers.Posting_Read (FDI_Regs (Port).RX_MISC);
+      end if;
+
+      if OT >= Clock_Off then
+         Registers.Unset_And_Set_Mask
+           (Register    => FDI_Regs (Port).RX_CTL,
+            Mask_Unset  => FDI_RX_CTL_RAWCLK_TO_PCDCLK_SEL_MASK,
+            Mask_Set    => FDI_RX_CTL_RAWCLK_TO_PCDCLK_SEL_RAWCLK);
+
+         Registers.Unset_Mask
+           (Register    => FDI_Regs (Port).RX_CTL,
+            Mask        => FDI_RX_CTL_FDI_PLL_ENABLE);
+      end if;
+   end Off;
+
+end HW.GFX.GMA.PCH.FDI;
diff --git a/common/hw-gfx-gma-pch-fdi.ads b/common/hw-gfx-gma-pch-fdi.ads
new file mode 100644
index 0000000..1de78d5
--- /dev/null
+++ b/common/hw-gfx-gma-pch-fdi.ads
@@ -0,0 +1,29 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.PCH.FDI is
+
+   type Training_Pattern is (TP_1, TP_2, TP_Idle, TP_None);
+
+   procedure Pre_Train (Port : PCH.FDI_Port_Type; Port_Cfg : Port_Config);
+   procedure Train
+     (Port     : in     PCH.FDI_Port_Type;
+      TP       : in     Training_Pattern;
+      Success  :    out Boolean);
+   procedure Auto_Train (Port : PCH.FDI_Port_Type);
+   procedure Enable_EC (Port : PCH.FDI_Port_Type);
+
+   type Off_Type is (Rx_Off, Lanes_Off, Clock_Off);
+   procedure Off (Port : PCH.FDI_Port_Type; OT : Off_Type);
+
+end HW.GFX.GMA.PCH.FDI;
diff --git a/common/hw-gfx-gma-pch-sideband.adb b/common/hw-gfx-gma-pch-sideband.adb
new file mode 100644
index 0000000..abbf472
--- /dev/null
+++ b/common/hw-gfx-gma-pch-sideband.adb
@@ -0,0 +1,137 @@
+--
+-- Copyright (C) 2015 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.PCH.Sideband is
+
+   SBI_CTL_DEST_ICLK : constant := 0 * 2 ** 16;
+   SBI_CTL_DEST_MPHY : constant := 1 * 2 ** 16;
+   SBI_CTL_OP_IORD   : constant := 2 * 2 **  8;
+   SBI_CTL_OP_IOWR   : constant := 3 * 2 **  8;
+   SBI_CTL_OP_CRRD   : constant := 6 * 2 **  8;
+   SBI_CTL_OP_CRWR   : constant := 7 * 2 **  8;
+
+   SBI_RESPONSE_FAIL : constant := 1 * 2 **  1;
+   SBI_BUSY          : constant := 1 * 2 **  0;
+
+   type Register_Array is array (Register_Type) of Word32;
+   Register_Addr : constant Register_Array := Register_Array'
+     (SBI_SSCDIVINTPHASE6  => 16#0600_0000#,
+      SBI_SSCCTL6          => 16#060c_0000#,
+      SBI_SSCAUXDIV        => 16#0610_0000#);
+
+   ----------------------------------------------------------------------------
+
+   procedure Read
+     (Dest     : in     Destination_Type;
+      Register : in     Register_Type;
+      Value    :    out Word32)
+   is
+   begin
+      Registers.Wait_Unset_Mask
+        (Register => Registers.SBI_CTL_STAT,
+         Mask     => SBI_BUSY);
+
+      Registers.Write
+        (Register => Registers.SBI_ADDR,
+         Value    => Register_Addr (Register));
+
+      if Dest = SBI_ICLK then
+         Registers.Write
+           (Register => Registers.SBI_CTL_STAT,
+            Value    => SBI_CTL_DEST_ICLK or SBI_CTL_OP_CRRD or SBI_BUSY);
+      else
+         Registers.Write
+           (Register => Registers.SBI_CTL_STAT,
+            Value    => SBI_CTL_DEST_MPHY or SBI_CTL_OP_IORD or SBI_BUSY);
+      end if;
+
+      Registers.Wait_Unset_Mask
+        (Register => Registers.SBI_CTL_STAT,
+         Mask     => SBI_BUSY);
+
+      Registers.Read
+        (Register => Registers.SBI_DATA,
+         Value    => Value);
+   end Read;
+
+   procedure Write
+     (Dest     : in     Destination_Type;
+      Register : in     Register_Type;
+      Value    : in     Word32)
+   is
+   begin
+      Registers.Wait_Unset_Mask
+        (Register => Registers.SBI_CTL_STAT,
+         Mask     => SBI_BUSY);
+
+      Registers.Write
+        (Register => Registers.SBI_ADDR,
+         Value    => Register_Addr (Register));
+      Registers.Write
+        (Register => Registers.SBI_DATA,
+         Value    => Value);
+
+      if Dest = SBI_ICLK then
+         Registers.Write
+           (Register => Registers.SBI_CTL_STAT,
+            Value    => SBI_CTL_DEST_ICLK or SBI_CTL_OP_CRWR or SBI_BUSY);
+      else
+         Registers.Write
+           (Register => Registers.SBI_CTL_STAT,
+            Value    => SBI_CTL_DEST_MPHY or SBI_CTL_OP_IOWR or SBI_BUSY);
+      end if;
+
+      Registers.Wait_Unset_Mask
+        (Register => Registers.SBI_CTL_STAT,
+         Mask     => SBI_BUSY);
+   end Write;
+
+   ----------------------------------------------------------------------------
+
+   procedure Unset_Mask
+     (Dest     : in     Destination_Type;
+      Register : in     Register_Type;
+      Mask     : in     Word32)
+   is
+      Value : Word32;
+   begin
+      Read (Dest, Register, Value);
+      Write (Dest, Register, Value and not Mask);
+   end Unset_Mask;
+
+   procedure Set_Mask
+     (Dest     : in     Destination_Type;
+      Register : in     Register_Type;
+      Mask     : in     Word32)
+   is
+      Value : Word32;
+   begin
+      Read (Dest, Register, Value);
+      Write (Dest, Register, Value or Mask);
+   end Set_Mask;
+
+   procedure Unset_And_Set_Mask
+     (Dest        : in     Destination_Type;
+      Register    : in     Register_Type;
+      Mask_Unset  : in     Word32;
+      Mask_Set    : in     Word32)
+   is
+      Value : Word32;
+   begin
+      Read (Dest, Register, Value);
+      Write (Dest, Register, (Value and not Mask_Unset) or Mask_Set);
+   end Unset_And_Set_Mask;
+
+end HW.GFX.GMA.PCH.Sideband;
diff --git a/common/hw-gfx-gma-pch-sideband.ads b/common/hw-gfx-gma-pch-sideband.ads
new file mode 100644
index 0000000..2d29275
--- /dev/null
+++ b/common/hw-gfx-gma-pch-sideband.ads
@@ -0,0 +1,49 @@
+--
+-- Copyright (C) 2015 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PCH.Sideband is
+
+   type Destination_Type is (SBI_ICLK, SBI_MPHY);
+
+   type Register_Type is
+     (SBI_SSCDIVINTPHASE6,
+      SBI_SSCCTL6,
+      SBI_SSCAUXDIV);
+
+   procedure Read
+     (Dest     : in     Destination_Type;
+      Register : in     Register_Type;
+      Value    :    out Word32);
+
+   procedure Write
+     (Dest     : in     Destination_Type;
+      Register : in     Register_Type;
+      Value    : in     Word32);
+
+   procedure Unset_Mask
+     (Dest     : in     Destination_Type;
+      Register : in     Register_Type;
+      Mask     : in     Word32);
+
+   procedure Set_Mask
+     (Dest     : in     Destination_Type;
+      Register : in     Register_Type;
+      Mask     : in     Word32);
+
+   procedure Unset_And_Set_Mask
+     (Dest        : in     Destination_Type;
+      Register    : in     Register_Type;
+      Mask_Unset  : in     Word32;
+      Mask_Set    : in     Word32);
+
+end HW.GFX.GMA.PCH.Sideband;
diff --git a/common/hw-gfx-gma-pch-transcoder.adb b/common/hw-gfx-gma-pch-transcoder.adb
new file mode 100644
index 0000000..bab3f31
--- /dev/null
+++ b/common/hw-gfx-gma-pch-transcoder.adb
@@ -0,0 +1,280 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+-- Copyright (C) 2016 Nico Huber <nico.h@gmx.de>
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PCH.Transcoder is
+
+   type DPLL_SEL_Array is array (FDI_Port_Type) of Word32;
+   DPLL_SEL_TRANSCODER_x_DPLL_ENABLE : constant DPLL_SEL_Array :=
+     (FDI_A => 1 * 2 **  3,
+      FDI_B => 1 * 2 **  7,
+      FDI_C => 1 * 2 ** 11);
+   DPLL_SEL_TRANSCODER_x_DPLL_SEL_MASK : constant DPLL_SEL_Array :=
+     (FDI_A => 1 * 2 **  0,
+      FDI_B => 1 * 2 **  4,
+      FDI_C => 1 * 2 **  8);
+   function DPLL_SEL_TRANSCODER_x_DPLL_SEL
+     (Port : FDI_Port_Type;
+      PLL  : Word32)
+      return Word32
+   is
+   begin
+      return Shift_Left (PLL,
+        (case Port is
+            when FDI_A => 0,
+            when FDI_B => 4,
+            when FDI_C => 8));
+   end DPLL_SEL_TRANSCODER_x_DPLL_SEL;
+
+   TRANS_CONF_TRANSCODER_ENABLE        : constant := 1 * 2 ** 31;
+   TRANS_CONF_TRANSCODER_STATE         : constant := 1 * 2 ** 30;
+
+   TRANS_CHICKEN2_TIMING_OVERRIDE      : constant := 1 * 2 ** 31;
+
+   TRANS_DP_CTL_OUTPUT_ENABLE          : constant := 1 * 2 ** 31;
+   TRANS_DP_CTL_PORT_SELECT_MASK       : constant := 3 * 2 ** 29;
+   TRANS_DP_CTL_PORT_SELECT_NONE       : constant := 3 * 2 ** 29;
+   TRANS_DP_CTL_ENHANCED_FRAMING       : constant := 1 * 2 ** 18;
+   TRANS_DP_CTL_VSYNC_ACTIVE_HIGH      : constant := 1 * 2 **  4;
+   TRANS_DP_CTL_HSYNC_ACTIVE_HIGH      : constant := 1 * 2 **  3;
+
+   type TRANS_DP_CTL_PORT_SELECT_Array is array (PCH_Port) of Word32;
+   TRANS_DP_CTL_PORT_SELECT : constant TRANS_DP_CTL_PORT_SELECT_Array :=
+     (PCH_DP_B => 0 * 2 ** 29,
+      PCH_DP_C => 1 * 2 ** 29,
+      PCH_DP_D => 2 * 2 ** 29,
+      others   => 0);
+
+   function TRANS_DP_CTL_BPC (BPC : BPC_Type) return Word32
+   is
+   begin
+      return
+        (case BPC is
+            when      6 => 2 * 2 ** 9,
+            when     10 => 1 * 2 ** 9,
+            when     12 => 3 * 2 ** 9,
+            when others => 0 * 2 ** 9);
+   end TRANS_DP_CTL_BPC;
+
+   function TRANS_DATA_M_TU (Transfer_Unit : Positive) return Word32 is
+   begin
+      return Shift_Left (Word32 (Transfer_Unit - 1), 25);
+   end TRANS_DATA_M_TU;
+
+   ----------------------------------------------------------------------------
+
+   type Transcoder_Registers is record
+      HTOTAL   : Registers.Registers_Index;
+      HBLANK   : Registers.Registers_Index;
+      HSYNC    : Registers.Registers_Index;
+      VTOTAL   : Registers.Registers_Index;
+      VBLANK   : Registers.Registers_Index;
+      VSYNC    : Registers.Registers_Index;
+      CONF     : Registers.Registers_Index;
+      DP_CTL   : Registers.Registers_Index;
+      DATA_M   : Registers.Registers_Index;
+      DATA_N   : Registers.Registers_Index;
+      LINK_M   : Registers.Registers_Index;
+      LINK_N   : Registers.Registers_Index;
+      CHICKEN2 : Registers.Registers_Index;
+   end record;
+
+   type Transcoder_Registers_Array is
+      array (FDI_Port_Type) of Transcoder_Registers;
+
+   TRANS : constant Transcoder_Registers_Array := Transcoder_Registers_Array'
+     (FDI_A =>
+        (HTOTAL   => Registers.TRANS_HTOTAL_A,
+         HBLANK   => Registers.TRANS_HBLANK_A,
+         HSYNC    => Registers.TRANS_HSYNC_A,
+         VTOTAL   => Registers.TRANS_VTOTAL_A,
+         VBLANK   => Registers.TRANS_VBLANK_A,
+         VSYNC    => Registers.TRANS_VSYNC_A,
+         CONF     => Registers.TRANSACONF,
+         DP_CTL   => Registers.TRANS_DP_CTL_A,
+         DATA_M   => Registers.TRANSA_DATA_M1,
+         DATA_N   => Registers.TRANSA_DATA_N1,
+         LINK_M   => Registers.TRANSA_DP_LINK_M1,
+         LINK_N   => Registers.TRANSA_DP_LINK_N1,
+         CHICKEN2 => Registers.TRANSA_CHICKEN2),
+      FDI_B =>
+        (HTOTAL   => Registers.TRANS_HTOTAL_B,
+         HBLANK   => Registers.TRANS_HBLANK_B,
+         HSYNC    => Registers.TRANS_HSYNC_B,
+         VTOTAL   => Registers.TRANS_VTOTAL_B,
+         VBLANK   => Registers.TRANS_VBLANK_B,
+         VSYNC    => Registers.TRANS_VSYNC_B,
+         CONF     => Registers.TRANSBCONF,
+         DP_CTL   => Registers.TRANS_DP_CTL_B,
+         DATA_M   => Registers.TRANSB_DATA_M1,
+         DATA_N   => Registers.TRANSB_DATA_N1,
+         LINK_M   => Registers.TRANSB_DP_LINK_M1,
+         LINK_N   => Registers.TRANSB_DP_LINK_N1,
+         CHICKEN2 => Registers.TRANSB_CHICKEN2),
+      FDI_C =>
+        (HTOTAL   => Registers.TRANS_HTOTAL_C,
+         HBLANK   => Registers.TRANS_HBLANK_C,
+         HSYNC    => Registers.TRANS_HSYNC_C,
+         VTOTAL   => Registers.TRANS_VTOTAL_C,
+         VBLANK   => Registers.TRANS_VBLANK_C,
+         VSYNC    => Registers.TRANS_VSYNC_C,
+         CONF     => Registers.TRANSCCONF,
+         DP_CTL   => Registers.TRANS_DP_CTL_C,
+         DATA_M   => Registers.TRANSC_DATA_M1,
+         DATA_N   => Registers.TRANSC_DATA_N1,
+         LINK_M   => Registers.TRANSC_DP_LINK_M1,
+         LINK_N   => Registers.TRANSC_DP_LINK_N1,
+         CHICKEN2 => Registers.TRANSC_CHICKEN2));
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (Port_Cfg : Port_Config;
+      Port     : FDI_Port_Type;
+      PLL      : Word32)
+   is
+      Mode : constant Mode_Type := Port_Cfg.Mode;
+
+      function Encode (LSW, MSW : Pos16) return Word32 is
+      begin
+         return (Word32 (LSW) - 1) or ((Word32 (MSW) - 1) * 2 ** 16);
+      end Encode;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.Has_DPLL_SEL then
+         Registers.Unset_And_Set_Mask
+           (Register    => Registers.PCH_DPLL_SEL,
+            Mask_Unset  => DPLL_SEL_TRANSCODER_x_DPLL_SEL_MASK (Port),
+            Mask_Set    => DPLL_SEL_TRANSCODER_x_DPLL_ENABLE (Port) or
+                           DPLL_SEL_TRANSCODER_x_DPLL_SEL (Port, PLL));
+      end if;
+
+      Registers.Write
+        (Register => TRANS (Port).HTOTAL,
+         Value    => Encode (Mode.H_Visible,    Mode.H_Total));
+      Registers.Write
+        (Register => TRANS (Port).HBLANK,
+         Value    => Encode (Mode.H_Visible,    Mode.H_Total));
+      Registers.Write
+        (Register => TRANS (Port).HSYNC,
+         Value    => Encode (Mode.H_Sync_Begin, Mode.H_Sync_End));
+      Registers.Write
+        (Register => TRANS (Port).VTOTAL,
+         Value    => Encode (Mode.V_Visible,    Mode.V_Total));
+      Registers.Write
+        (Register => TRANS (Port).VBLANK,
+         Value    => Encode (Mode.V_Visible,    Mode.V_Total));
+      Registers.Write
+        (Register => TRANS (Port).VSYNC,
+         Value    => Encode (Mode.V_Sync_Begin, Mode.V_Sync_End));
+
+      if Port_Cfg.Display = DP then
+         declare
+            Data_M, Link_M : DP_Info.M_Type;
+            Data_N, Link_N : DP_Info.N_Type;
+         begin
+            DP_Info.Calculate_M_N
+              (Link     => Port_Cfg.DP,
+               Mode     => Port_Cfg.Mode,
+               Data_M   => Data_M,
+               Data_N   => Data_N,
+               Link_M   => Link_M,
+               Link_N   => Link_N);
+            Registers.Write
+              (Register => TRANS (Port).DATA_M,
+               Value    => TRANS_DATA_M_TU (64) or
+                           Word32 (Data_M));
+            Registers.Write
+              (Register => TRANS (Port).DATA_N,
+               Value    => Word32 (Data_N));
+            Registers.Write
+              (Register => TRANS (Port).LINK_M,
+               Value    => Word32 (Link_M));
+            Registers.Write
+              (Register => TRANS (Port).LINK_N,
+               Value    => Word32 (Link_N));
+         end;
+
+         if Config.Has_Trans_DP_Ctl then
+            declare
+               Polarity : constant Word32 :=
+                 (if Port_Cfg.Mode.H_Sync_Active_High then
+                     TRANS_DP_CTL_HSYNC_ACTIVE_HIGH else 0) or
+                 (if Port_Cfg.Mode.V_Sync_Active_High then
+                     TRANS_DP_CTL_VSYNC_ACTIVE_HIGH else 0);
+               Enhanced_Framing : constant Word32 :=
+                 (if Port_Cfg.DP.Enhanced_Framing then
+                     TRANS_DP_CTL_ENHANCED_FRAMING else 0);
+            begin
+               Registers.Write
+                 (Register => TRANS (Port).DP_CTL,
+                  Value    => TRANS_DP_CTL_OUTPUT_ENABLE or
+                              TRANS_DP_CTL_PORT_SELECT (Port_Cfg.PCH_Port) or
+                              Enhanced_Framing or
+                              TRANS_DP_CTL_BPC (Port_Cfg.Mode.BPC) or
+                              Polarity);
+            end;
+         end if;
+      end if;
+
+      if Config.Has_Trans_Timing_Ovrrde then
+         Registers.Set_Mask
+           (Register => TRANS (Port).CHICKEN2,
+            Mask     => TRANS_CHICKEN2_TIMING_OVERRIDE);
+      end if;
+
+      Registers.Write
+        (Register => TRANS (Port).CONF,
+         Value    => TRANS_CONF_TRANSCODER_ENABLE);
+   end On;
+
+   procedure Off (Port : FDI_Port_Type) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_Mask
+        (Register => TRANS (Port).CONF,
+         Mask     => TRANS_CONF_TRANSCODER_ENABLE);
+      Registers.Wait_Unset_Mask
+        (Register => TRANS (Port).CONF,
+         Mask     => TRANS_CONF_TRANSCODER_STATE,
+         TOut_MS  => 50);
+
+      if Config.Has_Trans_Timing_Ovrrde then
+         Registers.Unset_Mask
+           (Register => TRANS (Port).CHICKEN2,
+            Mask     => TRANS_CHICKEN2_TIMING_OVERRIDE);
+      end if;
+
+      if Config.Has_Trans_DP_Ctl then
+         Registers.Write
+           (Register => TRANS (Port).DP_CTL,
+            Value    => TRANS_DP_CTL_PORT_SELECT_NONE);
+      end if;
+
+      if Config.Has_DPLL_SEL then
+         Registers.Unset_Mask
+           (Register => Registers.PCH_DPLL_SEL,
+            Mask     => DPLL_SEL_TRANSCODER_x_DPLL_ENABLE (Port));
+      end if;
+   end Off;
+
+end HW.GFX.GMA.PCH.Transcoder;
diff --git a/common/hw-gfx-gma-pch-transcoder.ads b/common/hw-gfx-gma-pch-transcoder.ads
new file mode 100644
index 0000000..b1c5ca9
--- /dev/null
+++ b/common/hw-gfx-gma-pch-transcoder.ads
@@ -0,0 +1,24 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.PCH.Transcoder is
+
+   procedure On
+     (Port_Cfg : Port_Config;
+      Port     : FDI_Port_Type;
+      PLL      : Word32);
+
+   procedure Off
+     (Port  : FDI_Port_Type);
+
+end HW.GFX.GMA.PCH.Transcoder;
diff --git a/common/hw-gfx-gma-pch-vga.adb b/common/hw-gfx-gma-pch-vga.adb
new file mode 100644
index 0000000..8e3f9c1
--- /dev/null
+++ b/common/hw-gfx-gma-pch-vga.adb
@@ -0,0 +1,171 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.PCH.Sideband;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+use type HW.Word64;
+
+package body HW.GFX.GMA.PCH.VGA is
+
+   PCH_ADPA_DAC_ENABLE        : constant := 1 * 2 ** 31;
+   PCH_ADPA_VSYNC_DISABLE     : constant := 1 * 2 ** 11;
+   PCH_ADPA_HSYNC_DISABLE     : constant := 1 * 2 ** 10;
+   PCH_ADPA_VSYNC_ACTIVE_HIGH : constant := 1 * 2 **  4;
+   PCH_ADPA_HSYNC_ACTIVE_HIGH : constant := 1 * 2 **  3;
+
+   PCH_ADPA_MASK : constant Word32 :=
+      PCH_TRANSCODER_SELECT_MASK or
+      PCH_ADPA_DAC_ENABLE        or
+      PCH_ADPA_VSYNC_DISABLE     or
+      PCH_ADPA_HSYNC_DISABLE     or
+      PCH_ADPA_VSYNC_ACTIVE_HIGH or
+      PCH_ADPA_HSYNC_ACTIVE_HIGH;
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (Port : FDI_Port_Type;
+      Mode : Mode_Type)
+   is
+      Polarity : Word32 := 0;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Mode.H_Sync_Active_High then
+         Polarity := Polarity or PCH_ADPA_HSYNC_ACTIVE_HIGH;
+      end if;
+      if Mode.V_Sync_Active_High then
+         Polarity := Polarity or PCH_ADPA_VSYNC_ACTIVE_HIGH;
+      end if;
+
+      Registers.Unset_And_Set_Mask
+        (Register    => Registers.PCH_ADPA,
+         Mask_Unset  => PCH_ADPA_MASK,
+         Mask_Set    => PCH_ADPA_DAC_ENABLE or
+                        PCH_TRANSCODER_SELECT (Port) or
+                        Polarity);
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off
+   is
+      Sync_Disable : Word32 := 0;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.VGA_Has_Sync_Disable then
+         Sync_Disable := PCH_ADPA_HSYNC_DISABLE or PCH_ADPA_VSYNC_DISABLE;
+      end if;
+
+      Registers.Unset_And_Set_Mask
+        (Register    => Registers.PCH_ADPA,
+         Mask_Unset  => PCH_ADPA_DAC_ENABLE,
+         Mask_Set    => Sync_Disable);
+   end Off;
+
+   ----------------------------------------------------------------------------
+
+   PCH_PIXCLK_GATE_GATE    : constant := 0 * 2 ** 0;
+   PCH_PIXCLK_GATE_UNGATE  : constant := 1 * 2 ** 0;
+
+   SBI_SSCCTL_DISABLE               : constant :=      1 * 2 **  0;
+   SBI_SSCDIVINTPHASE_DIVSEL_SHIFT  : constant :=                1;
+   SBI_SSCDIVINTPHASE_DIVSEL_MASK   : constant := 16#7f# * 2 **  1;
+   SBI_SSCDIVINTPHASE_INCVAL_SHIFT  : constant :=                8;
+   SBI_SSCDIVINTPHASE_INCVAL_MASK   : constant := 16#7f# * 2 **  8;
+   SBI_SSCDIVINTPHASE_DIR_SHIFT     : constant :=               15;
+   SBI_SSCDIVINTPHASE_DIR_MASK      : constant := 16#01# * 2 ** 15;
+   SBI_SSCDIVINTPHASE_PROPAGATE     : constant :=      1 * 2 **  0;
+   SBI_SSCAUXDIV_FINALDIV2SEL_SHIFT : constant :=                4;
+   SBI_SSCAUXDIV_FINALDIV2SEL_MASK  : constant := 16#01# * 2 **  4;
+
+   function SBI_SSCDIVINTPHASE_DIVSEL (Val : Word32) return Word32 is
+   begin
+      return Shift_Left (Val, SBI_SSCDIVINTPHASE_DIVSEL_SHIFT);
+   end SBI_SSCDIVINTPHASE_DIVSEL;
+
+   function SBI_SSCDIVINTPHASE_INCVAL (Val : Word32) return Word32 is
+   begin
+      return Shift_Left (Val, SBI_SSCDIVINTPHASE_INCVAL_SHIFT);
+   end SBI_SSCDIVINTPHASE_INCVAL;
+
+   function SBI_SSCDIVINTPHASE_DIR (Val : Word32) return Word32 is
+   begin
+      return Shift_Left (Val, SBI_SSCDIVINTPHASE_DIR_SHIFT);
+   end SBI_SSCDIVINTPHASE_DIR;
+
+   function SBI_SSCAUXDIV_FINALDIV2SEL (Val : Word32) return Word32 is
+   begin
+      return Shift_Left (Val, SBI_SSCAUXDIV_FINALDIV2SEL_SHIFT);
+   end SBI_SSCAUXDIV_FINALDIV2SEL;
+
+   procedure Clock_On (Mode : Mode_Type)
+   is
+      Refclock : constant := 2_700_000_000;
+
+      Aux_Div,
+      Div_Sel,
+      Phase_Inc,
+      Phase_Dir   : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Write (Registers.PCH_PIXCLK_GATE, PCH_PIXCLK_GATE_GATE);
+
+      Sideband.Set_Mask
+        (Dest     => Sideband.SBI_ICLK,
+         Register => Sideband.SBI_SSCCTL6,
+         Mask     => SBI_SSCCTL_DISABLE);
+
+      Aux_Div     := 16#0000_0000#;
+      Div_Sel     := Word32 (Refclock / Mode.Dotclock - 2);
+      Phase_Inc   := Word32 ((Refclock * 64) / Mode.Dotclock) and 16#0000_003f#;
+      Phase_Dir   := 16#0000_0000#;
+
+      pragma Debug (Debug.Put_Reg32 ("Aux_Div  ", Aux_Div));
+      pragma Debug (Debug.Put_Reg32 ("Div_Sel  ", Div_Sel));
+      pragma Debug (Debug.Put_Reg32 ("Phase_Inc", Phase_Inc));
+      pragma Debug (Debug.Put_Reg32 ("Phase_Dir", Phase_Dir));
+
+      Sideband.Unset_And_Set_Mask
+        (Dest        => Sideband.SBI_ICLK,
+         Register    => Sideband.SBI_SSCDIVINTPHASE6,
+         Mask_Unset  => SBI_SSCDIVINTPHASE_DIVSEL_MASK or
+                        SBI_SSCDIVINTPHASE_INCVAL_MASK or
+                        SBI_SSCDIVINTPHASE_DIR_MASK,
+         Mask_Set    => SBI_SSCDIVINTPHASE_DIVSEL (Div_Sel) or
+                        SBI_SSCDIVINTPHASE_INCVAL (Phase_Inc) or
+                        SBI_SSCDIVINTPHASE_DIR (Phase_Dir) or
+                        SBI_SSCDIVINTPHASE_PROPAGATE);
+
+      Sideband.Unset_And_Set_Mask
+        (Dest        => Sideband.SBI_ICLK,
+         Register    => Sideband.SBI_SSCAUXDIV,
+         Mask_Unset  => SBI_SSCAUXDIV_FINALDIV2SEL_MASK,
+         Mask_Set    => SBI_SSCAUXDIV_FINALDIV2SEL (Aux_Div));
+
+      Sideband.Unset_Mask
+        (Dest     => Sideband.SBI_ICLK,
+         Register => Sideband.SBI_SSCCTL6,
+         Mask     => SBI_SSCCTL_DISABLE);
+
+      Registers.Write (Registers.PCH_PIXCLK_GATE, PCH_PIXCLK_GATE_UNGATE);
+   end Clock_On;
+
+end HW.GFX.GMA.PCH.VGA;
diff --git a/common/hw-gfx-gma-pch-vga.ads b/common/hw-gfx-gma-pch-vga.ads
new file mode 100644
index 0000000..95df5e5
--- /dev/null
+++ b/common/hw-gfx-gma-pch-vga.ads
@@ -0,0 +1,24 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.PCH.VGA is
+
+   procedure On
+     (Port : FDI_Port_Type;
+      Mode : Mode_Type);
+
+   procedure Off;
+
+   procedure Clock_On (Mode : Mode_Type);
+
+end HW.GFX.GMA.PCH.VGA;
diff --git a/common/hw-gfx-gma-pch.ads b/common/hw-gfx-gma-pch.ads
new file mode 100644
index 0000000..5826c1d
--- /dev/null
+++ b/common/hw-gfx-gma-pch.ads
@@ -0,0 +1,42 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Config;
+
+private package HW.GFX.GMA.PCH is
+
+   type FDI_Port_Type is (FDI_A, FDI_B, FDI_C);
+
+   ----------------------------------------------------------------------------
+
+   -- common to all PCH outputs
+
+   PCH_TRANSCODER_SELECT_SHIFT : constant :=
+     (case Config.CPU is
+         when Ironlake                       => 30,
+         when Sandybridge | Ivybridge        => 29,
+         when Haswell | Broadwell | Skylake  =>  0);
+
+   PCH_TRANSCODER_SELECT_MASK : constant :=
+     (case Config.CPU is
+         when Ironlake                       => 1 * 2 ** 30,
+         when Sandybridge | Ivybridge        => 3 * 2 ** 29,
+         when Haswell | Broadwell | Skylake  =>           0);
+
+   type PCH_TRANSCODER_SELECT_Array is array (FDI_Port_Type) of Word32;
+   PCH_TRANSCODER_SELECT : constant PCH_TRANSCODER_SELECT_Array :=
+     (FDI_A => 0 * 2 ** PCH_TRANSCODER_SELECT_SHIFT,
+      FDI_B => 1 * 2 ** PCH_TRANSCODER_SELECT_SHIFT,
+      FDI_C => 2 * 2 ** PCH_TRANSCODER_SELECT_SHIFT);
+
+end HW.GFX.GMA.PCH;
diff --git a/common/hw-gfx-gma-pipe_setup.adb b/common/hw-gfx-gma-pipe_setup.adb
new file mode 100644
index 0000000..1315c78
--- /dev/null
+++ b/common/hw-gfx-gma-pipe_setup.adb
@@ -0,0 +1,610 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.Registers;
+
+use type HW.Word64;
+use type HW.Pos16;
+use type HW.Int32;
+use type HW.GFX.GMA.Registers.Registers_Invalid_Index;
+
+package body HW.GFX.GMA.Pipe_Setup is
+
+   DSPCNTR_ENABLE               : constant :=  1 * 2 ** 31;
+   DSPCNTR_GAMMA_CORRECTION     : constant :=  1 * 2 ** 30;
+   DSPCNTR_DISABLE_TRICKLE_FEED : constant :=  1 * 2 ** 14;
+   DSPCNTR_FORMAT_MASK          : constant := 15 * 2 ** 26;
+
+   DSPCNTR_MASK : constant Word32 :=
+      DSPCNTR_ENABLE or
+      DSPCNTR_GAMMA_CORRECTION or
+      DSPCNTR_FORMAT_MASK or
+      DSPCNTR_DISABLE_TRICKLE_FEED;
+
+   PLANE_CTL_PLANE_ENABLE              : constant := 1 * 2 ** 31;
+   PLANE_CTL_SRC_PIX_FMT_RGB_32B_8888  : constant := 4 * 2 ** 24;
+   PLANE_CTL_PLANE_GAMMA_DISABLE       : constant := 1 * 2 ** 13;
+
+   PLANE_WM_ENABLE                     : constant :=        1 * 2 ** 31;
+   PLANE_WM_LINES_SHIFT                : constant :=                 14;
+   PLANE_WM_LINES_MASK                 : constant := 16#001f# * 2 ** 14;
+   PLANE_WM_BLOCKS_MASK                : constant := 16#03ff# * 2 **  0;
+
+   SPCNTR_ENABLE : constant :=  1 * 2 ** 31;
+
+   TRANS_CLK_SEL_PORT_NONE : constant := 0 * 2 ** 29;
+
+   type TRANS_CLK_SEL_PORT_Array is
+      array (Digital_Port) of Word32;
+   TRANS_CLK_SEL_PORT : constant TRANS_CLK_SEL_PORT_Array :=
+      TRANS_CLK_SEL_PORT_Array'
+     (DIGI_A => 0 * 2 ** 29,   -- DDI A is not selectable
+      DIGI_B => 2 * 2 ** 29,
+      DIGI_C => 3 * 2 ** 29,
+      DIGI_D => 4 * 2 ** 29,
+      DIGI_E => 5 * 2 ** 29);
+
+   PIPECONF_ENABLE          : constant := 1 * 2 ** 31;
+   PIPECONF_ENABLED_STATUS  : constant := 1 * 2 ** 30;
+   PIPECONF_ENABLE_DITHER   : constant := 1 * 2 **  4;
+   PIPECONF_DITHER_TEMPORAL : constant := 1 * 2 **  2;
+
+   PF_CTL_1_ENABLE : constant Word32 := 1 * 2 ** 31;
+
+   PS_CTRL_ENABLE_SCALER               : constant Word32 := 1 * 2 ** 31;
+   PS_CTRL_SCALER_MODE_7X5_EXTENDED    : constant Word32 := 1 * 2 ** 28;
+   PS_CTRL_FILTER_SELECT_MEDIUM_2      : constant Word32 := 1 * 2 ** 23;
+
+   PIPE_DDI_FUNC_CTL_ENABLE               : constant := 1 * 2 ** 31;
+   PIPE_DDI_FUNC_CTL_DDI_SELECT_MASK      : constant := 7 * 2 ** 28;
+   PIPE_DDI_FUNC_CTL_DDI_SELECT_NONE      : constant := 0 * 2 ** 28;
+   PIPE_DDI_FUNC_CTL_DDI_SELECT_B         : constant := 1 * 2 ** 28;
+   PIPE_DDI_FUNC_CTL_DDI_SELECT_C         : constant := 2 * 2 ** 28;
+   PIPE_DDI_FUNC_CTL_DDI_SELECT_D         : constant := 3 * 2 ** 28;
+   PIPE_DDI_FUNC_CTL_DDI_SELECT_E         : constant := 4 * 2 ** 28;
+   PIPE_DDI_FUNC_CTL_MODE_SELECT_MASK     : constant := 7 * 2 ** 24;
+   PIPE_DDI_FUNC_CTL_MODE_SELECT_HDMI     : constant := 0 * 2 ** 24;
+   PIPE_DDI_FUNC_CTL_MODE_SELECT_DVI      : constant := 1 * 2 ** 24;
+   PIPE_DDI_FUNC_CTL_MODE_SELECT_DP_SST   : constant := 2 * 2 ** 24;
+   PIPE_DDI_FUNC_CTL_MODE_SELECT_DP_MST   : constant := 3 * 2 ** 24;
+   PIPE_DDI_FUNC_CTL_MODE_SELECT_FDI      : constant := 4 * 2 ** 24;
+   PIPE_DDI_FUNC_CTL_BPC_MASK             : constant := 7 * 2 ** 20;
+   PIPE_DDI_FUNC_CTL_BPC_8BITS            : constant := 0 * 2 ** 20;
+   PIPE_DDI_FUNC_CTL_BPC_10BITS           : constant := 1 * 2 ** 20;
+   PIPE_DDI_FUNC_CTL_BPC_6BITS            : constant := 2 * 2 ** 20;
+   PIPE_DDI_FUNC_CTL_BPC_12BITS           : constant := 3 * 2 ** 20;
+   PIPE_DDI_FUNC_CTL_VSYNC_ACTIVE_LOW     : constant := 0 * 2 ** 17;
+   PIPE_DDI_FUNC_CTL_VSYNC_ACTIVE_HIGH    : constant := 1 * 2 ** 17;
+   PIPE_DDI_FUNC_CTL_HSYNC_ACTIVE_LOW     : constant := 0 * 2 ** 16;
+   PIPE_DDI_FUNC_CTL_HSYNC_ACTIVE_HIGH    : constant := 1 * 2 ** 16;
+   PIPE_DDI_FUNC_CTL_EDP_SELECT_MASK      : constant := 7 * 2 ** 12;
+   PIPE_DDI_FUNC_CTL_EDP_SELECT_ALWAYS_ON : constant := 0 * 2 ** 12;
+   PIPE_DDI_FUNC_CTL_EDP_SELECT_A         : constant := 4 * 2 ** 12;
+   PIPE_DDI_FUNC_CTL_EDP_SELECT_B         : constant := 5 * 2 ** 12;
+   PIPE_DDI_FUNC_CTL_EDP_SELECT_C         : constant := 6 * 2 ** 12;
+   PIPE_DDI_FUNC_CTL_DP_VC_PAYLOAD_ALLOC  : constant := 1 * 2 **  8;
+   PIPE_DDI_FUNC_CTL_BFI_ENABLE           : constant := 1 * 2 **  4;
+   PIPE_DDI_FUNC_CTL_PORT_WIDTH_MASK      : constant := 7 * 2 **  1;
+   PIPE_DDI_FUNC_CTL_PORT_WIDTH_1_LANE    : constant := 0 * 2 **  1;
+   PIPE_DDI_FUNC_CTL_PORT_WIDTH_2_LANES   : constant := 1 * 2 **  1;
+   PIPE_DDI_FUNC_CTL_PORT_WIDTH_4_LANES   : constant := 3 * 2 **  1;
+
+   type DDI_Select_Array is array (Digital_Port) of Word32;
+   PIPE_DDI_FUNC_CTL_DDI_SELECT : constant DDI_Select_Array :=
+      DDI_Select_Array'
+     (DIGI_A => PIPE_DDI_FUNC_CTL_DDI_SELECT_NONE,
+      DIGI_B => PIPE_DDI_FUNC_CTL_DDI_SELECT_B,
+      DIGI_C => PIPE_DDI_FUNC_CTL_DDI_SELECT_C,
+      DIGI_D => PIPE_DDI_FUNC_CTL_DDI_SELECT_D,
+      DIGI_E => PIPE_DDI_FUNC_CTL_DDI_SELECT_E);
+
+   type DDI_Mode_Array is array (Display_Type) of Word32;
+   PIPE_DDI_FUNC_CTL_MODE_SELECT : constant DDI_Mode_Array :=
+      DDI_Mode_Array'
+     (VGA      => PIPE_DDI_FUNC_CTL_MODE_SELECT_FDI,
+      HDMI     => PIPE_DDI_FUNC_CTL_MODE_SELECT_DVI,
+      DP       => PIPE_DDI_FUNC_CTL_MODE_SELECT_DP_SST,
+      others   => 0);
+
+   type HV_Sync_Array is array (Boolean) of Word32;
+   PIPE_DDI_FUNC_CTL_VSYNC : constant HV_Sync_Array := HV_Sync_Array'
+     (False => PIPE_DDI_FUNC_CTL_VSYNC_ACTIVE_LOW,
+      True  => PIPE_DDI_FUNC_CTL_VSYNC_ACTIVE_HIGH);
+   PIPE_DDI_FUNC_CTL_HSYNC : constant HV_Sync_Array := HV_Sync_Array'
+     (False => PIPE_DDI_FUNC_CTL_HSYNC_ACTIVE_LOW,
+      True  => PIPE_DDI_FUNC_CTL_HSYNC_ACTIVE_HIGH);
+
+   type EDP_Select_Array is array (Controller_Kind) of Word32;
+   PIPE_DDI_FUNC_CTL_EDP_SELECT : constant EDP_Select_Array :=
+      EDP_Select_Array'
+     (A => PIPE_DDI_FUNC_CTL_EDP_SELECT_ALWAYS_ON, -- we never use panel fitter
+      B => PIPE_DDI_FUNC_CTL_EDP_SELECT_B,
+      C => PIPE_DDI_FUNC_CTL_EDP_SELECT_C);
+   PIPE_DDI_FUNC_CTL_EDP_SELECT_ONOFF : constant EDP_Select_Array :=
+      EDP_Select_Array'
+     (A => PIPE_DDI_FUNC_CTL_EDP_SELECT_A,
+      B => PIPE_DDI_FUNC_CTL_EDP_SELECT_B,
+      C => PIPE_DDI_FUNC_CTL_EDP_SELECT_C);
+
+   type Port_Width_Array is array (HW.GFX.DP_Lane_Count) of Word32;
+   PIPE_DDI_FUNC_CTL_PORT_WIDTH : constant Port_Width_Array :=
+      Port_Width_Array'
+     (HW.GFX.DP_Lane_Count_1 => PIPE_DDI_FUNC_CTL_PORT_WIDTH_1_LANE,
+      HW.GFX.DP_Lane_Count_2 => PIPE_DDI_FUNC_CTL_PORT_WIDTH_2_LANES,
+      HW.GFX.DP_Lane_Count_4 => PIPE_DDI_FUNC_CTL_PORT_WIDTH_4_LANES);
+
+   function PIPE_DDI_FUNC_CTL_BPC (BPC : HW.GFX.BPC_Type) return Word32
+   is
+      Result : Word32;
+   begin
+      case BPC is
+         when      6 => Result := PIPE_DDI_FUNC_CTL_BPC_6BITS;
+         when      8 => Result := PIPE_DDI_FUNC_CTL_BPC_8BITS;
+         when     10 => Result := PIPE_DDI_FUNC_CTL_BPC_10BITS;
+         when     12 => Result := PIPE_DDI_FUNC_CTL_BPC_12BITS;
+         when others => Result := PIPE_DDI_FUNC_CTL_BPC_8BITS;
+      end case;
+      return Result;
+   end PIPE_DDI_FUNC_CTL_BPC;
+
+   function PIPE_DATA_M_TU (Transfer_Unit : Positive) return Word32 is
+   begin
+      return Shift_Left (Word32 (Transfer_Unit - 1), 25);
+   end PIPE_DATA_M_TU;
+
+   PIPE_MSA_MISC_SYNC_CLK     : constant := 1 * 2 ** 0;
+   PIPE_MSA_MISC_BPC_6BITS    : constant := 0 * 2 ** 5;
+   PIPE_MSA_MISC_BPC_8BITS    : constant := 1 * 2 ** 5;
+   PIPE_MSA_MISC_BPC_10BITS   : constant := 2 * 2 ** 5;
+   PIPE_MSA_MISC_BPC_12BITS   : constant := 3 * 2 ** 5;
+   PIPE_MSA_MISC_BPC_16BITS   : constant := 4 * 2 ** 5;
+
+   function PIPE_MSA_MISC_BPC (BPC : HW.GFX.BPC_Type) return Word32 is
+      Result : Word32;
+   begin
+      case BPC is
+         when      6 => Result := PIPE_MSA_MISC_BPC_6BITS;
+         when      8 => Result := PIPE_MSA_MISC_BPC_8BITS;
+         when     10 => Result := PIPE_MSA_MISC_BPC_10BITS;
+         when     12 => Result := PIPE_MSA_MISC_BPC_12BITS;
+         --when     16 => Result := PIPE_MSA_MISC_BPC_16BITS;
+         when others => Result := PIPE_MSA_MISC_BPC_8BITS;
+      end case;
+      return Result;
+   end PIPE_MSA_MISC_BPC;
+
+   ---------------------------------------------------------------------------
+
+   function PIPECONF_BPC_MAP (Bits_Per_Color : HW.GFX.BPC_Type) return Word32
+   is
+      Result : Word32;
+   begin
+      if    Bits_Per_Color = 6 then
+         Result := 2 * 2 ** 5;
+      elsif Bits_Per_Color = 10 then
+         Result := 1 * 2 ** 5;
+      elsif Bits_Per_Color = 12 then
+         Result := 3 * 2 ** 5;
+      else
+         Result := 0;
+      end if;
+      return Result;
+   end PIPECONF_BPC_MAP;
+
+   ---------------------------------------------------------------------------
+
+   function PLANE_WM_LINES (Lines : Natural) return Word32 is
+   begin
+      return Shift_Left (Word32 (Lines), PLANE_WM_LINES_SHIFT)
+               and PLANE_WM_LINES_MASK;
+   end PLANE_WM_LINES;
+
+   function PLANE_WM_BLOCKS (Blocks : Natural) return Word32 is
+   begin
+      return Word32 (Blocks) and PLANE_WM_BLOCKS_MASK;
+   end PLANE_WM_BLOCKS;
+
+   ---------------------------------------------------------------------------
+
+   function Encode (LSW, MSW : Pos16) return Word32 is
+   begin
+      return Shift_Left (Word32 (MSW - 1), 16) or Word32 (LSW - 1);
+   end Encode;
+
+   ----------------------------------------------------------------------------
+
+   procedure Setup_Link
+     (Head  : Head_Type;
+      Link  : DP_Link;
+      Mode  : Mode_Type)
+   with
+      Global => (In_Out => Registers.Register_State),
+      Depends => (Registers.Register_State =>+ (Head, Link, Mode))
+   is
+      Data_M, Link_M : DP_Info.M_Type;
+      Data_N, Link_N : DP_Info.N_Type;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      DP_Info.Calculate_M_N
+        (Link     => Link,
+         Mode     => Mode,
+         Data_M   => Data_M,
+         Data_N   => Data_N,
+         Link_M   => Link_M,
+         Link_N   => Link_N);
+
+      Registers.Write
+        (Register => Head.PIPE_DATA_M1,
+         Value    => PIPE_DATA_M_TU (64) or
+                     Word32 (Data_M));
+      Registers.Write
+        (Register => Head.PIPE_DATA_N1,
+         Value    => Word32 (Data_N));
+
+      Registers.Write
+        (Register => Head.PIPE_LINK_M1,
+         Value    => Word32 (Link_M));
+      Registers.Write
+        (Register => Head.PIPE_LINK_N1,
+         Value    => Word32 (Link_N));
+
+      if Config.Has_Pipe_MSA_Misc then
+         Registers.Write
+           (Register => Head.PIPE_MSA_MISC,
+            Value    => PIPE_MSA_MISC_SYNC_CLK or
+                        PIPE_MSA_MISC_BPC (Mode.BPC));
+      end if;
+   end Setup_Link;
+
+   ----------------------------------------------------------------------------
+
+   procedure Clear_Watermarks (Controller : Controller_Type) is
+   begin
+      Registers.Write
+        (Register    => Controller.PLANE_BUF_CFG,
+         Value       => 16#0000_0000#);
+      for Level in WM_Levels range 0 .. WM_Levels'Last loop
+         Registers.Write
+           (Register => Controller.PLANE_WM (Level),
+            Value    => 16#0000_0000#);
+      end loop;
+      Registers.Write
+        (Register    => Controller.WM_LINETIME,
+         Value       => 16#0000_0000#);
+   end Clear_Watermarks;
+
+   procedure Setup_Watermarks (Controller : Controller_Type)
+   is
+      type Per_Plane_Buffer_Range is array (Controller_Kind) of Word32;
+      Buffer_Range : constant Per_Plane_Buffer_Range := Per_Plane_Buffer_Range'
+        (A  => Shift_Left (159, 16) or   0,
+         B  => Shift_Left (319, 16) or 160,
+         C  => Shift_Left (479, 16) or 320);
+   begin
+      Registers.Write
+        (Register    => Controller.PLANE_BUF_CFG,
+         Value       => Buffer_Range (Controller.Kind));
+      Registers.Write
+        (Register    => Controller.PLANE_WM (0),
+         Value       => PLANE_WM_ENABLE or
+                        PLANE_WM_LINES (2) or
+                        PLANE_WM_BLOCKS (160));
+   end Setup_Watermarks;
+
+   ----------------------------------------------------------------------------
+
+   procedure Setup_Display
+     (Controller  : in     Controller_Type;
+      Head        : in     Head_Type;
+      Mode        : in     HW.GFX.Mode_Type;
+      Framebuffer : in     HW.GFX.Framebuffer_Type)
+   with
+      Global => (In_Out => Registers.Register_State),
+      Depends =>
+        (Registers.Register_State
+            =>+
+              (Registers.Register_State,
+               Controller,
+               Head,
+               Mode,
+               Framebuffer))
+   is
+      -- FIXME: setup correct format, based on framebuffer RGB format
+      Format : constant Word32 := 6 * 2 ** 26;
+      PRI : Word32 := DSPCNTR_ENABLE or Format;
+
+      function To_Bytes (Pixels : Width_Type) return Word32
+      with
+         Pre => (Word32 (Pixels) <= Word32'Last / 4 / Word32 (BPC_Type'Last) * 8)
+      is
+      begin
+         return Word32 (Pos64 (Pixels) * 4 * Framebuffer.BPC / 8);
+      end To_Bytes;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Write (Controller.PIPESRC, Encode (Mode.V_Visible, Mode.H_Visible));
+
+      if Config.Has_Plane_Control then
+         Setup_Watermarks (Controller);
+         Registers.Write
+           (Register    => Controller.PLANE_CTL,
+            Value       => PLANE_CTL_PLANE_ENABLE or
+                           PLANE_CTL_SRC_PIX_FMT_RGB_32B_8888 or
+                           PLANE_CTL_PLANE_GAMMA_DISABLE);
+         Registers.Write (Controller.PLANE_OFFSET, 16#0000_0000#);
+         Registers.Write (Controller.PLANE_SIZE,   Encode (Mode.H_Visible, Mode.V_Visible));
+         Registers.Write (Controller.PLANE_STRIDE, To_Bytes (Framebuffer.Stride) / 64);
+         Registers.Write (Controller.PLANE_POS,    16#0000_0000#);
+         Registers.Write (Controller.PLANE_SURF,   Framebuffer.Offset and 16#ffff_f000#);
+      else
+         if Config.Disable_Trickle_Feed then
+            PRI := PRI or DSPCNTR_DISABLE_TRICKLE_FEED;
+         end if;
+         -- for now, just disable gamma LUT (can't do anything
+         -- useful without colorimetry information from display)
+         Registers.Unset_And_Set_Mask
+            (Register   => Controller.DSPCNTR,
+             Mask_Unset => DSPCNTR_MASK,
+             Mask_Set   => PRI);
+
+         Registers.Write (Controller.DSPSTRIDE, To_Bytes (Framebuffer.Stride));
+         Registers.Write (Controller.DSPSURF, Framebuffer.Offset and 16#ffff_f000#);
+         if Config.Has_DSP_Linoff then
+            Registers.Write (Controller.DSPLINOFF, 0);
+         end if;
+         Registers.Write (Controller.DSPTILEOFF, 0);
+      end if;
+
+      Registers.Write (Head.HTOTAL,  Encode (Mode.H_Visible,    Mode.H_Total));
+      Registers.Write (Head.HBLANK,  Encode (Mode.H_Visible,    Mode.H_Total));
+      Registers.Write (Head.HSYNC,   Encode (Mode.H_Sync_Begin, Mode.H_Sync_End));
+      Registers.Write (Head.VTOTAL,  Encode (Mode.V_Visible,    Mode.V_Total));
+      Registers.Write (Head.VBLANK,  Encode (Mode.V_Visible,    Mode.V_Total));
+      Registers.Write (Head.VSYNC,   Encode (Mode.V_Sync_Begin, Mode.V_Sync_End));
+   end Setup_Display;
+
+   ----------------------------------------------------------------------------
+
+   procedure Setup_Head
+     (Controller  : Controller_Type;
+      Head        : Head_Type;
+      Port_Cfg    : Port_Config;
+      Framebuffer : Framebuffer_Type)
+   is
+      PIPECONF_Options : Word32 := 0;
+   begin
+      if Config.Has_Pipe_DDI_Func then
+         Registers.Write
+           (Register => Head.PIPE_DDI_FUNC_CTL,
+            Value    => PIPE_DDI_FUNC_CTL_ENABLE or
+                        PIPE_DDI_FUNC_CTL_DDI_SELECT (Port_Cfg.Port) or
+                        PIPE_DDI_FUNC_CTL_MODE_SELECT (Port_Cfg.Display) or
+                        PIPE_DDI_FUNC_CTL_BPC (Port_Cfg.Mode.BPC) or
+                        PIPE_DDI_FUNC_CTL_VSYNC (Port_Cfg.Mode.V_Sync_Active_High) or
+                        PIPE_DDI_FUNC_CTL_HSYNC (Port_Cfg.Mode.H_Sync_Active_High) or
+                        PIPE_DDI_FUNC_CTL_EDP_SELECT (Controller.Kind) or
+                        PIPE_DDI_FUNC_CTL_PORT_WIDTH (Port_Cfg.DP.Lane_Count));
+      end if;
+
+      if Config.Has_Pipeconf_BPC then
+         PIPECONF_Options := PIPECONF_BPC_MAP (Port_Cfg.Mode.BPC);
+      end if;
+
+      -- Enable dithering if framebuffer BPC differs from connector BPC,
+      -- as smooth gradients look really bad without
+      if Framebuffer.BPC /= Port_Cfg.Mode.BPC then
+         PIPECONF_Options := PIPECONF_Options or PIPECONF_ENABLE_DITHER;
+      end if;
+
+      if not Config.Has_Pipeconf_Misc then
+         Registers.Write
+           (Register => Head.PIPECONF,
+            Value    => PIPECONF_ENABLE or PIPECONF_Options);
+      else
+         Registers.Write
+           (Register => Controller.PIPEMISC,
+            Value    => PIPECONF_Options);
+         Registers.Write
+           (Register => Head.PIPECONF,
+            Value    => PIPECONF_ENABLE);
+      end if;
+      Registers.Posting_Read (Head.PIPECONF);
+   end Setup_Head;
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (Controller  : Controller_Type;
+      Head        : Head_Type;
+      Port_Cfg    : Port_Config;
+      Framebuffer : Framebuffer_Type)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.Has_Trans_Clk_Sel then
+         Registers.Write
+           (Register => Controller.TRANS_CLK_SEL,
+            Value    => TRANS_CLK_SEL_PORT (Port_Cfg.Port));
+      end if;
+
+      if Port_Cfg.Is_FDI then
+         Setup_Link (Head, Port_Cfg.FDI, Port_Cfg.Mode);
+      elsif Port_Cfg.Display = DP then
+         Setup_Link (Head, Port_Cfg.DP, Port_Cfg.Mode);
+      end if;
+
+      Setup_Display (Controller, Head, Port_Cfg.Mode, Framebuffer);
+
+      Setup_Head (Controller, Head, Port_Cfg, Framebuffer);
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Planes_Off (Controller : Controller_Type) is
+   begin
+      Registers.Unset_Mask (Controller.SPCNTR, SPCNTR_ENABLE);
+      if Config.Has_Plane_Control then
+         Clear_Watermarks (Controller);
+         Registers.Unset_Mask (Controller.PLANE_CTL, PLANE_CTL_PLANE_ENABLE);
+         Registers.Write (Controller.PLANE_SURF, 16#0000_0000#);
+      else
+         Registers.Unset_Mask (Controller.DSPCNTR, DSPCNTR_ENABLE);
+      end if;
+   end Planes_Off;
+
+   procedure Head_Off (Head : Head_Type)
+   is
+      Enabled : Boolean;
+   begin
+      Registers.Is_Set_Mask (Head.PIPECONF, PIPECONF_ENABLE, Enabled);
+
+      if Enabled then
+         Registers.Unset_Mask (Head.PIPECONF, PIPECONF_ENABLE);
+      end if;
+
+      -- Workaround for Broadwell:
+      -- Status may be wrong if pipe hasn't been enabled since reset.
+      if not Config.Pipe_Enabled_Workaround or else Enabled then
+         -- synchronously wait until pipe is truly off
+         Registers.Wait_Unset_Mask
+           (Register => Head.PIPECONF,
+            Mask     => PIPECONF_ENABLED_STATUS,
+            TOut_MS  => 40);
+      end if;
+
+      if Config.Has_Pipe_DDI_Func then
+         Registers.Write (Head.PIPE_DDI_FUNC_CTL, 0);
+      end if;
+   end Head_Off;
+
+   procedure Panel_Fitter_Off (Controller : Controller_Type) is
+   begin
+      -- Writes to WIN_SZ arm the PS/PF registers.
+      if Config.Has_Plane_Control then
+         Registers.Unset_Mask (Controller.PS_CTRL_1, PS_CTRL_ENABLE_SCALER);
+         Registers.Write (Controller.PS_WIN_SZ_1, 16#0000_0000#);
+         if Controller.PS_CTRL_2 /= Registers.Invalid_Register and
+            Controller.PS_WIN_SZ_2 /= Registers.Invalid_Register
+         then
+            Registers.Unset_Mask (Controller.PS_CTRL_2, PS_CTRL_ENABLE_SCALER);
+            Registers.Write (Controller.PS_WIN_SZ_2, 16#0000_0000#);
+         end if;
+      else
+         Registers.Unset_Mask (Controller.PF_CTL_1, PF_CTL_1_ENABLE);
+         Registers.Write (Controller.PF_WIN_SZ, 16#0000_0000#);
+      end if;
+   end Panel_Fitter_Off;
+
+   procedure Trans_Clk_Off (Controller : Controller_Type) is
+   begin
+      if Config.Has_Trans_Clk_Sel then
+         Registers.Write (Controller.TRANS_CLK_SEL, TRANS_CLK_SEL_PORT_NONE);
+      end if;
+   end Trans_Clk_Off;
+
+   procedure Off (Controller : Controller_Type; Head : Head_Type) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Planes_Off (Controller);
+      Head_Off (Head);
+      Panel_Fitter_Off (Controller);
+      Trans_Clk_Off (Controller);
+   end Off;
+
+   procedure All_Off
+   is
+      EDP_Enabled, EDP_Piped : Boolean;
+
+      procedure EDP_Piped_To (Kind : Controller_Kind; Piped_To : out Boolean)
+      is
+         Pipe_DDI_Func_Ctl : Word32;
+      begin
+         Registers.Read (Registers.PIPE_EDP_DDI_FUNC_CTL, Pipe_DDI_Func_Ctl);
+         Pipe_DDI_Func_Ctl :=
+            Pipe_DDI_Func_Ctl and PIPE_DDI_FUNC_CTL_EDP_SELECT_MASK;
+
+         Piped_To := (Kind = A and Pipe_DDI_Func_Ctl = PIPE_DDI_FUNC_CTL_EDP_SELECT_ALWAYS_ON) or
+                     Pipe_DDI_Func_Ctl = PIPE_DDI_FUNC_CTL_EDP_SELECT_ONOFF (Kind);
+      end EDP_Piped_To;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.Has_EDP_Pipe then
+         Registers.Is_Set_Mask
+           (Registers.PIPE_EDP_CONF, PIPECONF_ENABLE, EDP_Enabled);
+      else
+         EDP_Enabled := False;
+      end if;
+
+      for Kind in Controller_Kind loop
+         Planes_Off (Controllers (Kind));
+         if EDP_Enabled then
+            EDP_Piped_To (Kind, EDP_Piped);
+            if EDP_Piped then
+               Head_Off (Heads (Head_EDP));
+               EDP_Enabled := False;
+            end if;
+         end if;
+         Head_Off (Default_Pipe_Head (Kind));
+         Panel_Fitter_Off (Controllers (Kind));
+         Trans_Clk_Off (Controllers (Kind));
+      end loop;
+
+      if EDP_Enabled then
+         Head_Off (Heads (Head_EDP));
+      end if;
+   end All_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Update_Offset
+     (Controller  : Controller_Type;
+      Framebuffer : HW.GFX.Framebuffer_Type) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Write (Controller.DSPSURF, Framebuffer.Offset and 16#ffff_f000#);
+   end Update_Offset;
+
+   ----------------------------------------------------------------------------
+
+   function Get_Pipe_Hint (Head : Head_Type) return Word32
+   is
+      type Pipe_Hint_Array is array (Pipe_Head) of Word32;
+      Pipe_Hint : constant Pipe_Hint_Array := Pipe_Hint_Array'
+        (Head_EDP => 0, Head_A => 0, Head_B => 1, Head_C => 2);
+   begin
+      return Pipe_Hint (Head.Head);
+   end Get_Pipe_Hint;
+
+   ----------------------------------------------------------------------------
+
+   function Default_Pipe_Head (Kind : Controller_Kind) return Head_Type
+   is
+      type Default_Head_Array is array (Controller_Kind) of Head_Type;
+      Default_Head : constant Default_Head_Array := Default_Head_Array'
+        (A => Heads (Head_A), B => Heads (Head_B), C => Heads (Head_C));
+   begin
+      return Default_Head (Kind);
+   end Default_Pipe_Head;
+
+end HW.GFX.GMA.Pipe_Setup;
diff --git a/common/hw-gfx-gma-pipe_setup.ads b/common/hw-gfx-gma-pipe_setup.ads
new file mode 100644
index 0000000..416cf01
--- /dev/null
+++ b/common/hw-gfx-gma-pipe_setup.ads
@@ -0,0 +1,274 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Registers;
+
+private package HW.GFX.GMA.Pipe_Setup
+is
+
+   type Controller_Kind is (A, B, C);
+   type Controller_Type is private;
+   type Controller_Array is array (Controller_Kind) of Controller_Type;
+
+   type Pipe_Head is (Head_EDP, Head_A, Head_B, Head_C);
+   type Head_Type is private;
+   type Head_Array is array (Pipe_Head) of Head_Type;
+
+   procedure On
+     (Controller  : Controller_Type;
+      Head        : Head_Type;
+      Port_Cfg    : Port_Config;
+      Framebuffer : Framebuffer_Type);
+
+   procedure Off
+     (Controller : Controller_Type;
+      Head       : Head_Type);
+
+   procedure All_Off;
+
+   function Get_Pipe_Hint (Head : Head_Type) return Word32;
+
+   procedure Update_Offset
+     (Controller  : Controller_Type;
+      Framebuffer : HW.GFX.Framebuffer_Type);
+
+   Controllers : constant Controller_Array;
+
+   Heads : constant Head_Array;
+
+   function Default_Pipe_Head (Kind : Controller_Kind) return Head_Type;
+
+private
+
+   subtype WM_Levels is Natural range 0 .. 7;
+   type PLANE_WM_Type is array (WM_Levels) of Registers.Registers_Index;
+
+   type Controller_Type is
+      record
+         Kind              : Controller_Kind;
+         PIPESRC           : Registers.Registers_Index;
+         PIPEMISC          : Registers.Registers_Index;
+         PF_CTL_1          : Registers.Registers_Index;
+         PF_WIN_POS        : Registers.Registers_Index;
+         PF_WIN_SZ         : Registers.Registers_Index;
+         DSPCNTR           : Registers.Registers_Index;
+         DSPLINOFF         : Registers.Registers_Index;
+         DSPSTRIDE         : Registers.Registers_Index;
+         DSPSURF           : Registers.Registers_Index;
+         DSPTILEOFF        : Registers.Registers_Index;
+         SPCNTR            : Registers.Registers_Index;
+         TRANS_CLK_SEL     : Registers.Registers_Index;
+         -- Skylake registers (partially aliased)
+         PLANE_CTL         : Registers.Registers_Index;
+         PLANE_OFFSET      : Registers.Registers_Index;
+         PLANE_POS         : Registers.Registers_Index;
+         PLANE_SIZE        : Registers.Registers_Index;
+         PLANE_STRIDE      : Registers.Registers_Index;
+         PLANE_SURF        : Registers.Registers_Index;
+         PS_CTRL_1         : Registers.Registers_Index;
+         PS_CTRL_2         : Registers.Registers_Invalid_Index;
+         PS_WIN_SZ_1       : Registers.Registers_Index;
+         PS_WIN_SZ_2       : Registers.Registers_Invalid_Index;
+         WM_LINETIME       : Registers.Registers_Index;
+         PLANE_BUF_CFG     : Registers.Registers_Index;
+         PLANE_WM          : PLANE_WM_Type;
+      end record;
+
+   type Head_Type is
+      record
+         Head              : Pipe_Head;
+         HTOTAL            : Registers.Registers_Index;
+         HBLANK            : Registers.Registers_Index;
+         HSYNC             : Registers.Registers_Index;
+         VTOTAL            : Registers.Registers_Index;
+         VBLANK            : Registers.Registers_Index;
+         VSYNC             : Registers.Registers_Index;
+         PIPECONF          : Registers.Registers_Index;
+         PIPE_DATA_M1      : Registers.Registers_Index;
+         PIPE_DATA_N1      : Registers.Registers_Index;
+         PIPE_LINK_M1      : Registers.Registers_Index;
+         PIPE_LINK_N1      : Registers.Registers_Index;
+         PIPE_DDI_FUNC_CTL : Registers.Registers_Index;
+         PIPE_MSA_MISC     : Registers.Registers_Index;
+      end record;
+
+   Controllers : constant Controller_Array := Controller_Array'
+     (A => Controller_Type'
+        (Kind              => A,
+         PIPESRC           => Registers.PIPEASRC,
+         PIPEMISC          => Registers.PIPEAMISC,
+         PF_CTL_1          => Registers.PFA_CTL_1,
+         PF_WIN_POS        => Registers.PFA_WIN_POS,
+         PF_WIN_SZ         => Registers.PFA_WIN_SZ,
+         DSPCNTR           => Registers.DSPACNTR,
+         DSPLINOFF         => Registers.DSPALINOFF,
+         DSPSTRIDE         => Registers.DSPASTRIDE,
+         DSPSURF           => Registers.DSPASURF,
+         DSPTILEOFF        => Registers.DSPATILEOFF,
+         SPCNTR            => Registers.SPACNTR,
+         TRANS_CLK_SEL     => Registers.TRANSA_CLK_SEL,
+         PLANE_CTL         => Registers.DSPACNTR,
+         PLANE_OFFSET      => Registers.DSPATILEOFF,
+         PLANE_POS         => Registers.PLANE_POS_1_A,
+         PLANE_SIZE        => Registers.PLANE_SIZE_1_A,
+         PLANE_STRIDE      => Registers.DSPASTRIDE,
+         PLANE_SURF        => Registers.DSPASURF,
+         PS_CTRL_1         => Registers.PS_CTRL_1_A,
+         PS_CTRL_2         => Registers.PS_CTRL_2_A,
+         PS_WIN_SZ_1       => Registers.PS_WIN_SZ_1_A,
+         PS_WIN_SZ_2       => Registers.PS_WIN_SZ_2_A,
+         WM_LINETIME       => Registers.WM_LINETIME_A,
+         PLANE_BUF_CFG     => Registers.PLANE_BUF_CFG_1_A,
+         PLANE_WM          => PLANE_WM_Type'(
+                              Registers.PLANE_WM_1_A_0,
+                              Registers.PLANE_WM_1_A_1,
+                              Registers.PLANE_WM_1_A_2,
+                              Registers.PLANE_WM_1_A_3,
+                              Registers.PLANE_WM_1_A_4,
+                              Registers.PLANE_WM_1_A_5,
+                              Registers.PLANE_WM_1_A_6,
+                              Registers.PLANE_WM_1_A_7)),
+      B => Controller_Type'
+        (Kind              => B,
+         PIPESRC           => Registers.PIPEBSRC,
+         PIPEMISC          => Registers.PIPEBMISC,
+         PF_CTL_1          => Registers.PFB_CTL_1,
+         PF_WIN_POS        => Registers.PFB_WIN_POS,
+         PF_WIN_SZ         => Registers.PFB_WIN_SZ,
+         DSPCNTR           => Registers.DSPBCNTR,
+         DSPLINOFF         => Registers.DSPBLINOFF,
+         DSPSTRIDE         => Registers.DSPBSTRIDE,
+         DSPSURF           => Registers.DSPBSURF,
+         DSPTILEOFF        => Registers.DSPBTILEOFF,
+         SPCNTR            => Registers.SPBCNTR,
+         TRANS_CLK_SEL     => Registers.TRANSB_CLK_SEL,
+         PLANE_CTL         => Registers.DSPBCNTR,
+         PLANE_OFFSET      => Registers.DSPBTILEOFF,
+         PLANE_POS         => Registers.PLANE_POS_1_B,
+         PLANE_SIZE        => Registers.PLANE_SIZE_1_B,
+         PLANE_STRIDE      => Registers.DSPBSTRIDE,
+         PLANE_SURF        => Registers.DSPBSURF,
+         PS_CTRL_1         => Registers.PS_CTRL_1_B,
+         PS_CTRL_2         => Registers.PS_CTRL_2_B,
+         PS_WIN_SZ_1       => Registers.PS_WIN_SZ_1_B,
+         PS_WIN_SZ_2       => Registers.PS_WIN_SZ_2_B,
+         WM_LINETIME       => Registers.WM_LINETIME_B,
+         PLANE_BUF_CFG     => Registers.PLANE_BUF_CFG_1_B,
+         PLANE_WM          => PLANE_WM_Type'(
+                              Registers.PLANE_WM_1_B_0,
+                              Registers.PLANE_WM_1_B_1,
+                              Registers.PLANE_WM_1_B_2,
+                              Registers.PLANE_WM_1_B_3,
+                              Registers.PLANE_WM_1_B_4,
+                              Registers.PLANE_WM_1_B_5,
+                              Registers.PLANE_WM_1_B_6,
+                              Registers.PLANE_WM_1_B_7)),
+      C => Controller_Type'
+        (Kind              => C,
+         PIPESRC           => Registers.PIPECSRC,
+         PIPEMISC          => Registers.PIPECMISC,
+         PF_CTL_1          => Registers.PFC_CTL_1,
+         PF_WIN_POS        => Registers.PFC_WIN_POS,
+         PF_WIN_SZ         => Registers.PFC_WIN_SZ,
+         DSPCNTR           => Registers.DSPCCNTR,
+         DSPLINOFF         => Registers.DSPCLINOFF,
+         DSPSTRIDE         => Registers.DSPCSTRIDE,
+         DSPSURF           => Registers.DSPCSURF,
+         DSPTILEOFF        => Registers.DSPCTILEOFF,
+         SPCNTR            => Registers.SPCCNTR,
+         TRANS_CLK_SEL     => Registers.TRANSC_CLK_SEL,
+         PLANE_CTL         => Registers.DSPCCNTR,
+         PLANE_OFFSET      => Registers.DSPCTILEOFF,
+         PLANE_POS         => Registers.PLANE_POS_1_C,
+         PLANE_SIZE        => Registers.PLANE_SIZE_1_C,
+         PLANE_STRIDE      => Registers.DSPCSTRIDE,
+         PLANE_SURF        => Registers.DSPCSURF,
+         PS_CTRL_1         => Registers.PS_CTRL_1_C,
+         PS_CTRL_2         => Registers.Invalid_Register,
+         PS_WIN_SZ_1       => Registers.PS_WIN_SZ_1_C,
+         PS_WIN_SZ_2       => Registers.Invalid_Register,
+         WM_LINETIME       => Registers.WM_LINETIME_C,
+         PLANE_BUF_CFG     => Registers.PLANE_BUF_CFG_1_C,
+         PLANE_WM          => PLANE_WM_Type'(
+                              Registers.PLANE_WM_1_C_0,
+                              Registers.PLANE_WM_1_C_1,
+                              Registers.PLANE_WM_1_C_2,
+                              Registers.PLANE_WM_1_C_3,
+                              Registers.PLANE_WM_1_C_4,
+                              Registers.PLANE_WM_1_C_5,
+                              Registers.PLANE_WM_1_C_6,
+                              Registers.PLANE_WM_1_C_7)));
+
+   Heads : constant Head_Array := Head_Array'
+     (Head_EDP => Head_Type'
+        (Head              => Head_EDP,
+         HTOTAL            => Registers.HTOTAL_EDP,
+         HBLANK            => Registers.HBLANK_EDP,
+         HSYNC             => Registers.HSYNC_EDP,
+         VTOTAL            => Registers.VTOTAL_EDP,
+         VBLANK            => Registers.VBLANK_EDP,
+         VSYNC             => Registers.VSYNC_EDP,
+         PIPECONF          => Registers.PIPE_EDP_CONF,
+         PIPE_DATA_M1      => Registers.PIPE_EDP_DATA_M1,
+         PIPE_DATA_N1      => Registers.PIPE_EDP_DATA_N1,
+         PIPE_LINK_M1      => Registers.PIPE_EDP_LINK_M1,
+         PIPE_LINK_N1      => Registers.PIPE_EDP_LINK_N1,
+         PIPE_DDI_FUNC_CTL => Registers.PIPE_EDP_DDI_FUNC_CTL,
+         PIPE_MSA_MISC     => Registers.PIPE_EDP_MSA_MISC),
+      Head_A => Head_Type'
+        (Head              => Head_A,
+         HTOTAL            => Registers.HTOTAL_A,
+         HBLANK            => Registers.HBLANK_A,
+         HSYNC             => Registers.HSYNC_A,
+         VTOTAL            => Registers.VTOTAL_A,
+         VBLANK            => Registers.VBLANK_A,
+         VSYNC             => Registers.VSYNC_A,
+         PIPECONF          => Registers.PIPEACONF,
+         PIPE_DATA_M1      => Registers.PIPEA_DATA_M1,
+         PIPE_DATA_N1      => Registers.PIPEA_DATA_N1,
+         PIPE_LINK_M1      => Registers.PIPEA_LINK_M1,
+         PIPE_LINK_N1      => Registers.PIPEA_LINK_N1,
+         PIPE_DDI_FUNC_CTL => Registers.PIPEA_DDI_FUNC_CTL,
+         PIPE_MSA_MISC     => Registers.PIPEA_MSA_MISC),
+      Head_B => Head_Type'
+        (Head              => Head_B,
+         HTOTAL            => Registers.HTOTAL_B,
+         HBLANK            => Registers.HBLANK_B,
+         HSYNC             => Registers.HSYNC_B,
+         VTOTAL            => Registers.VTOTAL_B,
+         VBLANK            => Registers.VBLANK_B,
+         VSYNC             => Registers.VSYNC_B,
+         PIPECONF          => Registers.PIPEBCONF,
+         PIPE_DATA_M1      => Registers.PIPEB_DATA_M1,
+         PIPE_DATA_N1      => Registers.PIPEB_DATA_N1,
+         PIPE_LINK_M1      => Registers.PIPEB_LINK_M1,
+         PIPE_LINK_N1      => Registers.PIPEB_LINK_N1,
+         PIPE_DDI_FUNC_CTL => Registers.PIPEB_DDI_FUNC_CTL,
+         PIPE_MSA_MISC     => Registers.PIPEB_MSA_MISC),
+      Head_C => Head_Type'
+        (Head              => Head_C,
+         HTOTAL            => Registers.HTOTAL_C,
+         HBLANK            => Registers.HBLANK_C,
+         HSYNC             => Registers.HSYNC_C,
+         VTOTAL            => Registers.VTOTAL_C,
+         VBLANK            => Registers.VBLANK_C,
+         VSYNC             => Registers.VSYNC_C,
+         PIPECONF          => Registers.PIPECCONF,
+         PIPE_DATA_M1      => Registers.PIPEC_DATA_M1,
+         PIPE_DATA_N1      => Registers.PIPEC_DATA_N1,
+         PIPE_LINK_M1      => Registers.PIPEC_LINK_M1,
+         PIPE_LINK_N1      => Registers.PIPEC_LINK_N1,
+         PIPE_DDI_FUNC_CTL => Registers.PIPEC_DDI_FUNC_CTL,
+         PIPE_MSA_MISC     => Registers.PIPEC_MSA_MISC));
+
+end HW.GFX.GMA.Pipe_Setup;
diff --git a/common/hw-gfx-gma-port_detect.ads b/common/hw-gfx-gma-port_detect.ads
new file mode 100644
index 0000000..848d7ab
--- /dev/null
+++ b/common/hw-gfx-gma-port_detect.ads
@@ -0,0 +1,20 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Port_Detect is
+
+   procedure Initialize;
+
+   procedure Hotplug_Detect (Port_Cfg : in Port_Config; Detected : out Boolean);
+
+end HW.GFX.GMA.Port_Detect;
diff --git a/common/hw-gfx-gma-registers.adb b/common/hw-gfx-gma-registers.adb
new file mode 100644
index 0000000..22c5b9b
--- /dev/null
+++ b/common/hw-gfx-gma-registers.adb
@@ -0,0 +1,312 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with System.Storage_Elements;
+
+with HW.Time;
+with HW.MMIO_Range;
+pragma Elaborate_All (HW.MMIO_Range);
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+use type System.Address;
+use type HW.Word64;
+
+package body HW.GFX.GMA.Registers
+with
+   Refined_State =>
+     (Address_State  => (Regs.Base_Address, GTT.Base_Address),
+      Register_State => Regs.State,
+      GTT_State      => GTT.State)
+is
+   pragma Disable_Atomic_Synchronization;
+
+   type Registers_Range is
+      new Natural range 0 .. 16#0020_0000# / Register_Width - 1;
+   type Registers_Type is array (Registers_Range) of Word32
+   with
+      Atomic_Components,
+      Size => 16#20_0000# * 8;
+   package Regs is new MMIO_Range
+     (Base_Addr   => Config.Default_MMIO_Base,
+      Element_T   => Word32,
+      Index_T     => Registers_Range,
+      Array_T     => Registers_Type);
+
+   ----------------------------------------------------------------------------
+
+   GTT_Offset  : constant := (case Config.CPU is
+                                 when Ironlake .. Haswell   => 16#0020_0000#,
+                                 when Broadwell .. Skylake  => 16#0080_0000#);
+
+   GTT_Size    : constant := (case Config.CPU is
+                                 when Ironlake .. Haswell   => 16#0020_0000#,
+                                 -- Limit Broadwell to 4MiB to have a stable
+                                 -- interface (i.e. same number of entries):
+                                 when Broadwell .. Skylake  => 16#0040_0000#);
+
+   GTT_PTE_Size   : constant := (case Config.CPU is
+                                    when Ironlake .. Haswell   => 4,
+                                    when Broadwell .. Skylake  => 8);
+
+
+   type GTT_PTE_Type is mod 2 ** (GTT_PTE_Size * 8);
+   type GTT_Registers_Type is array (GTT_Range) of GTT_PTE_Type
+   with
+      Volatile_Components,
+      Size => GTT_Size * 8;
+   package GTT is new MMIO_Range
+     (Base_Addr   => Config.Default_MMIO_Base + GTT_Offset,
+      Element_T   => GTT_PTE_Type,
+      Index_T     => GTT_Range,
+      Array_T     => GTT_Registers_Type);
+
+   GTT_PTE_Valid : constant Word32 := 1;
+
+   ----------------------------------------------------------------------------
+
+   procedure Write_GTT
+     (GTT_Page       : GTT_Range;
+      Device_Address : GTT_Address_Type;
+      Valid          : Boolean)
+   is
+   begin
+      if Config.Fold_39Bit_GTT_PTE then
+         GTT.Write
+           (Index => GTT_Page,
+            Value => GTT_PTE_Type (Device_Address and 16#ffff_f000#) or
+                     GTT_PTE_Type (Shift_Right (Word64 (Device_Address), 32 - 4)
+                                   and 16#0000_07f0#) or
+                     Boolean'Pos (Valid));
+      else
+         GTT.Write
+           (Index => GTT_Page,
+            Value => GTT_PTE_Type (Device_Address and 16#7f_ffff_f000#) or
+                     Boolean'Pos (Valid));
+      end if;
+   end Write_GTT;
+
+   ----------------------------------------------------------------------------
+
+   package Rep is
+      function Index (Reg : Registers_Index) return Registers_Range;
+   end Rep;
+
+   package body Rep is
+      function Index (Reg : Registers_Index) return Registers_Range
+      with
+         SPARK_Mode => Off
+      is
+      begin
+         return Reg'Enum_Rep;
+      end Index;
+   end Rep;
+
+   -- Read a specific register
+   procedure Read
+     (Register : in     Registers_Index;
+      Value    :    out Word32;
+      Verbose  : in     Boolean := True)
+   is
+   begin
+      Regs.Read (Value, Rep.Index (Register));
+
+      pragma Debug (Verbose, Debug.Put (GNAT.Source_Info.Enclosing_Entity & ":  "));
+      pragma Debug (Verbose, Debug.Put_Word32 (Value));
+      pragma Debug (Verbose, Debug.Put (" <- "));
+      pragma Debug (Verbose, Debug.Put_Word32 (Register'Enum_Rep * Register_Width));
+      pragma Debug (Verbose, Debug.Put (":"));
+      pragma Debug (Verbose, Debug.Put_Line (Registers_Index'Image (Register)));
+   end Read;
+
+   ----------------------------------------------------------------------------
+
+   -- Read a specific register to post a previous write
+   procedure Posting_Read (Register : Registers_Index)
+   is
+      Discard_Value : Word32;
+   begin
+      pragma Warnings
+        (Off, "unused assignment to ""Discard_Value""",
+         Reason => "Intentional dummy read to affect hardware.");
+
+      Read (Register, Discard_Value);
+
+      pragma Warnings
+        (On, "unused assignment to ""Discard_Value""");
+   end Posting_Read;
+
+   ----------------------------------------------------------------------------
+
+   -- Write a specific register
+   procedure Write (Register : Registers_Index; Value : Word32)
+   is
+   begin
+      pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+      pragma Debug (Debug.Put_Word32 (Value));
+      pragma Debug (Debug.Put (" -> "));
+      pragma Debug (Debug.Put_Word32 (Register'Enum_Rep * Register_Width));
+      pragma Debug (Debug.Put (":"));
+      pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+      Regs.Write (Rep.Index (Register), Value);
+      pragma Debug (Debug.Register_Write_Wait);
+   end Write;
+
+   ----------------------------------------------------------------------------
+
+   -- Check whether all bits in @Register@ indicated by @Mask@ are set
+   procedure Is_Set_Mask
+      (Register : in     Registers_Index;
+       Mask     : in     Word32;
+       Result   :    out Boolean)
+   is
+      Value : Word32;
+   begin
+      pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+      pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+      Read (Register, Value);
+      Result := (Value and Mask) = Mask;
+
+   end Is_Set_Mask;
+
+   ----------------------------------------------------------------------------
+
+   -- TODO: Should have Success parameter
+   -- Wait for all bits in @Register@ indicated by @Mask@ to be set
+   procedure Wait_Set_Mask
+     (Register : in     Registers_Index;
+      Mask     : in     Word32;
+      TOut_MS  : in     Natural := Default_Timeout_MS;
+      Verbose  : in     Boolean := False)
+   is
+      Value : Word32;
+      Timeout : Time.T;
+      Timed_Out : Boolean;
+   begin
+      pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+      pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+      Timeout := Time.MS_From_Now (TOut_MS);
+      loop
+         Timed_Out := Time.Timed_Out (Timeout);
+         Read (Register, Value, Verbose);
+         if (Value and Mask) = Mask then
+            exit;
+         end if;
+         pragma Debug (Timed_Out, Debug.Put (GNAT.Source_Info.Enclosing_Entity));
+         pragma Debug (Timed_Out, Debug.Put_Line (": Timed Out!"));
+         exit when Timed_Out;
+      end loop;
+
+   end Wait_Set_Mask;
+
+   ----------------------------------------------------------------------------
+
+   -- TODO: Should have Success parameter
+   -- Wait for bits in @Register@ indicated by @Mask@ to be clear
+   procedure Wait_Unset_Mask
+     (Register : Registers_Index;
+      Mask     : Word32;
+      TOut_MS  : in     Natural := Default_Timeout_MS;
+      Verbose  : in     Boolean := False)
+   is
+      Value : Word32;
+      Timeout : Time.T;
+      Timed_Out : Boolean;
+   begin
+      pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+      pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+      Timeout := Time.MS_From_Now (TOut_MS);
+      loop
+         Timed_Out := Time.Timed_Out (Timeout);
+         Read (Register, Value, Verbose);
+         if (Value and Mask) = 0 then
+            exit;
+         end if;
+         pragma Debug (Timed_Out, Debug.Put (GNAT.Source_Info.Enclosing_Entity));
+         pragma Debug (Timed_Out, Debug.Put_Line (": Timed Out!"));
+         exit when Timed_Out;
+      end loop;
+
+   end Wait_Unset_Mask;
+
+   ----------------------------------------------------------------------------
+
+   -- Set bits from @Mask@ in @Register@
+   procedure Set_Mask
+      (Register : Registers_Index;
+       Mask     : Word32)
+   is
+      Value : Word32;
+   begin
+      pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+      pragma Debug (Debug.Put_Word32 (Mask));
+      pragma Debug (Debug.Put (" .S "));
+      pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+      Read (Register, Value);
+      Value := Value or Mask;
+      Write (Register, Value);
+   end Set_Mask;
+
+   ----------------------------------------------------------------------------
+
+   -- Mask out @Mask@ in @Register@
+   procedure Unset_Mask
+      (Register : Registers_Index;
+       Mask     : Word32)
+   is
+      Value : Word32;
+   begin
+      pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+      pragma Debug (Debug.Put_Word32 (Mask));
+      pragma Debug (Debug.Put (" !S "));
+      pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+      Read (Register, Value);
+      Value := Value and not Mask;
+      Write (Register, Value);
+   end Unset_Mask;
+
+   ----------------------------------------------------------------------------
+
+   -- Mask out @Unset_Mask@ and set @Set_Mask@ in @Register@
+   procedure Unset_And_Set_Mask
+      (Register   : Registers_Index;
+       Mask_Unset : Word32;
+       Mask_Set   : Word32)
+   is
+      Value : Word32;
+   begin
+      pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+      pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+      Read (Register, Value);
+      Value := (Value and not Mask_Unset) or Mask_Set;
+      Write (Register, Value);
+   end Unset_And_Set_Mask;
+
+   ----------------------------------------------------------------------------
+
+   procedure Set_Register_Base (Base : Word64)
+   is
+   begin
+      Regs.Set_Base_Address (Base);
+      GTT.Set_Base_Address (Base + GTT_Offset);
+   end Set_Register_Base;
+
+end HW.GFX.GMA.Registers;
diff --git a/common/hw-gfx-gma-registers.ads b/common/hw-gfx-gma-registers.ads
new file mode 100644
index 0000000..a765e2d
--- /dev/null
+++ b/common/hw-gfx-gma-registers.ads
@@ -0,0 +1,1121 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with System;
+with HW.GFX.GMA;
+with HW.GFX.GMA.Config;
+
+private package HW.GFX.GMA.Registers
+with
+   Abstract_State =>
+    ((Address_State  with Part_Of => GMA.State),
+     (Register_State with External, Part_Of => GMA.Device_State),
+     (GTT_State      with External, Part_Of => GMA.Device_State)),
+   Initializes => Address_State
+is
+   type Registers_Invalid_Index is
+     (Invalid_Register, -- Allow a placeholder when access is not acceptable
+
+      RCS_RING_BUFFER_TAIL,
+      RCS_RING_BUFFER_HEAD,
+      RCS_RING_BUFFER_STRT,
+      RCS_RING_BUFFER_CTL,
+      QUIRK_02084,
+      QUIRK_02090,
+      HWSTAM,
+      MI_MODE,
+      INSTPM,
+      GT_MODE,
+      CACHE_MODE_0,
+      CTX_SIZE,
+      PP_DCLV_HIGH,
+      PP_DCLV_LOW,
+      GFX_MODE,
+      ARB_MODE,
+      HWS_PGA,
+      GAM_ECOCHK,
+      MBCTL,
+      UCGCTL1,
+      UCGCTL2,
+      VCS_RING_BUFFER_TAIL,
+      VCS_RING_BUFFER_HEAD,
+      VCS_RING_BUFFER_STRT,
+      VCS_RING_BUFFER_CTL,
+      SLEEP_PSMI_CONTROL,
+      VCS_HWSTAM,
+      VCS_PP_DCLV_HIGH,
+      VCS_PP_DCLV_LOW,
+      GAC_ECO_BITS,
+      BCS_RING_BUFFER_TAIL,
+      BCS_RING_BUFFER_HEAD,
+      BCS_RING_BUFFER_STRT,
+      BCS_RING_BUFFER_CTL,
+      BCS_HWSTAM,
+      BCS_PP_DCLV_HIGH,
+      BCS_PP_DCLV_LOW,
+      GAB_CTL_REG,
+      VGACNTRL,
+      FUSE_STATUS,
+      QUIRK_42004,
+      DSPCLK_GATE_D,
+      FBA_CFB_BASE,
+      FBC_CTL,
+      IPS_CTL,
+      DEISR,
+      DEIMR,
+      DEIIR,
+      DEIER,
+      GTISR,
+      GTIMR,
+      GTIIR,
+      GTIER,
+      IIR,
+      HOTPLUG_CTL,
+      ARB_CTL,
+      DBUF_CTL,
+      WM_PIPE_A,
+      WM_PIPE_B,
+      WM1_LP_ILK,
+      WM2_LP_ILK,
+      WM3_LP_ILK,
+      WM_PIPE_C,
+      WM_LINETIME_A,
+      WM_LINETIME_B,
+      WM_LINETIME_C,
+      PWR_WELL_CTL_BIOS,
+      PWR_WELL_CTL_DRIVER,
+      PWR_WELL_CTL_KVMR,
+      PWR_WELL_CTL_DEBUG,
+      PWR_WELL_CTL5,
+      PWR_WELL_CTL6,
+      CDCLK_CTL,
+      LCPLL1_CTL,
+      LCPLL2_CTL,
+      SPLL_CTL,
+      WRPLL_CTL_1,
+      WRPLL_CTL_2,
+      PORT_CLK_SEL_DDIA,
+      PORT_CLK_SEL_DDIB,
+      PORT_CLK_SEL_DDIC,
+      PORT_CLK_SEL_DDID,
+      PORT_CLK_SEL_DDIE,
+      TRANSA_CLK_SEL,
+      TRANSB_CLK_SEL,
+      TRANSC_CLK_SEL,
+      NDE_RSTWRN_OPT,
+      BLC_PWM_CPU_CTL2,
+      BLC_PWM_CPU_CTL,
+      HTOTAL_A,
+      HBLANK_A,
+      HSYNC_A,
+      VTOTAL_A,
+      VBLANK_A,
+      VSYNC_A,
+      PIPEASRC,
+      PIPE_VSYNCSHIFT_A,
+      PIPEA_DATA_M1,
+      PIPEA_DATA_N1,
+      PIPEA_LINK_M1,
+      PIPEA_LINK_N1,
+      FDI_TX_CTL_A,
+      PIPEA_DDI_FUNC_CTL,
+      PIPEA_MSA_MISC,
+      SRD_CTL_A,
+      SRD_STATUS_A,
+      HTOTAL_B,
+      HBLANK_B,
+      HSYNC_B,
+      VTOTAL_B,
+      VBLANK_B,
+      VSYNC_B,
+      PIPEBSRC,
+      PIPE_VSYNCSHIFT_B,
+      PIPEB_DATA_M1,
+      PIPEB_DATA_N1,
+      PIPEB_LINK_M1,
+      PIPEB_LINK_N1,
+      FDI_TX_CTL_B,
+      PIPEB_DDI_FUNC_CTL,
+      PIPEB_MSA_MISC,
+      SRD_CTL_B,
+      SRD_STATUS_B,
+      HTOTAL_C,
+      HBLANK_C,
+      HSYNC_C,
+      VTOTAL_C,
+      VBLANK_C,
+      VSYNC_C,
+      PIPECSRC,
+      PIPE_VSYNCSHIFT_C,
+      PIPEC_DATA_M1,
+      PIPEC_DATA_N1,
+      PIPEC_LINK_M1,
+      PIPEC_LINK_N1,
+      FDI_TX_CTL_C,
+      PIPEC_DDI_FUNC_CTL,
+      PIPEC_MSA_MISC,
+      SRD_CTL_C,
+      SRD_STATUS_C,
+      DDI_BUF_CTL_A,
+      DDI_AUX_CTL_A,
+      DDI_AUX_DATA_A_1,
+      DDI_AUX_DATA_A_2,
+      DDI_AUX_DATA_A_3,
+      DDI_AUX_DATA_A_4,
+      DDI_AUX_DATA_A_5,
+      DDI_AUX_MUTEX_A,
+      DP_TP_CTL_A,
+      DDI_BUF_CTL_B,
+      DDI_AUX_CTL_B,
+      DDI_AUX_DATA_B_1,
+      DDI_AUX_DATA_B_2,
+      DDI_AUX_DATA_B_3,
+      DDI_AUX_DATA_B_4,
+      DDI_AUX_DATA_B_5,
+      DDI_AUX_MUTEX_B,
+      DP_TP_CTL_B,
+      DP_TP_STATUS_B,
+      DDI_BUF_CTL_C,
+      DDI_AUX_CTL_C,
+      DDI_AUX_DATA_C_1,
+      DDI_AUX_DATA_C_2,
+      DDI_AUX_DATA_C_3,
+      DDI_AUX_DATA_C_4,
+      DDI_AUX_DATA_C_5,
+      DDI_AUX_MUTEX_C,
+      DP_TP_CTL_C,
+      DP_TP_STATUS_C,
+      DDI_BUF_CTL_D,
+      DDI_AUX_CTL_D,
+      DDI_AUX_DATA_D_1,
+      DDI_AUX_DATA_D_2,
+      DDI_AUX_DATA_D_3,
+      DDI_AUX_DATA_D_4,
+      DDI_AUX_DATA_D_5,
+      DDI_AUX_MUTEX_D,
+      DP_TP_CTL_D,
+      DP_TP_STATUS_D,
+      DDI_BUF_CTL_E,
+      DP_TP_CTL_E,
+      DP_TP_STATUS_E,
+      SRD_CTL,
+      SRD_STATUS,
+      AUD_VID_DID,
+      PFA_WIN_POS,
+      PFA_WIN_SZ,
+      PFA_CTL_1,
+      PS_WIN_POS_1_A,
+      PS_WIN_SZ_1_A,
+      PS_CTRL_1_A,
+      PS_WIN_POS_2_A,
+      PS_WIN_SZ_2_A,
+      PS_CTRL_2_A,
+      PFB_WIN_POS,
+      PFB_WIN_SZ,
+      PFB_CTL_1,
+      PS_WIN_POS_1_B,
+      PS_WIN_SZ_1_B,
+      PS_CTRL_1_B,
+      PS_WIN_POS_2_B,
+      PS_WIN_SZ_2_B,
+      PS_CTRL_2_B,
+      PFC_WIN_POS,
+      PFC_WIN_SZ,
+      PFC_CTL_1,
+      PS_WIN_POS_1_C,
+      PS_WIN_SZ_1_C,
+      PS_CTRL_1_C,
+      DPLL1_CFGR1,
+      DPLL1_CFGR2,
+      DPLL2_CFGR1,
+      DPLL2_CFGR2,
+      DPLL3_CFGR1,
+      DPLL3_CFGR2,
+      DPLL_CTRL1,
+      DPLL_CTRL2,
+      DPLL_STATUS,
+      HTOTAL_EDP,
+      HBLANK_EDP,
+      HSYNC_EDP,
+      VTOTAL_EDP,
+      VBLANK_EDP,
+      VSYNC_EDP,
+      PIPE_EDP_DATA_M1,
+      PIPE_EDP_DATA_N1,
+      PIPE_EDP_LINK_M1,
+      PIPE_EDP_LINK_N1,
+      PIPE_EDP_DDI_FUNC_CTL,
+      PIPE_EDP_MSA_MISC,
+      SRD_CTL_EDP,
+      SRD_STATUS_EDP,
+      PIPE_SCANLINE_A,
+      PIPEACONF,
+      PIPEAMISC,
+      PIPE_FRMCNT_A,
+      DSPACNTR,
+      DSPALINOFF,
+      DSPASTRIDE,
+      PLANE_POS_1_A,
+      PLANE_SIZE_1_A,
+      DSPASURF,
+      DSPATILEOFF,
+      PLANE_WM_1_A_0,
+      PLANE_WM_1_A_1,
+      PLANE_WM_1_A_2,
+      PLANE_WM_1_A_3,
+      PLANE_WM_1_A_4,
+      PLANE_WM_1_A_5,
+      PLANE_WM_1_A_6,
+      PLANE_WM_1_A_7,
+      PLANE_BUF_CFG_1_A,
+      SPACNTR,
+      PIPE_SCANLINE_B,
+      PIPEBCONF,
+      PIPEBMISC,
+      PIPE_FRMCNT_B,
+      DSPBCNTR,
+      DSPBLINOFF,
+      DSPBSTRIDE,
+      PLANE_POS_1_B,
+      PLANE_SIZE_1_B,
+      DSPBSURF,
+      DSPBTILEOFF,
+      PLANE_WM_1_B_0,
+      PLANE_WM_1_B_1,
+      PLANE_WM_1_B_2,
+      PLANE_WM_1_B_3,
+      PLANE_WM_1_B_4,
+      PLANE_WM_1_B_5,
+      PLANE_WM_1_B_6,
+      PLANE_WM_1_B_7,
+      PLANE_BUF_CFG_1_B,
+      SPBCNTR,
+      PIPE_SCANLINE_C,
+      PIPECCONF,
+      PIPECMISC,
+      PIPE_FRMCNT_C,
+      DSPCCNTR,
+      DSPCLINOFF,
+      DSPCSTRIDE,
+      PLANE_POS_1_C,
+      PLANE_SIZE_1_C,
+      DSPCSURF,
+      DSPCTILEOFF,
+      PLANE_WM_1_C_0,
+      PLANE_WM_1_C_1,
+      PLANE_WM_1_C_2,
+      PLANE_WM_1_C_3,
+      PLANE_WM_1_C_4,
+      PLANE_WM_1_C_5,
+      PLANE_WM_1_C_6,
+      PLANE_WM_1_C_7,
+      PLANE_BUF_CFG_1_C,
+      SPCCNTR,
+      PIPE_EDP_CONF,
+      PCH_FDI_CHICKEN_B_C,
+      QUIRK_C2004,
+      SFUSE_STRAP,
+      PCH_DSPCLK_GATE_D,
+      SDEISR,
+      SDEIMR,
+      SDEIIR,
+      SDEIER,
+      SHOTPLUG_CTL,
+      PCH_GMBUS0,
+      PCH_GMBUS1,
+      PCH_GMBUS2,
+      PCH_GMBUS3,
+      PCH_GMBUS4,
+      PCH_GMBUS5,
+      SBI_ADDR,
+      SBI_DATA,
+      SBI_CTL_STAT,
+      PCH_DPLL_A,
+      PCH_DPLL_B,
+      PCH_PIXCLK_GATE,
+      PCH_FPA0,
+      PCH_FPA1,
+      PCH_FPB0,
+      PCH_FPB1,
+      PCH_DREF_CONTROL,
+      RAWCLK_FREQ,
+      PCH_DPLL_SEL,
+      PCH_PP_STATUS,
+      PCH_PP_CONTROL,
+      PCH_PP_ON_DELAYS,
+      PCH_PP_OFF_DELAYS,
+      PCH_PP_DIVISOR,
+      BLC_PWM_PCH_CTL1,
+      BLC_PWM_PCH_CTL2,
+      TRANS_HTOTAL_A,
+      TRANS_HBLANK_A,
+      TRANS_HSYNC_A,
+      TRANS_VTOTAL_A,
+      TRANS_VBLANK_A,
+      TRANS_VSYNC_A,
+      TRANS_VSYNCSHIFT_A,
+      TRANSA_DATA_M1,
+      TRANSA_DATA_N1,
+      TRANSA_DP_LINK_M1,
+      TRANSA_DP_LINK_N1,
+      TRANS_DP_CTL_A,
+      TRANS_HTOTAL_B,
+      TRANS_HBLANK_B,
+      TRANS_HSYNC_B,
+      TRANS_VTOTAL_B,
+      TRANS_VBLANK_B,
+      TRANS_VSYNC_B,
+      TRANS_VSYNCSHIFT_B,
+      TRANSB_DATA_M1,
+      TRANSB_DATA_N1,
+      TRANSB_DP_LINK_M1,
+      TRANSB_DP_LINK_N1,
+      PCH_ADPA,
+      PCH_HDMIB,
+      PCH_HDMIC,
+      PCH_HDMID,
+      PCH_LVDS,
+      TRANS_DP_CTL_B,
+      TRANS_HTOTAL_C,
+      TRANS_HBLANK_C,
+      TRANS_HSYNC_C,
+      TRANS_VTOTAL_C,
+      TRANS_VBLANK_C,
+      TRANS_VSYNC_C,
+      TRANS_VSYNCSHIFT_C,
+      TRANSC_DATA_M1,
+      TRANSC_DATA_N1,
+      TRANSC_DP_LINK_M1,
+      TRANSC_DP_LINK_N1,
+      TRANS_DP_CTL_C,
+      PCH_DP_B,
+      PCH_DP_AUX_CTL_B,
+      PCH_DP_AUX_DATA_B_1,
+      PCH_DP_AUX_DATA_B_2,
+      PCH_DP_AUX_DATA_B_3,
+      PCH_DP_AUX_DATA_B_4,
+      PCH_DP_AUX_DATA_B_5,
+      PCH_DP_C,
+      PCH_DP_AUX_CTL_C,
+      PCH_DP_AUX_DATA_C_1,
+      PCH_DP_AUX_DATA_C_2,
+      PCH_DP_AUX_DATA_C_3,
+      PCH_DP_AUX_DATA_C_4,
+      PCH_DP_AUX_DATA_C_5,
+      PCH_DP_D,
+      PCH_DP_AUX_CTL_D,
+      PCH_DP_AUX_DATA_D_1,
+      PCH_DP_AUX_DATA_D_2,
+      PCH_DP_AUX_DATA_D_3,
+      PCH_DP_AUX_DATA_D_4,
+      PCH_DP_AUX_DATA_D_5,
+      AUD_CONFIG_A,
+      PCH_AUD_VID_DID,
+      AUD_HDMIW_HDMIEDID_A,
+      AUD_CNTL_ST_A,
+      AUD_CNTRL_ST2,
+      AUD_CONFIG_B,
+      AUD_HDMIW_HDMIEDID_B,
+      AUD_CNTL_ST_B,
+      AUD_CONFIG_C,
+      AUD_HDMIW_HDMIEDID_C,
+      AUD_CNTL_ST_C,
+      TRANSACONF,
+      FDI_RXA_CTL,
+      FDI_RX_MISC_A,
+      FDI_RXA_IIR,
+      FDI_RXA_IMR,
+      FDI_RXA_TUSIZE1,
+      QUIRK_F0060,
+      TRANSA_CHICKEN2,
+      TRANSBCONF,
+      FDI_RXB_CTL,
+      FDI_RX_MISC_B,
+      FDI_RXB_IIR,
+      FDI_RXB_IMR,
+      FDI_RXB_TUSIZE1,
+      QUIRK_F1060,
+      TRANSB_CHICKEN2,
+      TRANSCCONF,
+      FDI_RXC_CTL,
+      FDI_RX_MISC_C,
+      FDI_RXC_IIR,
+      FDI_RXC_IMR,
+      FDI_RXC_TUSIZE1,
+      QUIRK_F2060,
+      TRANSC_CHICKEN2,
+      GT_MAILBOX,
+      GT_MAILBOX_DATA,
+      GT_MAILBOX_DATA_1);
+
+   pragma Warnings
+     (GNATprove, Off, "pragma ""KEEP_NAMES"" ignored *(not yet supported)",
+      Reason => "TODO: Should it matter?");
+   pragma Keep_Names (Registers_Invalid_Index);
+   pragma Warnings
+     (GNATprove, On, "pragma ""KEEP_NAMES"" ignored *(not yet supported)");
+
+   Register_Width : constant := 4;
+
+   for Registers_Invalid_Index use
+     (Invalid_Register     => 0,
+
+   ---------------------------------------------------------------------------
+   -- Pipe A registers
+   ---------------------------------------------------------------------------
+
+      -- pipe timing registers
+
+      HTOTAL_A              => 16#06_0000# / Register_Width,
+      HBLANK_A              => 16#06_0004# / Register_Width,
+      HSYNC_A               => 16#06_0008# / Register_Width,
+      VTOTAL_A              => 16#06_000c# / Register_Width,
+      VBLANK_A              => 16#06_0010# / Register_Width,
+      VSYNC_A               => 16#06_0014# / Register_Width,
+      PIPEASRC              => 16#06_001c# / Register_Width,
+      PIPEACONF             => 16#07_0008# / Register_Width,
+      PIPEAMISC             => 16#07_0030# / Register_Width,
+      TRANS_HTOTAL_A        => 16#0e_0000# / Register_Width,
+      TRANS_HBLANK_A        => 16#0e_0004# / Register_Width,
+      TRANS_HSYNC_A         => 16#0e_0008# / Register_Width,
+      TRANS_VTOTAL_A        => 16#0e_000c# / Register_Width,
+      TRANS_VBLANK_A        => 16#0e_0010# / Register_Width,
+      TRANS_VSYNC_A         => 16#0e_0014# / Register_Width,
+      TRANSA_DATA_M1        => 16#0e_0030# / Register_Width,
+      TRANSA_DATA_N1        => 16#0e_0034# / Register_Width,
+      TRANSA_DP_LINK_M1     => 16#0e_0040# / Register_Width,
+      TRANSA_DP_LINK_N1     => 16#0e_0044# / Register_Width,
+      PIPEA_DATA_M1         => 16#06_0030# / Register_Width,
+      PIPEA_DATA_N1         => 16#06_0034# / Register_Width,
+      PIPEA_LINK_M1         => 16#06_0040# / Register_Width,
+      PIPEA_LINK_N1         => 16#06_0044# / Register_Width,
+      PIPEA_DDI_FUNC_CTL    => 16#06_0400# / Register_Width,
+      PIPEA_MSA_MISC        => 16#06_0410# / Register_Width,
+
+      -- PCH sideband interface registers
+      SBI_ADDR              => 16#0c_6000# / Register_Width,
+      SBI_DATA              => 16#0c_6004# / Register_Width,
+      SBI_CTL_STAT          => 16#0c_6008# / Register_Width,
+
+      -- clock registers
+      PCH_DPLL_A            => 16#0c_6014# / Register_Width,
+      PCH_PIXCLK_GATE       => 16#0c_6020# / Register_Width,
+      PCH_FPA0              => 16#0c_6040# / Register_Width,
+      PCH_FPA1              => 16#0c_6044# / Register_Width,
+
+      -- panel fitter
+      PFA_CTL_1             => 16#06_8080# / Register_Width,
+      PFA_WIN_POS           => 16#06_8070# / Register_Width,
+      PFA_WIN_SZ            => 16#06_8074# / Register_Width,
+      PS_WIN_POS_1_A        => 16#06_8170# / Register_Width,
+      PS_WIN_SZ_1_A         => 16#06_8174# / Register_Width,
+      PS_CTRL_1_A           => 16#06_8180# / Register_Width,
+      PS_WIN_POS_2_A        => 16#06_8270# / Register_Width,
+      PS_WIN_SZ_2_A         => 16#06_8274# / Register_Width,
+      PS_CTRL_2_A           => 16#06_8280# / Register_Width,
+
+      -- display control
+      DSPACNTR              => 16#07_0180# / Register_Width,
+      DSPALINOFF            => 16#07_0184# / Register_Width,
+      DSPASTRIDE            => 16#07_0188# / Register_Width,
+      PLANE_POS_1_A         => 16#07_018c# / Register_Width,
+      PLANE_SIZE_1_A        => 16#07_0190# / Register_Width,
+      DSPASURF              => 16#07_019c# / Register_Width,
+      DSPATILEOFF           => 16#07_01a4# / Register_Width,
+
+      -- sprite control
+      SPACNTR               => 16#07_0280# / Register_Width,
+
+      -- FDI and PCH transcoder control
+      FDI_TX_CTL_A          => 16#06_0100# / Register_Width,
+      FDI_RXA_CTL           => 16#0f_000c# / Register_Width,
+      FDI_RX_MISC_A         => 16#0f_0010# / Register_Width,
+      FDI_RXA_IIR           => 16#0f_0014# / Register_Width,
+      FDI_RXA_IMR           => 16#0f_0018# / Register_Width,
+      FDI_RXA_TUSIZE1       => 16#0f_0030# / Register_Width,
+      TRANSACONF            => 16#0f_0008# / Register_Width,
+      TRANSA_CHICKEN2       => 16#0f_0064# / Register_Width,
+
+      -- watermark registers
+      WM_LINETIME_A         => 16#04_5270# / Register_Width,
+      PLANE_WM_1_A_0        => 16#07_0240# / Register_Width,
+      PLANE_WM_1_A_1        => 16#07_0244# / Register_Width,
+      PLANE_WM_1_A_2        => 16#07_0248# / Register_Width,
+      PLANE_WM_1_A_3        => 16#07_024c# / Register_Width,
+      PLANE_WM_1_A_4        => 16#07_0250# / Register_Width,
+      PLANE_WM_1_A_5        => 16#07_0254# / Register_Width,
+      PLANE_WM_1_A_6        => 16#07_0258# / Register_Width,
+      PLANE_WM_1_A_7        => 16#07_025c# / Register_Width,
+      PLANE_BUF_CFG_1_A     => 16#07_027c# / Register_Width,
+
+      -- CPU transcoder clock select
+      TRANSA_CLK_SEL        => 16#04_6140# / Register_Width,
+
+   ---------------------------------------------------------------------------
+   -- Pipe B registers
+   ---------------------------------------------------------------------------
+
+      -- pipe timing registers
+
+      HTOTAL_B              => 16#06_1000# / Register_Width,
+      HBLANK_B              => 16#06_1004# / Register_Width,
+      HSYNC_B               => 16#06_1008# / Register_Width,
+      VTOTAL_B              => 16#06_100c# / Register_Width,
+      VBLANK_B              => 16#06_1010# / Register_Width,
+      VSYNC_B               => 16#06_1014# / Register_Width,
+      PIPEBSRC              => 16#06_101c# / Register_Width,
+      PIPEBCONF             => 16#07_1008# / Register_Width,
+      PIPEBMISC             => 16#07_1030# / Register_Width,
+      TRANS_HTOTAL_B        => 16#0e_1000# / Register_Width,
+      TRANS_HBLANK_B        => 16#0e_1004# / Register_Width,
+      TRANS_HSYNC_B         => 16#0e_1008# / Register_Width,
+      TRANS_VTOTAL_B        => 16#0e_100c# / Register_Width,
+      TRANS_VBLANK_B        => 16#0e_1010# / Register_Width,
+      TRANS_VSYNC_B         => 16#0e_1014# / Register_Width,
+      TRANSB_DATA_M1        => 16#0e_1030# / Register_Width,
+      TRANSB_DATA_N1        => 16#0e_1034# / Register_Width,
+      TRANSB_DP_LINK_M1     => 16#0e_1040# / Register_Width,
+      TRANSB_DP_LINK_N1     => 16#0e_1044# / Register_Width,
+      PIPEB_DATA_M1         => 16#06_1030# / Register_Width,
+      PIPEB_DATA_N1         => 16#06_1034# / Register_Width,
+      PIPEB_LINK_M1         => 16#06_1040# / Register_Width,
+      PIPEB_LINK_N1         => 16#06_1044# / Register_Width,
+      PIPEB_DDI_FUNC_CTL    => 16#06_1400# / Register_Width,
+      PIPEB_MSA_MISC        => 16#06_1410# / Register_Width,
+
+      -- clock registers
+      PCH_DPLL_B            => 16#0c_6018# / Register_Width,
+      PCH_FPB0              => 16#0c_6048# / Register_Width,
+      PCH_FPB1              => 16#0c_604c# / Register_Width,
+
+      -- panel fitter
+      PFB_CTL_1             => 16#06_8880# / Register_Width,
+      PFB_WIN_POS           => 16#06_8870# / Register_Width,
+      PFB_WIN_SZ            => 16#06_8874# / Register_Width,
+      PS_WIN_POS_1_B        => 16#06_8970# / Register_Width,
+      PS_WIN_SZ_1_B         => 16#06_8974# / Register_Width,
+      PS_CTRL_1_B           => 16#06_8980# / Register_Width,
+      PS_WIN_POS_2_B        => 16#06_8a70# / Register_Width,
+      PS_WIN_SZ_2_B         => 16#06_8a74# / Register_Width,
+      PS_CTRL_2_B           => 16#06_8a80# / Register_Width,
+
+      -- display control
+      DSPBCNTR              => 16#07_1180# / Register_Width,
+      DSPBLINOFF            => 16#07_1184# / Register_Width,
+      DSPBSTRIDE            => 16#07_1188# / Register_Width,
+      PLANE_POS_1_B         => 16#07_118c# / Register_Width,
+      PLANE_SIZE_1_B        => 16#07_1190# / Register_Width,
+      DSPBSURF              => 16#07_119c# / Register_Width,
+      DSPBTILEOFF           => 16#07_11a4# / Register_Width,
+
+      -- sprite control
+      SPBCNTR               => 16#07_1280# / Register_Width,
+
+      -- FDI and PCH transcoder control
+      FDI_TX_CTL_B          => 16#06_1100# / Register_Width,
+      FDI_RXB_CTL           => 16#0f_100c# / Register_Width,
+      FDI_RX_MISC_B         => 16#0f_1010# / Register_Width,
+      FDI_RXB_IIR           => 16#0f_1014# / Register_Width,
+      FDI_RXB_IMR           => 16#0f_1018# / Register_Width,
+      FDI_RXB_TUSIZE1       => 16#0f_1030# / Register_Width,
+      TRANSBCONF            => 16#0f_1008# / Register_Width,
+      TRANSB_CHICKEN2       => 16#0f_1064# / Register_Width,
+
+      -- watermark registers
+      WM_LINETIME_B         => 16#04_5274# / Register_Width,
+      PLANE_WM_1_B_0        => 16#07_1240# / Register_Width,
+      PLANE_WM_1_B_1        => 16#07_1244# / Register_Width,
+      PLANE_WM_1_B_2        => 16#07_1248# / Register_Width,
+      PLANE_WM_1_B_3        => 16#07_124c# / Register_Width,
+      PLANE_WM_1_B_4        => 16#07_1250# / Register_Width,
+      PLANE_WM_1_B_5        => 16#07_1254# / Register_Width,
+      PLANE_WM_1_B_6        => 16#07_1258# / Register_Width,
+      PLANE_WM_1_B_7        => 16#07_125c# / Register_Width,
+      PLANE_BUF_CFG_1_B     => 16#07_127c# / Register_Width,
+
+      -- CPU transcoder clock select
+      TRANSB_CLK_SEL        => 16#04_6144# / Register_Width,
+
+   ---------------------------------------------------------------------------
+   -- Pipe C registers
+   ---------------------------------------------------------------------------
+
+      -- pipe timing registers
+
+      HTOTAL_C              => 16#06_2000# / Register_Width,
+      HBLANK_C              => 16#06_2004# / Register_Width,
+      HSYNC_C               => 16#06_2008# / Register_Width,
+      VTOTAL_C              => 16#06_200c# / Register_Width,
+      VBLANK_C              => 16#06_2010# / Register_Width,
+      VSYNC_C               => 16#06_2014# / Register_Width,
+      PIPECSRC              => 16#06_201c# / Register_Width,
+      PIPECCONF             => 16#07_2008# / Register_Width,
+      PIPECMISC             => 16#07_2030# / Register_Width,
+      TRANS_HTOTAL_C        => 16#0e_2000# / Register_Width,
+      TRANS_HBLANK_C        => 16#0e_2004# / Register_Width,
+      TRANS_HSYNC_C         => 16#0e_2008# / Register_Width,
+      TRANS_VTOTAL_C        => 16#0e_200c# / Register_Width,
+      TRANS_VBLANK_C        => 16#0e_2010# / Register_Width,
+      TRANS_VSYNC_C         => 16#0e_2014# / Register_Width,
+      TRANSC_DATA_M1        => 16#0e_2030# / Register_Width,
+      TRANSC_DATA_N1        => 16#0e_2034# / Register_Width,
+      TRANSC_DP_LINK_M1     => 16#0e_2040# / Register_Width,
+      TRANSC_DP_LINK_N1     => 16#0e_2044# / Register_Width,
+      PIPEC_DATA_M1         => 16#06_2030# / Register_Width,
+      PIPEC_DATA_N1         => 16#06_2034# / Register_Width,
+      PIPEC_LINK_M1         => 16#06_2040# / Register_Width,
+      PIPEC_LINK_N1         => 16#06_2044# / Register_Width,
+      PIPEC_DDI_FUNC_CTL    => 16#06_2400# / Register_Width,
+      PIPEC_MSA_MISC        => 16#06_2410# / Register_Width,
+
+      -- panel fitter
+      PFC_CTL_1             => 16#06_9080# / Register_Width,
+      PFC_WIN_POS           => 16#06_9070# / Register_Width,
+      PFC_WIN_SZ            => 16#06_9074# / Register_Width,
+      PS_WIN_POS_1_C        => 16#06_9170# / Register_Width,
+      PS_WIN_SZ_1_C         => 16#06_9174# / Register_Width,
+      PS_CTRL_1_C           => 16#06_9180# / Register_Width,
+
+      -- display control
+      DSPCCNTR              => 16#07_2180# / Register_Width,
+      DSPCLINOFF            => 16#07_2184# / Register_Width,
+      DSPCSTRIDE            => 16#07_2188# / Register_Width,
+      PLANE_POS_1_C         => 16#07_218c# / Register_Width,
+      PLANE_SIZE_1_C        => 16#07_2190# / Register_Width,
+      DSPCSURF              => 16#07_219c# / Register_Width,
+      DSPCTILEOFF           => 16#07_21a4# / Register_Width,
+
+      -- sprite control
+      SPCCNTR               => 16#07_2280# / Register_Width,
+
+      -- PCH transcoder control
+      FDI_TX_CTL_C          => 16#06_2100# / Register_Width,
+      FDI_RXC_CTL           => 16#0f_200c# / Register_Width,
+      FDI_RX_MISC_C         => 16#0f_2010# / Register_Width,
+      FDI_RXC_IIR           => 16#0f_2014# / Register_Width,
+      FDI_RXC_IMR           => 16#0f_2018# / Register_Width,
+      FDI_RXC_TUSIZE1       => 16#0f_2030# / Register_Width,
+      TRANSCCONF            => 16#0f_2008# / Register_Width,
+      TRANSC_CHICKEN2       => 16#0f_2064# / Register_Width,
+
+      -- watermark registers
+      WM_LINETIME_C         => 16#04_5278# / Register_Width,
+      PLANE_WM_1_C_0        => 16#07_2240# / Register_Width,
+      PLANE_WM_1_C_1        => 16#07_2244# / Register_Width,
+      PLANE_WM_1_C_2        => 16#07_2248# / Register_Width,
+      PLANE_WM_1_C_3        => 16#07_224c# / Register_Width,
+      PLANE_WM_1_C_4        => 16#07_2250# / Register_Width,
+      PLANE_WM_1_C_5        => 16#07_2254# / Register_Width,
+      PLANE_WM_1_C_6        => 16#07_2258# / Register_Width,
+      PLANE_WM_1_C_7        => 16#07_225c# / Register_Width,
+      PLANE_BUF_CFG_1_C     => 16#07_227c# / Register_Width,
+
+      -- CPU transcoder clock select
+      TRANSC_CLK_SEL        => 16#04_6148# / Register_Width,
+
+   ---------------------------------------------------------------------------
+   -- Pipe EDP registers
+   ---------------------------------------------------------------------------
+
+      -- pipe timing registers
+
+      HTOTAL_EDP            => 16#06_f000# / Register_Width,
+      HBLANK_EDP            => 16#06_f004# / Register_Width,
+      HSYNC_EDP             => 16#06_f008# / Register_Width,
+      VTOTAL_EDP            => 16#06_f00c# / Register_Width,
+      VBLANK_EDP            => 16#06_f010# / Register_Width,
+      VSYNC_EDP             => 16#06_f014# / Register_Width,
+      PIPE_EDP_CONF         => 16#07_f008# / Register_Width,
+      PIPE_EDP_DATA_M1      => 16#06_f030# / Register_Width,
+      PIPE_EDP_DATA_N1      => 16#06_f034# / Register_Width,
+      PIPE_EDP_LINK_M1      => 16#06_f040# / Register_Width,
+      PIPE_EDP_LINK_N1      => 16#06_f044# / Register_Width,
+      PIPE_EDP_DDI_FUNC_CTL => 16#06_f400# / Register_Width,
+      PIPE_EDP_MSA_MISC     => 16#06_f410# / Register_Width,
+
+      -- PSR registers
+      SRD_CTL               => 16#06_4800# / Register_Width,
+      SRD_CTL_A             => 16#06_0800# / Register_Width,
+      SRD_CTL_B             => 16#06_1800# / Register_Width,
+      SRD_CTL_C             => 16#06_2800# / Register_Width,
+      SRD_CTL_EDP           => 16#06_f800# / Register_Width,
+      SRD_STATUS            => 16#06_4840# / Register_Width,
+      SRD_STATUS_A          => 16#06_0840# / Register_Width,
+      SRD_STATUS_B          => 16#06_1840# / Register_Width,
+      SRD_STATUS_C          => 16#06_2840# / Register_Width,
+      SRD_STATUS_EDP        => 16#06_f840# / Register_Width,
+
+      -- DDI registers
+      DDI_BUF_CTL_A         => 16#06_4000# / Register_Width, -- aliased by DP_CTL_A
+      DDI_AUX_CTL_A         => 16#06_4010# / Register_Width, -- aliased by DP_AUX_CTL_A
+      DDI_AUX_DATA_A_1      => 16#06_4014# / Register_Width, -- aliased by DP_AUX_DATA_A_1
+      DDI_AUX_DATA_A_2      => 16#06_4018# / Register_Width, -- aliased by DP_AUX_DATA_A_2
+      DDI_AUX_DATA_A_3      => 16#06_401c# / Register_Width, -- aliased by DP_AUX_DATA_A_3
+      DDI_AUX_DATA_A_4      => 16#06_4020# / Register_Width, -- aliased by DP_AUX_DATA_A_4
+      DDI_AUX_DATA_A_5      => 16#06_4024# / Register_Width, -- aliased by DP_AUX_DATA_A_5
+      DDI_AUX_MUTEX_A       => 16#06_402c# / Register_Width,
+      DDI_BUF_CTL_B         => 16#06_4100# / Register_Width,
+      DDI_AUX_CTL_B         => 16#06_4110# / Register_Width,
+      DDI_AUX_DATA_B_1      => 16#06_4114# / Register_Width,
+      DDI_AUX_DATA_B_2      => 16#06_4118# / Register_Width,
+      DDI_AUX_DATA_B_3      => 16#06_411c# / Register_Width,
+      DDI_AUX_DATA_B_4      => 16#06_4120# / Register_Width,
+      DDI_AUX_DATA_B_5      => 16#06_4124# / Register_Width,
+      DDI_AUX_MUTEX_B       => 16#06_412c# / Register_Width,
+      DDI_BUF_CTL_C         => 16#06_4200# / Register_Width,
+      DDI_AUX_CTL_C         => 16#06_4210# / Register_Width,
+      DDI_AUX_DATA_C_1      => 16#06_4214# / Register_Width,
+      DDI_AUX_DATA_C_2      => 16#06_4218# / Register_Width,
+      DDI_AUX_DATA_C_3      => 16#06_421c# / Register_Width,
+      DDI_AUX_DATA_C_4      => 16#06_4220# / Register_Width,
+      DDI_AUX_DATA_C_5      => 16#06_4224# / Register_Width,
+      DDI_AUX_MUTEX_C       => 16#06_422c# / Register_Width,
+      DDI_BUF_CTL_D         => 16#06_4300# / Register_Width,
+      DDI_AUX_CTL_D         => 16#06_4310# / Register_Width,
+      DDI_AUX_DATA_D_1      => 16#06_4314# / Register_Width,
+      DDI_AUX_DATA_D_2      => 16#06_4318# / Register_Width,
+      DDI_AUX_DATA_D_3      => 16#06_431c# / Register_Width,
+      DDI_AUX_DATA_D_4      => 16#06_4320# / Register_Width,
+      DDI_AUX_DATA_D_5      => 16#06_4324# / Register_Width,
+      DDI_AUX_MUTEX_D       => 16#06_432c# / Register_Width,
+      DDI_BUF_CTL_E         => 16#06_4400# / Register_Width,
+      DP_TP_CTL_A           => 16#06_4040# / Register_Width,
+      DP_TP_CTL_B           => 16#06_4140# / Register_Width,
+      DP_TP_CTL_C           => 16#06_4240# / Register_Width,
+      DP_TP_CTL_D           => 16#06_4340# / Register_Width,
+      DP_TP_CTL_E           => 16#06_4440# / Register_Width,
+      DP_TP_STATUS_B        => 16#06_4144# / Register_Width,
+      DP_TP_STATUS_C        => 16#06_4244# / Register_Width,
+      DP_TP_STATUS_D        => 16#06_4344# / Register_Width,
+      DP_TP_STATUS_E        => 16#06_4444# / Register_Width,
+      PORT_CLK_SEL_DDIA     => 16#04_6100# / Register_Width,
+      PORT_CLK_SEL_DDIB     => 16#04_6104# / Register_Width,
+      PORT_CLK_SEL_DDIC     => 16#04_6108# / Register_Width,
+      PORT_CLK_SEL_DDID     => 16#04_610c# / Register_Width,
+      PORT_CLK_SEL_DDIE     => 16#04_6110# / Register_Width,
+
+      -- Skylake DPLL registers
+      DPLL1_CFGR1           => 16#06_c040# / Register_Width,
+      DPLL1_CFGR2           => 16#06_c044# / Register_Width,
+      DPLL2_CFGR1           => 16#06_c048# / Register_Width,
+      DPLL2_CFGR2           => 16#06_c04c# / Register_Width,
+      DPLL3_CFGR1           => 16#06_c050# / Register_Width,
+      DPLL3_CFGR2           => 16#06_c054# / Register_Width,
+      DPLL_CTRL1            => 16#06_c058# / Register_Width,
+      DPLL_CTRL2            => 16#06_c05c# / Register_Width,
+      DPLL_STATUS           => 16#06_c060# / Register_Width,
+
+      -- CD CLK register
+      CDCLK_CTL             => 16#04_6000# / Register_Width,
+
+      -- Skylake LCPLL registers
+      LCPLL1_CTL            => 16#04_6010# / Register_Width,
+      LCPLL2_CTL            => 16#04_6014# / Register_Width,
+
+      -- SPLL register
+      SPLL_CTL              => 16#04_6020# / Register_Width,
+
+      -- WRPLL registers
+      WRPLL_CTL_1           => 16#04_6040# / Register_Width,
+      WRPLL_CTL_2           => 16#04_6060# / Register_Width,
+
+      -- Power Down Well registers
+      PWR_WELL_CTL_BIOS     => 16#04_5400# / Register_Width,
+      PWR_WELL_CTL_DRIVER   => 16#04_5404# / Register_Width,
+      PWR_WELL_CTL_KVMR     => 16#04_5408# / Register_Width,
+      PWR_WELL_CTL_DEBUG    => 16#04_540c# / Register_Width,
+      PWR_WELL_CTL5         => 16#04_5410# / Register_Width,
+      PWR_WELL_CTL6         => 16#04_5414# / Register_Width,
+
+      -- class Panel registers
+      PCH_PP_STATUS         => 16#0c_7200# / Register_Width,
+      PCH_PP_CONTROL        => 16#0c_7204# / Register_Width,
+      PCH_PP_ON_DELAYS      => 16#0c_7208# / Register_Width,
+      PCH_PP_OFF_DELAYS     => 16#0c_720c# / Register_Width,
+      PCH_PP_DIVISOR        => 16#0c_7210# / Register_Width,
+      BLC_PWM_CPU_CTL       => 16#04_8254# / Register_Width,
+      BLC_PWM_PCH_CTL2      => 16#0c_8254# / Register_Width,
+
+      -- PCH LVDS Connector Registers
+      PCH_LVDS              => 16#0e_1180# / Register_Width,
+
+      -- PCH ADPA Connector Registers
+      PCH_ADPA              => 16#0e_1100# / Register_Width,
+
+      -- PCH HDMIB Connector Registers
+      PCH_HDMIB             => 16#0e_1140# / Register_Width,
+
+      -- PCH HDMIC Connector Registers
+      PCH_HDMIC             => 16#0e_1150# / Register_Width,
+
+      -- PCH HDMID Connector Registers
+      PCH_HDMID             => 16#0e_1160# / Register_Width,
+
+      -- Intel Registers
+      VGACNTRL              => 16#04_1000# / Register_Width,
+      FUSE_STATUS           => 16#04_2000# / Register_Width,
+      FBA_CFB_BASE          => 16#04_3200# / Register_Width,
+      IPS_CTL               => 16#04_3408# / Register_Width,
+      ARB_CTL               => 16#04_5000# / Register_Width,
+      DBUF_CTL              => 16#04_5008# / Register_Width,
+      NDE_RSTWRN_OPT        => 16#04_6408# / Register_Width,
+      PCH_DREF_CONTROL      => 16#0c_6200# / Register_Width,
+      BLC_PWM_PCH_CTL1      => 16#0c_8250# / Register_Width,
+      BLC_PWM_CPU_CTL2      => 16#04_8250# / Register_Width,
+      PCH_DPLL_SEL          => 16#0c_7000# / Register_Width,
+      GT_MAILBOX            => 16#13_8124# / Register_Width,
+      GT_MAILBOX_DATA       => 16#13_8128# / Register_Width,
+      GT_MAILBOX_DATA_1     => 16#13_812c# / Register_Width,
+
+      PCH_DP_B              => 16#0e_4100# / Register_Width,
+      PCH_DP_AUX_CTL_B      => 16#0e_4110# / Register_Width,
+      PCH_DP_AUX_DATA_B_1   => 16#0e_4114# / Register_Width,
+      PCH_DP_AUX_DATA_B_2   => 16#0e_4118# / Register_Width,
+      PCH_DP_AUX_DATA_B_3   => 16#0e_411c# / Register_Width,
+      PCH_DP_AUX_DATA_B_4   => 16#0e_4120# / Register_Width,
+      PCH_DP_AUX_DATA_B_5   => 16#0e_4124# / Register_Width,
+      PCH_DP_C              => 16#0e_4200# / Register_Width,
+      PCH_DP_AUX_CTL_C      => 16#0e_4210# / Register_Width,
+      PCH_DP_AUX_DATA_C_1   => 16#0e_4214# / Register_Width,
+      PCH_DP_AUX_DATA_C_2   => 16#0e_4218# / Register_Width,
+      PCH_DP_AUX_DATA_C_3   => 16#0e_421c# / Register_Width,
+      PCH_DP_AUX_DATA_C_4   => 16#0e_4220# / Register_Width,
+      PCH_DP_AUX_DATA_C_5   => 16#0e_4224# / Register_Width,
+      PCH_DP_D              => 16#0e_4300# / Register_Width,
+      PCH_DP_AUX_CTL_D      => 16#0e_4310# / Register_Width,
+      PCH_DP_AUX_DATA_D_1   => 16#0e_4314# / Register_Width,
+      PCH_DP_AUX_DATA_D_2   => 16#0e_4318# / Register_Width,
+      PCH_DP_AUX_DATA_D_3   => 16#0e_431c# / Register_Width,
+      PCH_DP_AUX_DATA_D_4   => 16#0e_4320# / Register_Width,
+      PCH_DP_AUX_DATA_D_5   => 16#0e_4324# / Register_Width,
+
+      -- watermark registers
+      WM1_LP_ILK            => 16#04_5108# / Register_Width,
+      WM2_LP_ILK            => 16#04_510c# / Register_Width,
+      WM3_LP_ILK            => 16#04_5110# / Register_Width,
+
+      -- audio VID/DID
+      AUD_VID_DID           => 16#06_5020# / Register_Width,
+      PCH_AUD_VID_DID       => 16#0e_5020# / Register_Width,
+
+      -- interrupt registers
+      DEISR                 => 16#04_4000# / Register_Width,
+      DEIMR                 => 16#04_4004# / Register_Width,
+      DEIIR                 => 16#04_4008# / Register_Width,
+      DEIER                 => 16#04_400c# / Register_Width,
+      GTISR                 => 16#04_4010# / Register_Width,
+      GTIMR                 => 16#04_4014# / Register_Width,
+      GTIIR                 => 16#04_4018# / Register_Width,
+      GTIER                 => 16#04_401c# / Register_Width,
+      SDEISR                => 16#0c_4000# / Register_Width,
+      SDEIMR                => 16#0c_4004# / Register_Width,
+      SDEIIR                => 16#0c_4008# / Register_Width,
+      SDEIER                => 16#0c_400c# / Register_Width,
+
+      -- I2C stuff
+      PCH_GMBUS0            => 16#0c_5100# / Register_Width,
+      PCH_GMBUS1            => 16#0c_5104# / Register_Width,
+      PCH_GMBUS2            => 16#0c_5108# / Register_Width,
+      PCH_GMBUS3            => 16#0c_510c# / Register_Width,
+      PCH_GMBUS4            => 16#0c_5110# / Register_Width,
+      PCH_GMBUS5            => 16#0c_5120# / Register_Width,
+
+      -- clock gating -- maybe have to touch this
+      DSPCLK_GATE_D         => 16#04_2020# / Register_Width,
+      PCH_FDI_CHICKEN_B_C   => 16#0c_2000# / Register_Width,
+      PCH_DSPCLK_GATE_D     => 16#0c_2020# / Register_Width,
+
+      -- hotplug and initial detection
+      HOTPLUG_CTL           => 16#04_4030# / Register_Width,
+      SHOTPLUG_CTL          => 16#0c_4030# / Register_Width,
+      SFUSE_STRAP           => 16#0c_2014# / Register_Width,
+
+      -- Render Engine Command Streamer
+      ARB_MODE              => 16#00_4030# / Register_Width,
+      HWS_PGA               => 16#00_4080# / Register_Width,
+      RCS_RING_BUFFER_TAIL  => 16#00_2030# / Register_Width,
+      VCS_RING_BUFFER_TAIL  => 16#01_2030# / Register_Width,
+      BCS_RING_BUFFER_TAIL  => 16#02_2030# / Register_Width,
+      RCS_RING_BUFFER_HEAD  => 16#00_2034# / Register_Width,
+      VCS_RING_BUFFER_HEAD  => 16#01_2034# / Register_Width,
+      BCS_RING_BUFFER_HEAD  => 16#02_2034# / Register_Width,
+      RCS_RING_BUFFER_STRT  => 16#00_2038# / Register_Width,
+      VCS_RING_BUFFER_STRT  => 16#01_2038# / Register_Width,
+      BCS_RING_BUFFER_STRT  => 16#02_2038# / Register_Width,
+      RCS_RING_BUFFER_CTL   => 16#00_203c# / Register_Width,
+      VCS_RING_BUFFER_CTL   => 16#01_203c# / Register_Width,
+      BCS_RING_BUFFER_CTL   => 16#02_203c# / Register_Width,
+      MI_MODE               => 16#00_209c# / Register_Width,
+      INSTPM                => 16#00_20c0# / Register_Width,
+      GAB_CTL_REG           => 16#02_4000# / Register_Width,
+      PP_DCLV_HIGH          => 16#00_2220# / Register_Width,
+      PP_DCLV_LOW           => 16#00_2228# / Register_Width,
+      VCS_PP_DCLV_HIGH      => 16#01_2220# / Register_Width,
+      VCS_PP_DCLV_LOW       => 16#01_2228# / Register_Width,
+      BCS_PP_DCLV_HIGH      => 16#02_2220# / Register_Width,
+      BCS_PP_DCLV_LOW       => 16#02_2228# / Register_Width,
+      QUIRK_42004           => 16#04_2004# / Register_Width,
+      UCGCTL1               => 16#00_9400# / Register_Width,
+      UCGCTL2               => 16#00_9404# / Register_Width,
+      MBCTL                 => 16#00_907c# / Register_Width,
+      HWSTAM                => 16#00_2098# / Register_Width,
+      VCS_HWSTAM            => 16#01_2098# / Register_Width,
+      BCS_HWSTAM            => 16#02_2098# / Register_Width,
+      IIR                   => 16#04_4028# / Register_Width,
+      PIPE_FRMCNT_A         => 16#07_0040# / Register_Width,
+      PIPE_FRMCNT_B         => 16#07_1040# / Register_Width,
+      PIPE_FRMCNT_C         => 16#07_2040# / Register_Width,
+      FBC_CTL               => 16#04_3208# / Register_Width,
+      PIPE_VSYNCSHIFT_A     => 16#06_0028# / Register_Width,
+      PIPE_VSYNCSHIFT_B     => 16#06_1028# / Register_Width,
+      PIPE_VSYNCSHIFT_C     => 16#06_2028# / Register_Width,
+      WM_PIPE_A             => 16#04_5100# / Register_Width,
+      WM_PIPE_B             => 16#04_5104# / Register_Width,
+      WM_PIPE_C             => 16#04_5200# / Register_Width,
+      PIPE_SCANLINE_A       => 16#07_0000# / Register_Width,
+      PIPE_SCANLINE_B       => 16#07_1000# / Register_Width,
+      PIPE_SCANLINE_C       => 16#07_2000# / Register_Width,
+      GFX_MODE              => 16#00_2520# / Register_Width,
+      CACHE_MODE_0          => 16#00_2120# / Register_Width,
+      SLEEP_PSMI_CONTROL    => 16#01_2050# / Register_Width,
+      CTX_SIZE              => 16#00_21a0# / Register_Width,
+      GAC_ECO_BITS          => 16#01_4090# / Register_Width,
+      GAM_ECOCHK            => 16#00_4090# / Register_Width,
+      QUIRK_02084           => 16#00_2084# / Register_Width,
+      QUIRK_02090           => 16#00_2090# / Register_Width,
+      GT_MODE               => 16#00_20d0# / Register_Width,
+      QUIRK_F0060           => 16#0f_0060# / Register_Width,
+      QUIRK_F1060           => 16#0f_1060# / Register_Width,
+      QUIRK_F2060           => 16#0f_2060# / Register_Width,
+      AUD_CNTRL_ST2         => 16#0e_50c0# / Register_Width,
+      AUD_CNTL_ST_A         => 16#0e_50b4# / Register_Width,
+      AUD_CNTL_ST_B         => 16#0e_51b4# / Register_Width,
+      AUD_CNTL_ST_C         => 16#0e_52b4# / Register_Width,
+      AUD_HDMIW_HDMIEDID_A  => 16#0e_5050# / Register_Width,
+      AUD_HDMIW_HDMIEDID_B  => 16#0e_5150# / Register_Width,
+      AUD_HDMIW_HDMIEDID_C  => 16#0e_5250# / Register_Width,
+      AUD_CONFIG_A          => 16#0e_5000# / Register_Width,
+      AUD_CONFIG_B          => 16#0e_5100# / Register_Width,
+      AUD_CONFIG_C          => 16#0e_5200# / Register_Width,
+      TRANS_DP_CTL_A        => 16#0e_0300# / Register_Width,
+      TRANS_DP_CTL_B        => 16#0e_1300# / Register_Width,
+      TRANS_DP_CTL_C        => 16#0e_2300# / Register_Width,
+      TRANS_VSYNCSHIFT_A    => 16#0e_0028# / Register_Width,
+      TRANS_VSYNCSHIFT_B    => 16#0e_1028# / Register_Width,
+      TRANS_VSYNCSHIFT_C    => 16#0e_2028# / Register_Width,
+      RAWCLK_FREQ           => 16#0c_6204# / Register_Width,
+      QUIRK_C2004           => 16#0c_2004# / Register_Width);
+
+   subtype Registers_Index is Registers_Invalid_Index range
+      Registers_Invalid_Index'Succ (Invalid_Register) ..
+      Registers_Invalid_Index'Last;
+
+   -- aliased registers
+   DP_CTL_A             : constant Registers_Index := DDI_BUF_CTL_A;
+   DP_AUX_CTL_A         : constant Registers_Index := DDI_AUX_CTL_A;
+   DP_AUX_DATA_A_1      : constant Registers_Index := DDI_AUX_DATA_A_1;
+   DP_AUX_DATA_A_2      : constant Registers_Index := DDI_AUX_DATA_A_2;
+   DP_AUX_DATA_A_3      : constant Registers_Index := DDI_AUX_DATA_A_3;
+   DP_AUX_DATA_A_4      : constant Registers_Index := DDI_AUX_DATA_A_4;
+   DP_AUX_DATA_A_5      : constant Registers_Index := DDI_AUX_DATA_A_5;
+
+   ---------------------------------------------------------------------------
+
+   Default_Timeout_MS : constant := 10;
+
+   ---------------------------------------------------------------------------
+
+   procedure Posting_Read
+      (Register : in     Registers_Index)
+   with
+      Global  => (In_Out => Register_State),
+      Depends => (Register_State =>+ (Register)),
+      Pre     => True,
+      Post    => True;
+
+   pragma Warnings (GNATprove, Off, "unused variable ""Verbose""",
+                    Reason => "Only used on debugging path");
+   procedure Read
+      (Register : in     Registers_Index;
+       Value    :    out Word32;
+       Verbose  : in     Boolean := True)
+   with
+      Global  => (In_Out => Register_State),
+      Depends => ((Value, Register_State) => (Register, Register_State),
+                  null  => Verbose),
+      Pre     => True,
+      Post    => True;
+   pragma Warnings (GNATprove, On, "unused variable ""Verbose""");
+
+   procedure Write
+      (Register : Registers_Index;
+       Value    : Word32)
+   with
+      Global  => (In_Out => Register_State),
+      Depends => (Register_State => (Register, Register_State, Value)),
+      Pre     => True,
+      Post    => True;
+
+   procedure Is_Set_Mask
+      (Register : in     Registers_Index;
+       Mask     : in     Word32;
+       Result   :    out Boolean);
+
+   pragma Warnings (GNATprove, Off, "unused initial value of ""Verbose""",
+                    Reason => "Only used on debugging path");
+   procedure Wait_Set_Mask
+      (Register : Registers_Index;
+       Mask     : Word32;
+       TOut_MS  : Natural := Default_Timeout_MS;
+       Verbose  : Boolean := False);
+
+   procedure Wait_Unset_Mask
+      (Register : Registers_Index;
+       Mask     : Word32;
+       TOut_MS  : Natural := Default_Timeout_MS;
+       Verbose  : Boolean := False);
+   pragma Warnings (GNATprove, On, "unused initial value of ""Verbose""");
+
+   procedure Set_Mask
+      (Register : Registers_Index;
+       Mask     : Word32);
+
+   procedure Unset_Mask
+      (Register : Registers_Index;
+       Mask     : Word32);
+
+   procedure Unset_And_Set_Mask
+      (Register   : Registers_Index;
+       Mask_Unset : Word32;
+       Mask_Set   : Word32);
+
+   pragma Warnings (Off, "declaration of ""Write_GTT"" hides one at *");
+   procedure Write_GTT
+     (GTT_Page       : GTT_Range;
+      Device_Address : GTT_Address_Type;
+      Valid          : Boolean)
+   with
+      Global  => (In_Out => GTT_State),
+      Depends => (GTT_State =>+ (GTT_Page, Device_Address, Valid)),
+      Pre     => True,
+      Post    => True;
+   pragma Warnings (On, "declaration of ""Write_GTT"" hides one at *");
+
+   procedure Set_Register_Base (Base : Word64)
+   with
+      Global   => (Output => Address_State),
+      Depends  => (Address_State => Base),
+      Pre      => True,
+      Post     => True;
+
+end HW.GFX.GMA.Registers;
diff --git a/common/hw-gfx-gma.adb b/common/hw-gfx-gma.adb
new file mode 100644
index 0000000..d004381
--- /dev/null
+++ b/common/hw-gfx-gma.adb
@@ -0,0 +1,836 @@
+--
+-- Copyright (C) 2014-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.EDID;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.Power_And_Clocks;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.PLLs;
+with HW.GFX.GMA.Port_Detect;
+with HW.GFX.GMA.Connectors;
+with HW.GFX.GMA.Connector_Info;
+with HW.GFX.GMA.Pipe_Setup;
+
+with System;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+use type HW.Word8;
+use type HW.Int32;
+
+package body HW.GFX.GMA
+   with Refined_State =>
+     (State =>
+        (Registers.Address_State,
+         PLLs.State, Panel.Panel_State,
+         Cur_Configs, Allocated_PLLs, DP_Links,
+         HPD_Delay, Wait_For_HPD),
+      Init_State => Initialized,
+      Config_State => Config.Valid_Port_GPU,
+      Device_State =>
+        (Registers.Register_State, Registers.GTT_State))
+is
+
+   subtype Port_Name is String (1 .. 8);
+   type Port_Name_Array is array (Port_Type) of Port_Name;
+   Port_Names : constant Port_Name_Array :=
+     (Disabled => "Disabled",
+      Internal => "Internal",
+      DP1      => "DP1     ",
+      DP2      => "DP2     ",
+      DP3      => "DP3     ",
+      Digital1 => "Digital1",
+      Digital2 => "Digital2",
+      Digital3 => "Digital3",
+      Analog   => "Analog  ");
+
+   package Display_Controller renames Pipe_Setup;
+
+   type PLLs_Type is array (Config_Index) of PLLs.T;
+
+   type Links_Type is array (Config_Index) of DP_Link;
+
+   type HPD_Type is array (Port_Type) of Boolean;
+   type HPD_Delay_Type is array (Port_Type) of Time.T;
+
+   Cur_Configs : Configs_Type;
+   Allocated_PLLs : PLLs_Type;
+   DP_Links : Links_Type;
+   HPD_Delay : HPD_Delay_Type;
+   Wait_For_HPD : HPD_Type;
+   Initialized : Boolean := False;
+
+   subtype Active_Port_Type is Port_Type range Port_Type'Succ (Disabled) .. Port_Type'Last;
+
+   ----------------------------------------------------------------------------
+
+   function To_GPU_Port
+     (Configs  : Configs_Type;
+      Idx      : Config_Index)
+      return GPU_Port
+   is
+   begin
+      return
+        (case Config.CPU is
+            when Ironlake .. Ivybridge => -- everything but eDP through FDI/PCH
+              (if Config.Internal_Is_EDP and then Configs (Idx).Port = Internal
+               then
+                  DIGI_A
+               else
+                 (case Idx is
+                     -- FDIs are fixed to the CPU pipe
+                     when Primary   => DIGI_B,
+                     when Secondary => DIGI_C,
+                     when Tertiary  => DIGI_D)),
+            when Haswell .. Skylake =>    -- everything but VGA directly on CPU
+              (case Configs (Idx).Port is
+                  when Disabled           => GPU_Port'First,
+                  when Internal           => DIGI_A,  -- LVDS not available
+                  when Digital1 | DP1     => DIGI_B,
+                  when Digital2 | DP2     => DIGI_C,
+                  when Digital3 | DP3     => DIGI_D,
+                  when Analog             => DIGI_E));
+   end To_GPU_Port;
+
+   function To_PCH_Port (Port : Active_Port_Type) return PCH_Port
+   is
+   begin
+      return
+        (case Port is
+            when Internal  => PCH_LVDS,   -- will be ignored if Internal is DP
+            when Analog    => PCH_DAC,
+            when Digital1  => PCH_HDMI_B,
+            when Digital2  => PCH_HDMI_C,
+            when Digital3  => PCH_HDMI_D,
+            when DP1       => PCH_DP_B,
+            when DP2       => PCH_DP_C,
+            when DP3       => PCH_DP_D);
+   end To_PCH_Port;
+
+   function To_Display_Type (Port : Active_Port_Type) return Display_Type
+   with Pre => True
+   is
+   begin
+      return
+        (case Port is
+            when Internal  => Config.Internal_Display,
+            when Analog    => VGA,
+            when Digital1 |
+                 Digital2 |
+                 Digital3  => HDMI,
+            when DP1 |
+                 DP2 |
+                 DP3       => DP);
+   end To_Display_Type;
+
+   procedure Configure_FDI_Link
+     (Port_Cfg : in out Port_Config;
+      Success  :    out Boolean)
+   with Pre => True
+   is
+      procedure Limit_Lane_Count
+      is
+         FDI_TX_CTL_FDI_TX_ENABLE : constant := 1 * 2 ** 31;
+         Enabled : Boolean;
+      begin
+         -- if DIGI_D enabled: (FDI names are off by one)
+         Registers.Is_Set_Mask
+           (Register => Registers.FDI_TX_CTL_C,
+            Mask     => FDI_TX_CTL_FDI_TX_ENABLE,
+            Result   => Enabled);
+         if Enabled then
+            Port_Cfg.FDI.Receiver_Caps.Max_Lane_Count := DP_Lane_Count_2;
+         end if;
+      end Limit_Lane_Count;
+   begin
+      Port_Cfg.FDI.Receiver_Caps.Max_Link_Rate    := DP_Bandwidth_2_7;
+      Port_Cfg.FDI.Receiver_Caps.Max_Lane_Count   :=
+         Config.FDI_Lane_Count (Port_Cfg.Port);
+      Port_Cfg.FDI.Receiver_Caps.Enhanced_Framing := True;
+      if Config.Has_FDI_C and then Port_Cfg.Port = DIGI_C then
+         Limit_Lane_Count;
+      end if;
+      DP_Info.Preferred_Link_Setting (Port_Cfg.FDI, Port_Cfg.Mode, Success);
+   end Configure_FDI_Link;
+
+   procedure Fill_Port_Config
+     (Port_Cfg :    out Port_Config;
+      Configs  : in     Configs_Type;
+      Idx      : in     Config_Index;
+      Success  :    out Boolean)
+   with Pre => True
+   is
+   begin
+      Success :=
+         Config.Supported_Pipe (Idx) and then
+         Config.Valid_Port (Configs (Idx).Port) and then
+         Configs (Idx).Port /= Disabled;
+
+      if Success then
+         declare
+            Port : constant Port_Type := Configs (Idx).Port;
+            Mode : constant Mode_Type := Configs (Idx).Mode;
+            Link : constant DP_Link := DP_Links (Idx);
+         begin
+            Port_Cfg := Port_Config'
+              (Port     => To_GPU_Port (Configs, Idx),
+               PCH_Port => To_PCH_Port (Port),
+               Display  => To_Display_Type (Port),
+               Mode     => Mode,
+               Is_FDI   => Config.FDI_Port (To_GPU_Port (Configs, Idx)),
+               FDI      => Default_DP,
+               DP       => Link);
+            if Port_Cfg.Mode.BPC = Auto_BPC then
+               Port_Cfg.Mode.BPC := Connector_Info.Default_BPC (Port_Cfg);
+            end if;
+         end;
+      else
+         Port_Cfg := Port_Config'
+           (Port     => GPU_Port'First,
+            PCH_Port => PCH_Port'First,
+            Display  => Display_Type'First,
+            Mode     => Invalid_Mode,
+            Is_FDI   => False,
+            FDI      => Default_DP,
+            DP       => Default_DP);
+      end if;
+   end Fill_Port_Config;
+
+   ----------------------------------------------------------------------------
+
+   function To_Controller
+      (Dsp_Config : Config_Index) return Display_Controller.Controller_Type
+   is
+      Result : Display_Controller.Controller_Type;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      case Dsp_Config is
+         when Primary =>
+            Result := Display_Controller.Controllers (Display_Controller.A);
+         when Secondary =>
+            Result := Display_Controller.Controllers (Display_Controller.B);
+         when Tertiary =>
+            Result := Display_Controller.Controllers (Display_Controller.C);
+      end case;
+      return Result;
+   end To_Controller;
+
+   ----------------------------------------------------------------------------
+
+   function To_Head
+     (N_Config : Config_Index;
+      Port     : Active_Port_Type)
+      return Display_Controller.Head_Type
+   is
+      Result : Display_Controller.Head_Type;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.Has_EDP_Pipe and then Port = Internal then
+         Result := Display_Controller.Heads (Display_Controller.Head_EDP);
+      else
+         case N_Config is
+            when Primary =>
+               Result := Display_Controller.Heads (Display_Controller.Head_A);
+            when Secondary =>
+               Result := Display_Controller.Heads (Display_Controller.Head_B);
+            when Tertiary =>
+               Result := Display_Controller.Heads (Display_Controller.Head_C);
+         end case;
+      end if;
+      return Result;
+   end To_Head;
+
+   ----------------------------------------------------------------------------
+
+   procedure Legacy_VGA_Off
+   is
+      Reg8 : Word8;
+   begin
+      -- disable legacy VGA plane, taking over control now
+      Port_IO.OutB (VGA_SR_INDEX, VGA_SR01);
+      Port_IO.InB  (Reg8, VGA_SR_DATA);
+      Port_IO.OutB (VGA_SR_DATA, Reg8 or 1 * 2 ** 5);
+      Time.U_Delay (100); -- PRM says 100us, Linux does 300
+      Registers.Set_Mask (Registers.VGACNTRL, 1 * 2 ** 31);
+   end Legacy_VGA_Off;
+
+   ----------------------------------------------------------------------------
+
+   function Port_Configured
+     (Configs  : Configs_Type;
+      Port     : Port_Type)
+      return Boolean
+   with
+      Global => null
+   is
+   begin
+      return Configs (Primary).Port    = Port or
+             Configs (Secondary).Port  = Port or
+             Configs (Tertiary).Port   = Port;
+   end Port_Configured;
+
+   procedure Scan_Ports
+     (Configs  :    out Configs_Type;
+      Ports    : in     Port_List)
+   is
+      Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
+      Port_Idx : Port_List_Range := Port_List_Range'First;
+      Port_Cfg : Port_Config;
+      Success  : Boolean := False;
+   begin
+      Configs := (Config_Index =>
+                    (Port        => Disabled,
+                     Mode        => Invalid_Mode,
+                     Framebuffer => Default_FB));
+
+      for Config_Idx in Config_Index loop
+         while Ports (Port_Idx) /= Disabled loop
+            if not Port_Configured (Configs, Ports (Port_Idx)) then
+               Configs (Config_Idx).Port := Ports (Port_Idx);
+               Fill_Port_Config (Port_Cfg, Configs, Config_Idx, Success);
+
+               if Success then
+                  -- May need power to probe port
+                  if Port_Cfg.Display = DP then
+                     Power_And_Clocks.Power_Up (Cur_Configs, Configs);
+                  end if;
+                  if Ports (Port_Idx) = Internal then
+                     Panel.On;
+                  end if;
+
+                  Connector_Info.Read_EDID (Raw_EDID, Port_Cfg, Success);
+               end if;
+
+               if Success and then EDID.Has_Preferred_Mode (Raw_EDID) then
+                  Configs (Config_Idx).Mode := EDID.Preferred_Mode (Raw_EDID);
+               else
+                  Configs (Config_Idx).Port := Disabled;
+
+                  if Ports (Port_Idx) = Internal and
+                     not Port_Configured (Cur_Configs, Internal)
+                  then
+                     Panel.Off;
+                  end if;
+               end if;
+            end if;
+
+            exit when Port_Idx = Port_List_Range'Last;
+            Port_Idx := Port_List_Range'Succ (Port_Idx);
+
+            exit when Success;
+         end loop;
+      end loop;
+
+      Power_And_Clocks.Power_Set_To (Cur_Configs);
+   end Scan_Ports;
+
+   procedure Auto_Configure
+     (Configs     : in out Configs_Type;
+      Keep_Power  : in     Boolean := False)
+   is
+      Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
+      Success : Boolean;
+
+      Config_Idx : Config_Index;
+      Port_Cfg : Port_Config;
+
+      function Free_Config return Boolean
+      with
+         Pre => True
+      is
+      begin
+         return Port_Configured (Configs, Disabled);
+      end Free_Config;
+
+      function First_Free_Config return Config_Index
+      with
+         Pre => Free_Config
+      is
+      begin
+         return (if Configs (Primary).Port = Disabled then Primary else
+                 (if Configs (Secondary).Port = Disabled then Secondary
+                  else Tertiary));
+      end First_Free_Config;
+   begin
+      -- TODO: Only check ports with hot-plug event?
+
+      if Config.Has_Internal_Display and then
+         not Keep_Power and then
+         not Port_Configured (Cur_Configs, Internal)
+      then
+         Panel.On (Wait => False);
+      end if;
+
+      -- Check if displays are still connected
+      for I in Config_Index loop
+         if Configs (I).Port /= Disabled then
+            Fill_Port_Config (Port_Cfg, Configs, I, Success);
+            if Success then
+               Connector_Info.Read_EDID
+                 (Raw_EDID => Raw_EDID,
+                  Port_Cfg => Port_Cfg,
+                  Success  => Success);
+            end if;
+            if not Success or else
+               not EDID.Has_Preferred_Mode (Raw_EDID) or else
+               Configs (I).Mode /= EDID.Preferred_Mode (Raw_EDID)
+            then
+               Configs (I).Port := Disabled;
+            end if;
+         end if;
+      end loop;
+
+      -- Add new displays as long as there is a free pipe config
+      for Port in Active_Port_Type loop
+         if Free_Config and then not Port_Configured (Configs, Port) then
+            Config_Idx := First_Free_Config;
+            Configs (Config_Idx).Port := Port;
+            Fill_Port_Config (Port_Cfg, Configs, Config_Idx, Success);
+
+            if Success then
+               -- Need power to probe port
+               if not Keep_Power and then To_Display_Type (Port) = DP then
+                  Power_And_Clocks.Power_Up (Cur_Configs, Configs);
+               end if;
+               if not Keep_Power and then Port = Internal then
+                  Panel.Wait_On;
+               end if;
+
+               Connector_Info.Read_EDID
+                 (Raw_EDID => Raw_EDID,
+                  Port_Cfg => Port_Cfg,
+                  Success  => Success);
+            end if;
+
+            if Success and then EDID.Has_Preferred_Mode (Raw_EDID) then
+               Configs (Config_Idx) := Config_Type'
+                 (Port        => Port,
+                  Framebuffer => Configs (Config_Idx).Framebuffer,
+                  Mode        => EDID.Preferred_Mode (Raw_EDID));
+            else
+               Configs (Config_Idx).Port := Disabled;
+            end if;
+         end if;
+      end loop;
+
+      if not Keep_Power then
+         Power_And_Clocks.Power_Set_To (Cur_Configs);
+
+         if Config.Has_Internal_Display and then
+            not Port_Configured (Cur_Configs, Internal)
+         then
+            Panel.Off;
+         end if;
+      end if;
+   end Auto_Configure;
+
+   ----------------------------------------------------------------------------
+
+   procedure Update_Outputs (Configs : Configs_Type)
+   is
+      Did_Power_Up : Boolean := False;
+
+      HPD, HPD_Delay_Over, Success : Boolean;
+      Old_Config, New_Config : Config_Type;
+      Old_Configs : Configs_Type;
+      Port_Cfg : Port_Config;
+
+      procedure Check_HPD
+        (Port_Cfg : in     Port_Config;
+         Port     : in     Port_Type;
+         Detected :    out Boolean)
+      is
+      begin
+         HPD_Delay_Over := Time.Timed_Out (HPD_Delay (Port));
+         if HPD_Delay_Over then
+            Port_Detect.Hotplug_Detect (Port_Cfg, Detected);
+            HPD_Delay (Port) := Time.MS_From_Now (333);
+         else
+            Detected := False;
+         end if;
+      end Check_HPD;
+   begin
+      Old_Configs := Cur_Configs;
+
+      for I in Config_Index loop
+         HPD := False;
+
+         Old_Config := Cur_Configs (I);
+         New_Config := Configs (I);
+
+         Fill_Port_Config (Port_Cfg, Old_Configs, I, Success);
+         if Success then
+            Check_HPD (Port_Cfg, Old_Config.Port, HPD);
+         end if;
+
+         -- Connector changed?
+         if (Success and then HPD) or
+            Old_Config.Port /= New_Config.Port or
+            Old_Config.Mode /= New_Config.Mode
+         then
+            if Old_Config.Port /= Disabled then
+               if Success then
+                  pragma Debug (Debug.New_Line);
+                  pragma Debug (Debug.Put_Line
+                    ("Disabling port " & Port_Names (Old_Config.Port)));
+
+                  Connectors.Pre_Off (Port_Cfg);
+
+                  Display_Controller.Off
+                    (To_Controller (I), To_Head (I, Old_Config.Port));
+
+                  Connectors.Post_Off (Port_Cfg);
+               end if;
+
+               -- Free PLL
+               PLLs.Free (Allocated_PLLs (I));
+
+               Cur_Configs (I).Port := Disabled;
+            end if;
+
+            if New_Config.Port /= Disabled then
+               Fill_Port_Config (Port_Cfg, Configs, I, Success);
+
+               if Success and then Wait_For_HPD (New_Config.Port) then
+                  Check_HPD (Port_Cfg, New_Config.Port, Success);
+                  Wait_For_HPD (New_Config.Port) := not Success;
+               end if;
+
+               if Success then
+                  pragma Debug (Debug.New_Line);
+                  pragma Debug (Debug.Put_Line
+                    ("Trying to enable port " & Port_Names (New_Config.Port)));
+
+                  if not Did_Power_Up then
+                     Power_And_Clocks.Power_Up (Old_Configs, Configs);
+                     Did_Power_Up := True;
+                  end if;
+
+                  if Port_Cfg.Is_FDI then
+                     Configure_FDI_Link (Port_Cfg, Success);
+                  end if;
+               end if;
+
+               if Success then
+                  Connector_Info.Preferred_Link_Setting
+                    (Port_Cfg => Port_Cfg,
+                     Success  => Success);
+               end if;
+
+               while Success loop
+                  pragma Loop_Invariant (New_Config.Port in Active_Port_Type);
+
+                  PLLs.Alloc
+                    (Port_Cfg => Port_Cfg,
+                     PLL      => Allocated_PLLs (I),
+                     Success  => Success);
+
+                  if Success then
+                     for Try in 1 .. 2 loop
+                        pragma Loop_Invariant
+                          (New_Config.Port in Active_Port_Type);
+
+                        Connectors.Pre_On
+                          (Port_Cfg    => Port_Cfg,
+                           PLL_Hint    => PLLs.Register_Value
+                                            (Allocated_PLLs (I)),
+                           Pipe_Hint   => Display_Controller.Get_Pipe_Hint
+                                            (To_Head (I, New_Config.Port)),
+                           Success     => Success);
+
+                        if Success then
+                           Display_Controller.On
+                             (Controller  => To_Controller (I),
+                              Head        => To_Head (I, New_Config.Port),
+                              Port_Cfg    => Port_Cfg,
+                              Framebuffer => New_Config.Framebuffer);
+
+                           Connectors.Post_On
+                             (Port_Cfg => Port_Cfg,
+                              PLL_Hint => PLLs.Register_Value
+                                            (Allocated_PLLs (I)),
+                              Success  => Success);
+
+                           if not Success then
+                              Display_Controller.Off
+                                (To_Controller (I),
+                                 To_Head (I, New_Config.Port));
+                              Connectors.Post_Off (Port_Cfg);
+                           end if;
+                        end if;
+
+                        exit when Success;
+                     end loop;
+                     exit when Success;   -- connection established => stop loop
+
+                     -- connection failed
+                     PLLs.Free (Allocated_PLLs (I));
+                  end if;
+
+                  Connector_Info.Next_Link_Setting
+                    (Port_Cfg => Port_Cfg,
+                     Success  => Success);
+               end loop;
+
+               if Success then
+                  pragma Debug (Debug.Put_Line
+                    ("Enabled port " & Port_Names (New_Config.Port)));
+                  Cur_Configs (I) := New_Config;
+                  DP_Links (I) := Port_Cfg.DP;
+               else
+                  Wait_For_HPD (New_Config.Port) := True;
+                  if New_Config.Port = Internal then
+                     Panel.Off;
+                  end if;
+               end if;
+            else
+               Cur_Configs (I) := New_Config;
+            end if;
+         elsif Old_Config.Framebuffer /= New_Config.Framebuffer and
+               Old_Config.Port /= Disabled
+         then
+            Display_Controller.Update_Offset
+              (Controller  => To_Controller (I),
+               Framebuffer => New_Config.Framebuffer);
+            Cur_Configs (I) := New_Config;
+         end if;
+      end loop;
+
+      if Did_Power_Up then
+         Power_And_Clocks.Power_Down (Old_Configs, Configs, Cur_Configs);
+      end if;
+
+   end Update_Outputs;
+
+   ----------------------------------------------------------------------------
+
+   procedure Initialize
+     (MMIO_Base   : in     Word64 := 0;
+      Write_Delay : in     Word64 := 0;
+      Success     :    out Boolean)
+   with
+      Refined_Global =>
+        (In_Out =>
+           (Config.Valid_Port_GPU,
+            Registers.Register_State, Port_IO.State),
+         Input =>
+           (Time.State),
+         Output =>
+           (Registers.Address_State,
+            PLLs.State, Panel.Panel_State,
+            Cur_Configs, Allocated_PLLs, DP_Links,
+            HPD_Delay, Wait_For_HPD, Initialized))
+   is
+      use type HW.Word64;
+
+      Now : constant Time.T := Time.Now;
+
+      procedure Check_Platform (Success : out Boolean)
+      is
+         Audio_VID_DID : Word32;
+      begin
+         case Config.CPU is
+            when Haswell .. Skylake =>
+               Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
+            when Ironlake .. Ivybridge =>
+               Registers.Read (Registers.PCH_AUD_VID_DID, Audio_VID_DID);
+         end case;
+         Success :=
+           (case Config.CPU is
+               when Skylake      => Audio_VID_DID = 16#8086_2809#,
+               when Broadwell    => Audio_VID_DID = 16#8086_2808#,
+               when Haswell      => Audio_VID_DID = 16#8086_2807#,
+               when Ivybridge |
+                    Sandybridge  => Audio_VID_DID = 16#8086_2806# or
+                                    Audio_VID_DID = 16#8086_2805#,
+               when Ironlake     => Audio_VID_DID = 16#8086_2804#);  -- not sure
+      end Check_Platform;
+   begin
+      pragma Warnings (GNATprove, Off, "unused variable ""Write_Delay""",
+         Reason => "Write_Delay is used for debugging only");
+
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
+
+      Wait_For_HPD := HPD_Type'(others => False);
+      HPD_Delay := HPD_Delay_Type'(others => Now);
+      DP_Links := Links_Type'(others => HW.GFX.Default_DP);
+      Allocated_PLLs := (others => PLLs.Invalid);
+      Cur_Configs := Configs_Type'
+        (others => Config_Type'
+           (Port        => Disabled,
+            Framebuffer => HW.GFX.Default_FB,
+            Mode        => HW.GFX.Invalid_Mode));
+      Registers.Set_Register_Base
+        (if MMIO_Base /= 0 then
+            MMIO_Base
+         else
+            Config.Default_MMIO_Base);
+      PLLs.Initialize;
+
+      Check_Platform (Success);
+      if not Success then
+         pragma Debug (Debug.Put_Line ("ERROR: Incompatible CPU or PCH."));
+
+         Panel.Static_Init;   -- for flow analysis
+
+         Initialized := False;
+         return;
+      end if;
+
+      Panel.Setup_PP_Sequencer;
+      Port_Detect.Initialize;
+
+      Power_And_Clocks.Pre_All_Off;
+
+      Legacy_VGA_Off;
+
+      Connectors.Pre_All_Off;
+      Display_Controller.All_Off;
+      Connectors.Post_All_Off;
+      PLLs.All_Off;
+
+      Power_And_Clocks.Post_All_Off;
+
+      -------------------- Now restart from a clean state ---------------------
+      Power_And_Clocks.Initialize;
+
+      Initialized := True;
+
+   end Initialize;
+
+   function Is_Initialized return Boolean
+   with
+      Refined_Post => Is_Initialized'Result = Initialized
+   is
+   begin
+      return Initialized;
+   end Is_Initialized;
+
+   ----------------------------------------------------------------------------
+
+   procedure Write_GTT
+     (GTT_Page       : GTT_Range;
+      Device_Address : GTT_Address_Type;
+      Valid          : Boolean) is
+   begin
+      Registers.Write_GTT (GTT_Page, Device_Address, Valid);
+   end Write_GTT;
+
+   procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32)
+   is
+      FB_Size : constant Pos32 :=
+         FB.Stride * FB.Height * Pos32 (((FB.BPC * 4) / 8));
+      Phys_Addr : GTT_Address_Type := GTT_Address_Type (Phys_FB);
+   begin
+      for Idx in GTT_Range range 0 .. GTT_Range (((FB_Size + 4095) / 4096) - 1)
+      loop
+         Registers.Write_GTT
+           (GTT_Page       => Idx,
+            Device_Address => Phys_Addr,
+            Valid          => True);
+         Phys_Addr := Phys_Addr + 4096;
+      end loop;
+   end Setup_Default_GTT;
+
+   ----------------------------------------------------------------------------
+
+   procedure Dump_Configs (Configs : Configs_Type)
+   is
+      subtype Pipe_Name is String (1 .. 9);
+      type Pipe_Name_Array is array (Config_Index) of Pipe_Name;
+      Pipe_Names : constant Pipe_Name_Array :=
+        (Primary     => "Primary  ",
+         Secondary   => "Secondary",
+         Tertiary    => "Tertiary ");
+   begin
+      Debug.New_Line;
+      Debug.Put_Line ("CONFIG => ");
+      for Pipe in Config_Index loop
+         if Pipe = Config_Index'First then
+            Debug.Put ("  (");
+         else
+            Debug.Put ("   ");
+         end if;
+         Debug.Put_Line (Pipe_Names (Pipe) & " =>");
+         Debug.Put_Line
+           ("     (Port => " & Port_Names (Configs (Pipe).Port) & ",");
+         Debug.Put_Line ("      Framebuffer =>");
+         Debug.Put ("        (Width  => ");
+         Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
+         Debug.Put_Line (",");
+         Debug.Put ("         Height => ");
+         Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
+         Debug.Put_Line (",");
+         Debug.Put ("         Stride => ");
+         Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
+         Debug.Put_Line (",");
+         Debug.Put ("         Offset => ");
+         Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
+         Debug.Put_Line (",");
+         Debug.Put ("         BPC    => ");
+         Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
+         Debug.Put_Line ("),");
+         Debug.Put_Line ("      Mode =>");
+         Debug.Put ("        (Dotclock           => ");
+         Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
+         Debug.Put_Line (",");
+         Debug.Put ("         H_Visible          => ");
+         Debug.Put_Int16 (Configs (Pipe).Mode.H_Visible);
+         Debug.Put_Line (",");
+         Debug.Put ("         H_Sync_Begin       => ");
+         Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_Begin);
+         Debug.Put_Line (",");
+         Debug.Put ("         H_Sync_End         => ");
+         Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_End);
+         Debug.Put_Line (",");
+         Debug.Put ("         H_Total            => ");
+         Debug.Put_Int16 (Configs (Pipe).Mode.H_Total);
+         Debug.Put_Line (",");
+         Debug.Put ("         V_Visible          => ");
+         Debug.Put_Int16 (Configs (Pipe).Mode.V_Visible);
+         Debug.Put_Line (",");
+         Debug.Put ("         V_Sync_Begin       => ");
+         Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_Begin);
+         Debug.Put_Line (",");
+         Debug.Put ("         V_Sync_End         => ");
+         Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_End);
+         Debug.Put_Line (",");
+         Debug.Put ("         V_Total            => ");
+         Debug.Put_Int16 (Configs (Pipe).Mode.V_Total);
+         Debug.Put_Line (",");
+         Debug.Put_Line ("         H_Sync_Active_High => " &
+           (if Configs (Pipe).Mode.H_Sync_Active_High
+            then "True,"
+            else "False,"));
+         Debug.Put_Line ("         V_Sync_Active_High => " &
+           (if Configs (Pipe).Mode.V_Sync_Active_High
+            then "True,"
+            else "False,"));
+         Debug.Put ("         BPC                => ");
+         Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
+         if Pipe /= Config_Index'Last then
+            Debug.Put_Line (")),");
+         else
+            Debug.Put_Line (")));");
+         end if;
+      end loop;
+   end Dump_Configs;
+
+end HW.GFX.GMA;
diff --git a/common/hw-gfx-gma.ads b/common/hw-gfx-gma.ads
new file mode 100644
index 0000000..465cf08
--- /dev/null
+++ b/common/hw-gfx-gma.ads
@@ -0,0 +1,138 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+with HW.Port_IO;
+
+package HW.GFX.GMA
+with
+   Abstract_State =>
+     (State,
+      Init_State,
+      Config_State,
+      (Device_State with External)),
+   Initializes =>
+     (Init_State,
+      Config_State)
+is
+
+   type CPU_Type is
+     (Ironlake,
+      Sandybridge,
+      Ivybridge,
+      Haswell,
+      Broadwell,
+      Skylake);
+
+   type CPU_Variant is (Normal, ULT);
+
+   type Port_Type is
+     (Disabled,
+      Internal,
+      DP1,
+      DP2,
+      DP3,
+      Digital1,
+      Digital2,
+      Digital3,
+      Analog);
+   type Port_List_Range is range 0 .. 7;
+   type Port_List is array (Port_List_Range) of Port_Type;
+
+   type Config_Type is record
+      Port        : Port_Type;
+      Framebuffer : Framebuffer_Type;
+      Mode        : Mode_Type;
+   end record;
+   type Config_Index is (Primary, Secondary, Tertiary);
+   type Configs_Type is array (Config_Index) of Config_Type;
+
+   procedure Initialize
+     (MMIO_Base   : in     Word64 := 0;
+      Write_Delay : in     Word64 := 0;
+      Success     :    out Boolean)
+   with
+      Global =>
+        (In_Out => (Config_State, Device_State, Port_IO.State),
+         Output => (State, Init_State),
+         Input  => (Time.State)),
+      Post => Success = Is_Initialized;
+   function Is_Initialized return Boolean
+   with
+      Global => (Input => Init_State);
+
+   procedure Legacy_VGA_Off;
+
+   procedure Scan_Ports
+     (Configs  :    out Configs_Type;
+      Ports    : in     Port_List);
+   procedure Auto_Configure
+     (Configs     : in out Configs_Type;
+      Keep_Power  : in     Boolean := False);
+   procedure Update_Outputs (Configs : Configs_Type);
+
+   pragma Warnings (GNATprove, Off, "subprogram ""Dump_Configs"" has no effect",
+                    Reason => "It's only used for debugging");
+   procedure Dump_Configs (Configs : Configs_Type);
+
+   type GTT_Address_Type is mod 2 ** 39;
+   type GTT_Range is range 0 .. 16#8_0000# - 1;
+   procedure Write_GTT
+     (GTT_Page       : GTT_Range;
+      Device_Address : GTT_Address_Type;
+      Valid          : Boolean);
+
+   procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32);
+
+private
+
+   type GPU_Port is (DIGI_A, DIGI_B, DIGI_C, DIGI_D, DIGI_E);
+
+   subtype Digital_Port is GPU_Port range DIGI_A .. DIGI_E;
+
+   type PCH_Port is
+     (PCH_DAC, PCH_LVDS,
+      PCH_HDMI_B, PCH_HDMI_C, PCH_HDMI_D,
+      PCH_DP_B, PCH_DP_C, PCH_DP_D);
+
+   subtype PCH_HDMI_Port is PCH_Port range PCH_HDMI_B .. PCH_HDMI_D;
+   subtype PCH_DP_Port is PCH_Port range PCH_DP_B .. PCH_DP_D;
+
+   type Display_Type is (None, LVDS, DP, HDMI, VGA);
+
+   subtype Internal_Type is Display_Type range None .. DP;
+
+   type Port_Config is
+      record
+         Port     : GPU_Port;
+         PCH_Port : GMA.PCH_Port;
+         Display  : Display_Type;
+         Mode     : Mode_Type;
+         Is_FDI   : Boolean;
+         FDI      : DP_Link;
+         DP       : DP_Link;
+      end record;
+
+   type FDI_Training_Type is (Simple_Training, Full_Training, Auto_Training);
+
+   ----------------------------------------------------------------------------
+
+   type DP_Port is (DP_A, DP_B, DP_C, DP_D);
+
+   ----------------------------------------------------------------------------
+
+   VGA_SR_INDEX   : constant Port_IO.Port_Type  := 16#03c4#;
+   VGA_SR_DATA    : constant Port_IO.Port_Type  := 16#03c5#;
+   VGA_SR01       : constant Word8 := 16#01#;
+
+end HW.GFX.GMA;
diff --git a/common/hw-gfx-i2c.ads b/common/hw-gfx-i2c.ads
new file mode 100644
index 0000000..db5e5cc
--- /dev/null
+++ b/common/hw-gfx-i2c.ads
@@ -0,0 +1,21 @@
+--
+-- Copyright (C) 2015 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.I2C is
+
+   type Transfer_Address is mod 2 ** 7;
+   subtype Transfer_Length is Natural range 0 .. 128;
+   subtype Transfer_Index is Natural range 0 .. Transfer_Length'Last - 1;
+   subtype Transfer_Data is Buffer (Transfer_Index);
+
+end HW.GFX.I2C;
diff --git a/common/hw-gfx.ads b/common/hw-gfx.ads
new file mode 100644
index 0000000..da0d6fb
--- /dev/null
+++ b/common/hw-gfx.ads
@@ -0,0 +1,155 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW;
+
+use type HW.Pos64;
+use type HW.Word32;
+
+package HW.GFX is
+
+   -- implementation only supports 4800p for now ;-)
+   subtype Width_Type  is Pos32 range 1 .. 4800;
+   subtype Height_Type is Pos32 range 1 .. 7680;
+
+   Auto_BPC : constant := 5;
+   subtype BPC_Type    is Int64 range Auto_BPC .. 16;
+
+   type Framebuffer_Type is
+   record
+      Width  : Width_Type;
+      Height : Height_Type;
+      BPC    : BPC_Type;
+      Stride : Width_Type;
+      Offset : Word32;
+   end record;
+
+   Default_FB : constant Framebuffer_Type := Framebuffer_Type'
+      (Width  => 1,
+       Height => 1,
+       BPC    => 8,
+       Stride => 1,
+       Offset => 0);
+
+   subtype Frequency_Type is Pos64 range 25_000_000 .. 600_000_000;
+
+   type DP_Lane_Count is (DP_Lane_Count_1, DP_Lane_Count_2, DP_Lane_Count_4);
+   subtype DP_Lane_Count_Type is Pos64 range 1 .. 4;
+   type DP_Lane_Count_Integers is array (DP_Lane_Count) of DP_Lane_Count_Type;
+   Lane_Count_As_Integer : constant DP_Lane_Count_Integers :=
+      DP_Lane_Count_Integers'
+        (DP_Lane_Count_1 => 1, DP_Lane_Count_2 => 2, DP_Lane_Count_4 => 4);
+
+   type DP_Bandwidth is (DP_Bandwidth_1_62, DP_Bandwidth_2_7, DP_Bandwidth_5_4);
+   for DP_Bandwidth use
+     (DP_Bandwidth_1_62 => 6, DP_Bandwidth_2_7 => 10, DP_Bandwidth_5_4 => 20);
+   for DP_Bandwidth'Size use 8;
+   subtype DP_Symbol_Rate_Type is Pos64 range 1 .. 810_000_000;
+   type DP_Symbol_Rate_Array is array (DP_Bandwidth) of DP_Symbol_Rate_Type;
+   DP_Symbol_Rate : constant DP_Symbol_Rate_Array := DP_Symbol_Rate_Array'
+     (DP_Bandwidth_1_62 => 162_000_000,
+      DP_Bandwidth_2_7  => 270_000_000,
+      DP_Bandwidth_5_4  => 540_000_000);
+
+   type DP_Caps is record
+      Rev               : Word8;
+      Max_Link_Rate     : DP_Bandwidth;
+      Max_Lane_Count    : DP_Lane_Count;
+      TPS3_Supported    : Boolean;
+      Enhanced_Framing  : Boolean;
+      No_Aux_Handshake  : Boolean;
+      Aux_RD_Interval   : Word8;
+   end record;
+
+   type DP_Link is
+      record
+         Receiver_Caps           : DP_Caps;
+         Lane_Count              : DP_Lane_Count;
+         Bandwidth               : DP_Bandwidth;
+         Enhanced_Framing        : Boolean;
+         Opportunistic_Training  : Boolean;
+      end record;
+   Default_DP : constant DP_Link := DP_Link'
+     (Receiver_Caps           => DP_Caps'
+        (Rev               => 16#00#,
+         Max_Link_Rate     => DP_Bandwidth'First,
+         Max_Lane_Count    => DP_Lane_Count'First,
+         TPS3_Supported    => False,
+         Enhanced_Framing  => False,
+         No_Aux_Handshake  => False,
+         Aux_RD_Interval   => 16#00#),
+      Lane_Count              => DP_Lane_Count'First,
+      Bandwidth               => DP_Bandwidth'First,
+      Enhanced_Framing        => False,
+      Opportunistic_Training  => False);
+
+   type Mode_Type is
+   record
+      Dotclock             : Frequency_Type;
+      H_Visible            : Pos16;
+      H_Sync_Begin         : Pos16;
+      H_Sync_End           : Pos16;
+      H_Total              : Pos16;
+      V_Visible            : Pos16;
+      V_Sync_Begin         : Pos16;
+      V_Sync_End           : Pos16;
+      V_Total              : Pos16;
+      H_Sync_Active_High   : Boolean;
+      V_Sync_Active_High   : Boolean;
+      BPC                  : BPC_Type;
+   end record;
+
+   ----------------------------------------------------------------------------
+   -- Constants
+   ----------------------------------------------------------------------------
+
+   -- modeline constants
+   -- Dotclock is calculated using: Refresh_Rate * H_Total * V_Total
+
+   M2560x1600_60 : constant Mode_Type := Mode_Type'
+      (60*(2720*1646), 2560, 2608, 2640, 2720, 1600, 1603, 1609, 1646, True, True, Auto_BPC);
+
+   M2560x1440_60 : constant Mode_Type := Mode_Type'
+      (60*(2720*1481), 2560, 2608, 2640, 2720, 1440, 1443, 1448, 1481, True, False, Auto_BPC);
+
+   M1920x1200_60 : constant Mode_Type := Mode_Type'
+      (60*(2080*1235), 1920, 1968, 2000, 2080, 1200, 1203, 1209, 1235, False, False, Auto_BPC);
+
+   M1920x1080_60 : constant Mode_Type := Mode_Type'
+      (60*(2185*1135), 1920, 2008, 2052, 2185, 1080, 1084, 1089, 1135, False, False, Auto_BPC);
+
+   M1680x1050_60 : constant Mode_Type := Mode_Type'
+      (60*(2256*1087), 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, False, True, Auto_BPC);
+
+   M1600x1200_60 : constant Mode_Type := Mode_Type'
+      (60*(2160*1250), 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, True, True, Auto_BPC);
+
+   M1600x900_60 : constant Mode_Type := Mode_Type'
+      (60*(2010*912), 1600, 1664, 1706, 2010, 900, 903, 906, 912, False, False, Auto_BPC);
+
+   M1440x900_60 : constant Mode_Type := Mode_Type'
+      (60*(1834*920), 1440, 1488, 1520, 1834, 900, 903, 909, 920, False, False, Auto_BPC);
+
+   M1366x768_60 : constant Mode_Type := Mode_Type'
+      (60*(1446*788), 1366, 1414, 1446, 1466, 768, 769, 773, 788, False, False, Auto_BPC);
+
+   M1280x1024_60 : constant Mode_Type := Mode_Type'
+      (60*(1712*1063), 1280, 1368, 1496, 1712, 1024, 1027, 1034, 1063, False, True, Auto_BPC);
+
+   M1024x768_60 : constant Mode_Type := Mode_Type'
+      (60*(1344*806), 1024, 1048, 1184, 1344, 768, 771, 777, 806, False, False, Auto_BPC);
+
+   Invalid_Mode : constant Mode_Type := Mode_Type'
+      (Frequency_Type'First, 1, 1, 1, 1, 1, 1, 1, 1, False, False, Auto_BPC);
+
+end HW.GFX;
diff --git a/common/ironlake/Makefile.inc b/common/ironlake/Makefile.inc
new file mode 100644
index 0000000..468bb95
--- /dev/null
+++ b/common/ironlake/Makefile.inc
@@ -0,0 +1,17 @@
+gfxinit-y += hw-gfx-gma-connectors-edp.adb
+gfxinit-y += hw-gfx-gma-connectors-edp.ads
+gfxinit-y += hw-gfx-gma-connectors-fdi.adb
+gfxinit-y += hw-gfx-gma-connectors-fdi.ads
+gfxinit-y += hw-gfx-gma-connectors.adb
+gfxinit-y += hw-gfx-gma-pch-dp.adb
+gfxinit-y += hw-gfx-gma-pch-dp.ads
+gfxinit-y += hw-gfx-gma-pch-hdmi.adb
+gfxinit-y += hw-gfx-gma-pch-hdmi.ads
+gfxinit-y += hw-gfx-gma-pch-lvds.adb
+gfxinit-y += hw-gfx-gma-pch-lvds.ads
+gfxinit-y += hw-gfx-gma-plls.adb
+gfxinit-y += hw-gfx-gma-plls.ads
+gfxinit-y += hw-gfx-gma-port_detect.adb
+gfxinit-y += hw-gfx-gma-power_and_clocks.ads
+gfxinit-y += hw-gfx-gma-power_and_clocks_ironlake.adb
+gfxinit-y += hw-gfx-gma-power_and_clocks_ironlake.ads
diff --git a/common/ironlake/hw-gfx-gma-connectors-edp.adb b/common/ironlake/hw-gfx-gma-connectors-edp.adb
new file mode 100644
index 0000000..96930f3
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-connectors-edp.adb
@@ -0,0 +1,297 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+with HW.GFX.DP_Training;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.DP_Aux_Ch;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors.EDP
+is
+
+   DP_CTL_DISPLAYPORT_ENABLE        : constant :=  1 * 2 ** 31;
+   DP_CTL_PIPE_SELECT_MASK          : constant :=  3 * 2 ** 29;
+   DP_CTL_PIPE_SELECT_SHIFT         : constant :=           29;
+   DP_CTL_VSWING_EMPH_SET_MASK      : constant := 63 * 2 ** 22;
+   DP_CTL_PORT_WIDTH_MASK           : constant :=  7 * 2 ** 19;
+   DP_CTL_PORT_WIDTH_1_LANE         : constant :=  0 * 2 ** 19;
+   DP_CTL_PORT_WIDTH_2_LANES        : constant :=  1 * 2 ** 19;
+   DP_CTL_PORT_WIDTH_4_LANES        : constant :=  3 * 2 ** 19;
+   DP_CTL_ENHANCED_FRAMING_ENABLE   : constant :=  1 * 2 ** 18;
+   DP_CTL_PLL_FREQUENCY_MASK        : constant :=  3 * 2 ** 16;
+   DP_CTL_PLL_FREQUENCY_270         : constant :=  0 * 2 ** 16;
+   DP_CTL_PLL_FREQUENCY_162         : constant :=  1 * 2 ** 16;
+   DP_CTL_PORT_REVERSAL             : constant :=  1 * 2 ** 15;
+   DP_CTL_PLL_ENABLE                : constant :=  1 * 2 ** 14;
+   DP_CTL_LINK_TRAIN_MASK           : constant :=  3 * 2 **  8;
+   DP_CTL_LINK_TRAIN_PAT1           : constant :=  0 * 2 **  8;
+   DP_CTL_LINK_TRAIN_PAT2           : constant :=  1 * 2 **  8;
+   DP_CTL_LINK_TRAIN_IDLE           : constant :=  2 * 2 **  8;
+   DP_CTL_LINK_TRAIN_NORMAL         : constant :=  3 * 2 **  8;
+   DP_CTL_ALT_SCRAMBLER_RESET       : constant :=  1 * 2 **  6;
+   DP_CTL_VSYNC_ACTIVE_HIGH         : constant :=  1 * 2 **  4;
+   DP_CTL_HSYNC_ACTIVE_HIGH         : constant :=  1 * 2 **  3;
+   DP_CTL_PORT_DETECT               : constant :=  1 * 2 **  2;
+
+   -- TODO? Values are for Ivy Bridge only
+   DP_CTL_VSWING_0_EMPH_0 : constant := 1 * 2 ** 27 + 1 * 2 ** 24 + 0 * 2 ** 22;
+   DP_CTL_VSWING_0_EMPH_1 : constant := 1 * 2 ** 27 + 2 * 2 ** 24 + 2 * 2 ** 22;
+   DP_CTL_VSWING_0_EMPH_2 : constant := 1 * 2 ** 27 + 3 * 2 ** 24 + 3 * 2 ** 22;
+   DP_CTL_VSWING_1_EMPH_0 : constant := 1 * 2 ** 27 + 4 * 2 ** 24 + 0 * 2 ** 22;
+   DP_CTL_VSWING_1_EMPH_1 : constant := 1 * 2 ** 27 + 5 * 2 ** 24 + 2 * 2 ** 22;
+   DP_CTL_VSWING_2_EMPH_0 : constant := 1 * 2 ** 27 + 6 * 2 ** 24 + 0 * 2 ** 22;
+   DP_CTL_VSWING_2_EMPH_1 : constant := 1 * 2 ** 27 + 7 * 2 ** 24 + 2 * 2 ** 22;
+
+   type DP_CTL_PORT_WIDTH_T is array (DP_Lane_Count) of Word32;
+   DP_CTL_PORT_WIDTH : constant DP_CTL_PORT_WIDTH_T :=
+      DP_CTL_PORT_WIDTH_T'
+     (DP_Lane_Count_1 => DP_CTL_PORT_WIDTH_1_LANE,
+      DP_Lane_Count_2 => DP_CTL_PORT_WIDTH_2_LANES,
+      DP_Lane_Count_4 => DP_CTL_PORT_WIDTH_4_LANES);
+
+   type DP_CTL_LINK_TRAIN_Array is array (DP_Info.Training_Pattern) of Word32;
+   DP_CTL_LINK_TRAIN : constant DP_CTL_LINK_TRAIN_Array :=
+      DP_CTL_LINK_TRAIN_Array'
+     (DP_Info.TP_1      => DP_CTL_LINK_TRAIN_PAT1,
+      DP_Info.TP_2      => DP_CTL_LINK_TRAIN_PAT2,
+      DP_Info.TP_3      => DP_CTL_LINK_TRAIN_PAT2,
+      DP_Info.TP_Idle   => DP_CTL_LINK_TRAIN_IDLE,
+      DP_Info.TP_None   => DP_CTL_LINK_TRAIN_NORMAL);
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_Training is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+        (Register    => Registers.DP_CTL_A,
+         Mask_Unset  => DP_CTL_LINK_TRAIN_MASK,
+         Mask_Set    => DP_CTL_LINK_TRAIN (DP_Info.TP_1) or
+                        DP_CTL_DISPLAYPORT_ENABLE);
+   end Pre_Training;
+
+   ----------------------------------------------------------------------------
+
+   pragma Warnings (GNATprove, Off, "unused variable ""Port""",
+                    Reason => "Needed for a common interface");
+   function Max_V_Swing
+     (Port : Digital_Port)
+      return DP_Info.DP_Voltage_Swing
+   is
+   begin
+      return DP_Info.VS_Level_2;
+   end Max_V_Swing;
+
+   function Max_Pre_Emph
+     (Port        : Digital_Port;
+      Train_Set   : DP_Info.Train_Set)
+      return DP_Info.DP_Pre_Emph
+   is
+   begin
+      return
+        (case Train_Set.Voltage_Swing is
+            when DP_Info.VS_Level_0 => DP_Info.Emph_Level_2,
+            when DP_Info.VS_Level_1 |
+                 DP_Info.VS_Level_2 => DP_Info.Emph_Level_1,
+            when others             => DP_Info.Emph_Level_0);
+   end Max_Pre_Emph;
+
+   ----------------------------------------------------------------------------
+
+   pragma Warnings (GNATprove, Off, "unused variable ""Link""",
+                    Reason => "Needed for a common interface");
+   procedure Set_Training_Pattern
+     (Port     : Digital_Port;
+      Link     : DP_Link;
+      Pattern  : DP_Info.Training_Pattern)
+   is
+      use type DP_Info.Training_Pattern;
+   begin
+      if Pattern < DP_Info.TP_Idle then
+         Registers.Unset_And_Set_Mask
+           (Register    => Registers.DP_CTL_A,
+            Mask_Unset  => DP_CTL_LINK_TRAIN_MASK,
+            Mask_Set    => DP_CTL_LINK_TRAIN (Pattern));
+      else
+         -- send at least 5 idle patterns
+         Registers.Unset_And_Set_Mask
+           (Register    => Registers.DP_CTL_A,
+            Mask_Unset  => DP_CTL_LINK_TRAIN_MASK,
+            Mask_Set    => DP_CTL_LINK_TRAIN (DP_Info.TP_Idle));
+
+         -- we switch to normal frame delivery later in Post_On procedure
+      end if;
+   end Set_Training_Pattern;
+
+   procedure Set_Signal_Levels
+     (Port        : Digital_Port;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set)
+   is
+      VSwing_Emph : Word32;
+   begin
+      VSwing_Emph :=
+        (case Train_Set.Voltage_Swing is
+            when DP_Info.VS_Level_0 =>
+              (case Train_Set.Pre_Emph is
+                  when DP_Info.Emph_Level_0  => DP_CTL_VSWING_0_EMPH_0,
+                  when DP_Info.Emph_Level_1  => DP_CTL_VSWING_0_EMPH_1,
+                  when DP_Info.Emph_Level_2  => DP_CTL_VSWING_0_EMPH_2,
+                  when others                => DP_CTL_VSWING_0_EMPH_0),
+            when DP_Info.VS_Level_1 =>
+              (case Train_Set.Pre_Emph is
+                  when DP_Info.Emph_Level_0  => DP_CTL_VSWING_1_EMPH_0,
+                  when DP_Info.Emph_Level_1  => DP_CTL_VSWING_1_EMPH_1,
+                  when others                => DP_CTL_VSWING_1_EMPH_0),
+            when DP_Info.VS_Level_2 =>
+              (case Train_Set.Pre_Emph is
+                  when DP_Info.Emph_Level_0  => DP_CTL_VSWING_2_EMPH_0,
+                  when DP_Info.Emph_Level_1  => DP_CTL_VSWING_2_EMPH_1,
+                  when others                => DP_CTL_VSWING_2_EMPH_0),
+            when others                      => DP_CTL_VSWING_0_EMPH_0);
+
+      Registers.Unset_And_Set_Mask
+        (Register    => Registers.DP_CTL_A,
+         Mask_Unset  => DP_CTL_VSWING_EMPH_SET_MASK,
+         Mask_Set    => VSwing_Emph);
+   end Set_Signal_Levels;
+   pragma Warnings (GNATprove, On, "unused variable ""Port""");
+   pragma Warnings (GNATprove, On, "unused variable ""Link""");
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_On
+     (Port_Cfg    : Port_Config;
+      Pipe_Hint   : Word32)
+   is
+      DP_CTL_Set : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      DP_CTL_Set :=
+         Shift_Left (Pipe_Hint, DP_CTL_PIPE_SELECT_SHIFT) or
+         DP_CTL_PORT_WIDTH (Port_Cfg.DP.Lane_Count);
+
+      if Port_Cfg.DP.Enhanced_Framing then
+         DP_CTL_Set := DP_CTL_Set or DP_CTL_ENHANCED_FRAMING_ENABLE;
+      end if;
+
+      case Port_Cfg.DP.Bandwidth is
+         when DP_Bandwidth_1_62 =>
+            DP_CTL_Set := DP_CTL_Set or DP_CTL_PLL_FREQUENCY_162;
+         when DP_Bandwidth_2_7 =>
+            DP_CTL_Set := DP_CTL_Set or DP_CTL_PLL_FREQUENCY_270;
+         when others =>
+            null;
+      end case;
+
+      if Port_Cfg.Mode.V_Sync_Active_High then
+         DP_CTL_Set := DP_CTL_Set or DP_CTL_VSYNC_ACTIVE_HIGH;
+      end if;
+      if Port_Cfg.Mode.H_Sync_Active_High then
+         DP_CTL_Set := DP_CTL_Set or DP_CTL_HSYNC_ACTIVE_HIGH;
+      end if;
+
+      Registers.Write
+        (Register => Registers.DP_CTL_A,
+         Value    => DP_CTL_Set);
+
+      Registers.Write
+        (Register => Registers.DP_CTL_A,
+         Value    => DP_CTL_PLL_ENABLE or DP_CTL_Set);
+      Registers.Posting_Read (Registers.DP_CTL_A);
+      Time.U_Delay (20);
+   end Pre_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Post_On
+     (Link     : in     DP_Link;
+      Success  :    out Boolean)
+   is
+      pragma Warnings (GNATprove, Off, "unused variable ""Port""",
+                       Reason => "Needed for a common interface");
+      function To_DP (Port : Digital_Port) return DP_Port
+      is
+      begin
+         return DP_A;
+      end To_DP;
+      pragma Warnings (GNATprove, On, "unused variable ""Port""");
+      package Training is new DP_Training
+        (TPS3_Supported    => False,
+         T                 => Digital_Port,
+         Aux_T             => DP_Port,
+         Aux_Ch            => DP_Aux_Ch,
+         DP_Info           => DP_Info,
+         To_Aux            => To_DP,
+         Max_V_Swing       => Max_V_Swing,
+         Max_Pre_Emph      => Max_Pre_Emph,
+         Set_Pattern       => Set_Training_Pattern,
+         Set_Signal_Levels => Set_Signal_Levels,
+         Off               => Off);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Training.Train_DP
+        (Port        => DIGI_A,
+         Link        => Link,
+         Success     => Success);
+
+      if Success then
+         Registers.Unset_And_Set_Mask
+           (Register    => Registers.DP_CTL_A,
+            Mask_Unset  => DP_CTL_LINK_TRAIN_MASK,
+            Mask_Set    => DP_CTL_LINK_TRAIN_NORMAL);
+      end if;
+   end Post_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off (Port : Digital_Port)
+   is
+      Enabled : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+        (Register    => Registers.DP_CTL_A,
+         Mask_Unset  => DP_CTL_LINK_TRAIN_MASK,
+         Mask_Set    => DP_CTL_LINK_TRAIN_IDLE);
+      Registers.Posting_Read (Registers.DP_CTL_A);
+
+      Registers.Unset_Mask
+        (Register => Registers.DP_CTL_A,
+         Mask     => DP_CTL_DISPLAYPORT_ENABLE);
+      -- implicit Posting_Read below
+
+      Registers.Is_Set_Mask
+        (Register => Registers.DP_CTL_A,
+         Mask     => DP_CTL_PLL_ENABLE,
+         Result   => Enabled);
+
+      Registers.Write
+        (Register => Registers.DP_CTL_A,
+         Value    => 16#0000_0000#);
+      Registers.Posting_Read (Registers.DP_CTL_A);
+
+      if Enabled then
+         Time.U_Delay (20);
+      end if;
+   end Off;
+
+end HW.GFX.GMA.Connectors.EDP;
diff --git a/common/ironlake/hw-gfx-gma-connectors-edp.ads b/common/ironlake/hw-gfx-gma-connectors-edp.ads
new file mode 100644
index 0000000..516e093
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-connectors-edp.ads
@@ -0,0 +1,32 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Connectors.EDP
+is
+
+   procedure Pre_On
+     (Port_Cfg    : Port_Config;
+      Pipe_Hint   : Word32);
+
+   procedure Post_On
+     (Link     : in     DP_Link;
+      Success  :    out Boolean);
+
+   pragma Warnings (GNATprove, Off, "unused variable ""Port""",
+                    Reason => "Needed for a common interface");
+   procedure Off (Port : Digital_Port);
+   pragma Warnings (GNATprove, On, "unused variable ""Port""");
+
+   procedure Pre_Training;
+
+end HW.GFX.GMA.Connectors.EDP;
diff --git a/common/ironlake/hw-gfx-gma-connectors-fdi.adb b/common/ironlake/hw-gfx-gma-connectors-fdi.adb
new file mode 100644
index 0000000..2d295f7
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-connectors-fdi.adb
@@ -0,0 +1,342 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.PCH.FDI;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors.FDI
+is
+
+   PCH_FDI_CHICKEN_B_AND_C             : constant :=      1 * 2 ** 12;
+
+   type TX_CTL_Regs is array (GPU_FDI_Port) of Registers.Registers_Index;
+   TX_CTL : constant TX_CTL_Regs :=
+     (DIGI_B => Registers.FDI_TX_CTL_A,
+      DIGI_C => Registers.FDI_TX_CTL_B,
+      DIGI_D => Registers.FDI_TX_CTL_C);
+
+   FDI_TX_CTL_FDI_TX_ENABLE            : constant :=      1 * 2 ** 31;
+   FDI_TX_CTL_VP_MASK                  : constant := 16#3f# * 2 ** 22;
+   FDI_TX_CTL_PORT_WIDTH_SEL_SHIFT     : constant :=               19;
+   FDI_TX_CTL_ENHANCED_FRAMING_ENABLE  : constant :=      1 * 2 ** 18;
+   FDI_TX_CTL_FDI_PLL_ENABLE           : constant :=      1 * 2 ** 14;
+   FDI_TX_CTL_COMPOSITE_SYNC_SELECT    : constant :=      1 * 2 ** 11;
+   FDI_TX_CTL_AUTO_TRAIN_ENABLE        : constant :=      1 * 2 ** 10;
+   FDI_TX_CTL_AUTO_TRAIN_DONE          : constant :=      1 * 2 **  1;
+
+   TP_SHIFT : constant := (if Config.CPU <= Sandybridge then 28 else 8);
+   FDI_TX_CTL_TRAINING_PATTERN_MASK    : constant := 3 * 2 ** TP_SHIFT;
+   FDI_TX_CTL_TRAINING_PATTERN_1       : constant := 0 * 2 ** TP_SHIFT;
+   FDI_TX_CTL_TRAINING_PATTERN_2       : constant := 1 * 2 ** TP_SHIFT;
+   FDI_TX_CTL_TRAINING_PATTERN_IDLE    : constant := 2 * 2 ** TP_SHIFT;
+   FDI_TX_CTL_TRAINING_PATTERN_NORMAL  : constant := 3 * 2 ** TP_SHIFT;
+
+   subtype FDI_TX_CTL_VP_T is Natural range 0 .. 3;
+   type Vswing_Preemph_Values is array (FDI_TX_CTL_VP_T) of Word32;
+   FDI_TX_CTL_VSWING_PREEMPH : constant Vswing_Preemph_Values :=
+     (0 => 16#00# * 2 ** 22,
+      1 => 16#3a# * 2 ** 22,
+      2 => 16#39# * 2 ** 22,
+      3 => 16#38# * 2 ** 22);
+
+   function FDI_TX_CTL_PORT_WIDTH_SEL (Lane_Count : DP_Lane_Count) return Word32
+   is
+   begin
+      return Shift_Left
+        (Word32 (Lane_Count_As_Integer (Lane_Count)) - 1,
+         FDI_TX_CTL_PORT_WIDTH_SEL_SHIFT);
+   end FDI_TX_CTL_PORT_WIDTH_SEL;
+
+   ----------------------------------------------------------------------------
+
+   --
+   -- This is usually used with Ivy Bridge.
+   --
+   procedure Auto_Training
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   with
+      Pre => Port_Cfg.Port in GPU_FDI_Port
+   is
+      PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      -- try each preemph/voltage pair twice
+      for VP2 in Natural range 0 .. FDI_TX_CTL_VP_T'Last * 2 + 1
+      loop
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_VP_MASK or
+                           FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_FDI_TX_ENABLE or
+                           FDI_TX_CTL_VSWING_PREEMPH (VP2 / 2) or
+                           FDI_TX_CTL_AUTO_TRAIN_ENABLE or
+                           FDI_TX_CTL_TRAINING_PATTERN_1);
+         Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+         PCH.FDI.Auto_Train (PCH_FDI_Port);
+
+         -- read at least twice
+         for I in 0 .. 3 loop
+            Registers.Is_Set_Mask
+              (Register => TX_CTL (Port_Cfg.Port),
+               Mask     => FDI_TX_CTL_AUTO_TRAIN_DONE,
+               Result   => Success);
+            exit when Success or I = 3;
+            Time.U_Delay (1);
+         end loop;
+         exit when Success;
+
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_FDI_TX_ENABLE or
+                           FDI_TX_CTL_AUTO_TRAIN_ENABLE or
+                           FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_1);
+
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
+      end loop;
+
+      if Success then
+         PCH.FDI.Enable_EC (PCH_FDI_Port);
+      else
+         Registers.Unset_Mask
+           (Register => TX_CTL (Port_Cfg.Port),
+            Mask     => FDI_TX_CTL_FDI_PLL_ENABLE);
+
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
+      end if;
+   end Auto_Training;
+
+   ----------------------------------------------------------------------------
+
+   --
+   -- Used with Sandy Bridge (should work with Ivy Bridge too)
+   --
+   procedure Full_Training
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   with
+      Pre => Port_Cfg.Port in GPU_FDI_Port
+   is
+      PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      -- try each preemph/voltage pair twice
+      for VP2 in Natural range 0 .. FDI_TX_CTL_VP_T'Last * 2 + 1
+      loop
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_VP_MASK or
+                           FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_FDI_TX_ENABLE or
+                           FDI_TX_CTL_VSWING_PREEMPH (VP2 / 2) or
+                           FDI_TX_CTL_TRAINING_PATTERN_1);
+         Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+         PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_1, Success);
+
+         if Success then
+            Registers.Unset_And_Set_Mask
+              (Register    => TX_CTL (Port_Cfg.Port),
+               Mask_Unset  => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+               Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_2);
+            Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+            PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_2, Success);
+         end if;
+         exit when Success;
+
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_FDI_TX_ENABLE or
+                           FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_1);
+
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
+      end loop;
+
+      if Success then
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_NORMAL);
+         Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+         PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_None, Success);
+      else
+         Registers.Unset_Mask
+           (Register => TX_CTL (Port_Cfg.Port),
+            Mask     => FDI_TX_CTL_FDI_PLL_ENABLE);
+
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
+      end if;
+   end Full_Training;
+
+   ----------------------------------------------------------------------------
+
+   --
+   -- Used with original Ironlake (Nehalem CPU)
+   --
+   -- This is close to what Linux' i915 does. A comment in i915_reg.h
+   -- states that it uses only the lowest voltage / pre-emphasis levels
+   -- which is why we leave them at zero here and don't try different
+   -- values.
+   --
+   -- It's actually not clear from i915's code if the values really are
+   -- at zero or if it's just reusing what the Video BIOS set. Some code
+   -- in coreboot sets them to zero explicitly.
+   --
+   procedure Simple_Training
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   with
+      Pre => Port_Cfg.Port in GPU_FDI_Port
+   is
+      PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+        (Register    => TX_CTL (Port_Cfg.Port),
+         Mask_Unset  => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+         Mask_Set    => FDI_TX_CTL_FDI_TX_ENABLE or
+                        FDI_TX_CTL_TRAINING_PATTERN_1);
+      Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+      PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_1, Success);
+
+      if Success then
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_2);
+         Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+         PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_2, Success);
+      end if;
+
+      if Success then
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_NORMAL);
+         Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+         PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_None, Success);
+      else
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_FDI_TX_ENABLE or
+                           FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_1);
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
+
+         Registers.Unset_Mask
+           (Register => TX_CTL (Port_Cfg.Port),
+            Mask     => FDI_TX_CTL_FDI_PLL_ENABLE);
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
+      end if;
+   end Simple_Training;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_On (Port_Cfg : Port_Config)
+   is
+      Composite_Sel : constant :=
+        (if Config.Has_FDI_Composite_Sel then
+            FDI_TX_CTL_COMPOSITE_SYNC_SELECT else 0);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      -- The PCH_FDI_CHICKEN_B_AND_C bit may not be changed when any of
+      -- both ports is active. Bandwidth calculations before calling us
+      -- should ensure this.
+      if Config.Has_FDI_C then
+         if Port_Cfg.Port = DIGI_D or
+            (Port_Cfg.Port = DIGI_C and
+             Port_Cfg.FDI.Lane_Count <= DP_Lane_Count_2)
+         then
+            Registers.Set_Mask
+              (Register => Registers.PCH_FDI_CHICKEN_B_C,
+               Mask     => PCH_FDI_CHICKEN_B_AND_C);
+         elsif Port_Cfg.Port = DIGI_C then
+            Registers.Unset_Mask
+              (Register => Registers.PCH_FDI_CHICKEN_B_C,
+               Mask     => PCH_FDI_CHICKEN_B_AND_C);
+         end if;
+      end if;
+
+      PCH.FDI.Pre_Train (PCH_FDIs (Port_Cfg.Port), Port_Cfg);
+
+      Registers.Write
+        (Register => TX_CTL (Port_Cfg.Port),
+         Value    => FDI_TX_CTL_PORT_WIDTH_SEL (Port_Cfg.FDI.Lane_Count) or
+                     FDI_TX_CTL_ENHANCED_FRAMING_ENABLE or
+                     FDI_TX_CTL_FDI_PLL_ENABLE or
+                     Composite_Sel or
+                     FDI_TX_CTL_TRAINING_PATTERN_1);
+      Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+      Time.U_Delay (100);
+   end Pre_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Post_On
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      case Config.FDI_Training is
+         when GMA.Simple_Training   => Simple_Training (Port_Cfg, Success);
+         when GMA.Full_Training     => Full_Training (Port_Cfg, Success);
+         when GMA.Auto_Training     => Auto_Training (Port_Cfg, Success);
+      end case;
+   end Post_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off (Port : GPU_FDI_Port; OT : Off_Type)
+   is
+      PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+        (Register    => TX_CTL (Port),
+         Mask_Unset  => FDI_TX_CTL_FDI_TX_ENABLE or
+                        FDI_TX_CTL_AUTO_TRAIN_ENABLE or
+                        FDI_TX_CTL_TRAINING_PATTERN_MASK,
+         Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_1);
+
+      PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
+
+      if OT >= Clock_Off then
+         Registers.Unset_Mask
+           (Register => TX_CTL (Port),
+            Mask     => FDI_TX_CTL_FDI_PLL_ENABLE);
+
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
+      end if;
+   end Off;
+
+end HW.GFX.GMA.Connectors.FDI;
diff --git a/common/ironlake/hw-gfx-gma-connectors-fdi.ads b/common/ironlake/hw-gfx-gma-connectors-fdi.ads
new file mode 100644
index 0000000..9f0cf08
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-connectors-fdi.ads
@@ -0,0 +1,43 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.PCH;
+
+private package HW.GFX.GMA.Connectors.FDI
+is
+
+   subtype GPU_FDI_Port is GPU_Port range DIGI_B .. DIGI_D;
+
+   type PCH_FDI_Mapping is array (GPU_FDI_Port) of PCH.FDI_Port_Type;
+   PCH_FDIs : constant PCH_FDI_Mapping :=
+     (DIGI_B => PCH.FDI_A,
+      DIGI_C => PCH.FDI_B,
+      DIGI_D => PCH.FDI_C);
+
+   type Off_Type is (Link_Off, Clock_Off);
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_On (Port_Cfg : Port_Config)
+   with
+      Pre => Port_Cfg.Port in GPU_FDI_Port;
+
+   procedure Post_On
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   with
+      Pre => Port_Cfg.Port in GPU_FDI_Port;
+
+   procedure Off (Port : GPU_FDI_Port; OT : Off_Type);
+
+end HW.GFX.GMA.Connectors.FDI;
diff --git a/common/ironlake/hw-gfx-gma-connectors.adb b/common/ironlake/hw-gfx-gma-connectors.adb
new file mode 100644
index 0000000..4e3ff53
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-connectors.adb
@@ -0,0 +1,184 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+-- Copyright (C) 2016 Nico Huber <nico.h@gmx.de>
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.Connectors.EDP;
+with HW.GFX.GMA.Connectors.FDI;
+with HW.GFX.GMA.PCH.VGA;
+with HW.GFX.GMA.PCH.LVDS;
+with HW.GFX.GMA.PCH.HDMI;
+with HW.GFX.GMA.PCH.DP;
+with HW.GFX.GMA.PCH.Transcoder;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors
+is
+
+   function Is_Internal (Port_Cfg : Port_Config) return Boolean
+   is
+   begin
+      return
+         Port_Cfg.Port = DIGI_A or
+         (Port_Cfg.Is_FDI and Port_Cfg.PCH_Port = PCH_LVDS);
+   end Is_Internal;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_On
+     (Port_Cfg    : in     Port_Config;
+      PLL_Hint    : in     Word32;
+      Pipe_Hint   : in     Word32;
+      Success     :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Port = DIGI_A then
+         EDP.Pre_On (Port_Cfg, Pipe_Hint);
+      elsif Port_Cfg.Port in FDI.GPU_FDI_Port then
+         FDI.Pre_On (Port_Cfg);
+      end if;
+      Success := True;
+   end Pre_On;
+
+   procedure Post_On
+     (Port_Cfg : in     Port_Config;
+      PLL_Hint : in     Word32;
+      Success  :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Port = DIGI_A then
+         EDP.Pre_Training;
+         Success := True;
+      elsif Port_Cfg.Port in FDI.GPU_FDI_Port then
+         declare
+            FDI_Port : constant PCH.FDI_Port_Type :=
+               FDI.PCH_FDIs (Port_Cfg.Port);
+         begin
+            FDI.Post_On (Port_Cfg, Success);
+
+            if Success then
+               PCH.Transcoder.On (Port_Cfg, FDI_Port, PLL_Hint);
+               if Port_Cfg.PCH_Port = PCH_DAC then
+                  PCH.VGA.On (FDI_Port, Port_Cfg.Mode);
+               elsif Port_Cfg.PCH_Port = PCH_LVDS then
+                  PCH.LVDS.On (Port_Cfg, FDI_Port);
+               elsif Port_Cfg.PCH_Port in PCH_HDMI_Port then
+                  PCH.HDMI.On (Port_Cfg, FDI_Port);
+               elsif Port_Cfg.PCH_Port in PCH_DP_Port then
+                  PCH.DP.On (Port_Cfg, Success);
+               end if;
+            end if;
+         end;
+      else
+         Success := False;
+      end if;
+
+      if Success and Is_Internal (Port_Cfg) then
+         Panel.On;
+      end if;
+
+      if Port_Cfg.Port = DIGI_A then
+         EDP.Post_On (Port_Cfg.DP, Success);
+      end if;
+
+      if Success and Is_Internal (Port_Cfg) then
+         Panel.Backlight_On;
+      end if;
+   end Post_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_Off (Port_Cfg : Port_Config)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Is_Internal (Port_Cfg) then
+         Panel.Backlight_Off;
+         Panel.Off;
+      end if;
+   end Pre_Off;
+
+   procedure Post_Off (Port_Cfg : Port_Config)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Port = DIGI_A then
+         EDP.Off (Port_Cfg.Port);
+      elsif Port_Cfg.Port in FDI.GPU_FDI_Port then
+         declare
+            FDI_Port : constant PCH.FDI_Port_Type :=
+               FDI.PCH_FDIs (Port_Cfg.Port);
+         begin
+            if Port_Cfg.PCH_Port in PCH_DP_Port then
+               PCH.DP.Off (Port_Cfg.PCH_Port);
+            end if;
+
+            FDI.Off (Port_Cfg.Port, FDI.Link_Off);
+
+            if Port_Cfg.PCH_Port = PCH_DAC then
+               PCH.VGA.Off;
+            elsif Port_Cfg.PCH_Port = PCH_LVDS then
+               PCH.LVDS.Off;
+            elsif Port_Cfg.PCH_Port in PCH_HDMI_Port then
+               PCH.HDMI.Off (Port_Cfg.PCH_Port);
+            end if;
+            PCH.Transcoder.Off (FDI_Port);
+
+            FDI.Off (Port_Cfg.Port, FDI.Clock_Off);
+         end;
+      end if;
+   end Post_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_All_Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Panel.Backlight_Off;
+      Panel.Off;
+   end Pre_All_Off;
+
+   procedure Post_All_Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      EDP.Off (DIGI_A);
+
+      for Port in FDI.GPU_FDI_Port loop
+         FDI.Off (Port, FDI.Link_Off);
+      end loop;
+      PCH.VGA.Off;
+      PCH.LVDS.Off;
+      PCH.HDMI.All_Off;
+      PCH.DP.All_Off;
+      for Port in PCH.FDI_Port_Type loop
+         PCH.Transcoder.Off (Port);
+      end loop;
+      for Port in FDI.GPU_FDI_Port loop
+         FDI.Off (Port, FDI.Clock_Off);
+      end loop;
+   end Post_All_Off;
+
+end HW.GFX.GMA.Connectors;
diff --git a/common/ironlake/hw-gfx-gma-pch-dp.adb b/common/ironlake/hw-gfx-gma-pch-dp.adb
new file mode 100644
index 0000000..1fb3578
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-pch-dp.adb
@@ -0,0 +1,205 @@
+--
+-- Copyright (C) 2016 Nico Huber <nico.h@gmx.de>
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.DP_Training;
+with HW.GFX.GMA.DP_Aux_Ch;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PCH.DP is
+
+   type DP_Array is array (PCH_DP_Port) of Registers.Registers_Index;
+   DP_CTL : constant DP_Array :=
+     (PCH_DP_B => Registers.PCH_DP_B,
+      PCH_DP_C => Registers.PCH_DP_C,
+      PCH_DP_D => Registers.PCH_DP_D);
+
+   DP_CTL_DISPLAY_PORT_ENABLE          : constant := 1 * 2 ** 31;
+   DP_CTL_VSWING_LEVEL_SET_SHIFT       : constant :=          25;
+   DP_CTL_VSWING_LEVEL_SET_MASK        : constant := 7 * 2 ** 25;
+   DP_CTL_PREEMPH_LEVEL_SET_SHIFT      : constant :=          22;
+   DP_CTL_PREEMPH_LEVEL_SET_MASK       : constant := 7 * 2 ** 22;
+   DP_CTL_PORT_WIDTH_SHIFT             : constant :=          19;
+   DP_CTL_PORT_REVERSAL                : constant := 1 * 2 ** 15;
+   DP_CTL_LINK_TRAIN_MASK              : constant := 7 * 2 **  8;
+   DP_CTL_LINK_TRAIN_PAT1              : constant := 0 * 2 **  8;
+   DP_CTL_LINK_TRAIN_PAT2              : constant := 1 * 2 **  8;
+   DP_CTL_LINK_TRAIN_IDLE              : constant := 2 * 2 **  8;
+   DP_CTL_LINK_TRAIN_NORMAL            : constant := 3 * 2 **  8;
+   DP_CTL_AUDIO_OUTPUT_ENABLE          : constant := 1 * 2 **  6;
+   DP_CTL_PORT_DETECT                  : constant := 1 * 2 **  2;
+
+   function DP_CTL_VSWING_LEVEL_SET
+     (VS : DP_Info.DP_Voltage_Swing)
+      return Word32
+   is
+   begin
+      return Shift_Left
+        (Word32 (DP_Info.DP_Voltage_Swing'Pos (VS)),
+         DP_CTL_VSWING_LEVEL_SET_SHIFT);
+   end DP_CTL_VSWING_LEVEL_SET;
+
+   function DP_CTL_PREEMPH_LEVEL_SET (PE : DP_Info.DP_Pre_Emph) return Word32
+   is
+   begin
+      return Shift_Left
+        (Word32 (DP_Info.DP_Pre_Emph'Pos (PE)), DP_CTL_PREEMPH_LEVEL_SET_SHIFT);
+   end DP_CTL_PREEMPH_LEVEL_SET;
+
+   function DP_CTL_PORT_WIDTH (Lane_Count : DP_Lane_Count) return Word32
+   is
+   begin
+      return Shift_Left
+        (Word32 (Lane_Count_As_Integer (Lane_Count)) - 1,
+         DP_CTL_PORT_WIDTH_SHIFT);
+   end DP_CTL_PORT_WIDTH;
+
+   type DP_CTL_LINK_TRAIN_Array is array (DP_Info.Training_Pattern) of Word32;
+   DP_CTL_LINK_TRAIN : constant DP_CTL_LINK_TRAIN_Array :=
+     (DP_Info.TP_1      => DP_CTL_LINK_TRAIN_PAT1,
+      DP_Info.TP_2      => DP_CTL_LINK_TRAIN_PAT2,
+      DP_Info.TP_3      => DP_CTL_LINK_TRAIN_PAT2,
+      DP_Info.TP_Idle   => DP_CTL_LINK_TRAIN_IDLE,
+      DP_Info.TP_None   => DP_CTL_LINK_TRAIN_NORMAL);
+
+   ----------------------------------------------------------------------------
+
+   pragma Warnings (GNATprove, Off, "unused variable ""Port""",
+                    Reason => "Needed for a common interface");
+   function Max_V_Swing
+     (Port : PCH_DP_Port)
+      return DP_Info.DP_Voltage_Swing
+   is
+   begin
+      return DP_Info.VS_Level_3;
+   end Max_V_Swing;
+
+   function Max_Pre_Emph
+     (Port        : PCH_DP_Port;
+      Train_Set   : DP_Info.Train_Set)
+      return DP_Info.DP_Pre_Emph
+   is
+   begin
+      return
+        (case Train_Set.Voltage_Swing is
+            when DP_Info.VS_Level_0 => DP_Info.Emph_Level_3,
+            when DP_Info.VS_Level_1 => DP_Info.Emph_Level_2,
+            when DP_Info.VS_Level_2 => DP_Info.Emph_Level_1,
+            when DP_Info.VS_Level_3 => DP_Info.Emph_Level_0);
+   end Max_Pre_Emph;
+
+   ----------------------------------------------------------------------------
+
+   pragma Warnings (GNATprove, Off, "unused variable ""Link""",
+                    Reason => "Needed for a common interface");
+   procedure Set_Training_Pattern
+     (Port     : PCH_DP_Port;
+      Link     : DP_Link;
+      Pattern  : DP_Info.Training_Pattern)
+   is
+   begin
+      Registers.Unset_And_Set_Mask
+        (Register    => DP_CTL (Port),
+         Mask_Unset  => DP_CTL_LINK_TRAIN_MASK,
+         Mask_Set    => DP_CTL_LINK_TRAIN (Pattern));
+   end Set_Training_Pattern;
+
+   procedure Set_Signal_Levels
+     (Port        : PCH_DP_Port;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set)
+   is
+   begin
+      Registers.Unset_And_Set_Mask
+        (Register    => DP_CTL (Port),
+         Mask_Unset  => DP_CTL_VSWING_LEVEL_SET_MASK or
+                        DP_CTL_PREEMPH_LEVEL_SET_MASK,
+         Mask_Set    => DP_CTL_VSWING_LEVEL_SET (Train_Set.Voltage_Swing) or
+                        DP_CTL_PREEMPH_LEVEL_SET (Train_Set.Pre_Emph));
+   end Set_Signal_Levels;
+
+   procedure Off (Port : PCH_DP_Port)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+        (Register    => DP_CTL (Port),
+         Mask_Unset  => DP_CTL_LINK_TRAIN_MASK,
+         Mask_Set    => DP_CTL_LINK_TRAIN_IDLE);
+      Registers.Posting_Read (DP_CTL (Port));
+
+      Registers.Write (DP_CTL (Port), 0);
+      Registers.Posting_Read (DP_CTL (Port));
+   end Off;
+   pragma Warnings (GNATprove, On, "unused variable ""Port""");
+   pragma Warnings (GNATprove, On, "unused variable ""Link""");
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   is
+      function To_DP (Port : PCH_DP_Port) return DP_Port
+      is
+      begin
+         return
+           (case Port is
+               when PCH_DP_B => DP_B,
+               when PCH_DP_C => DP_C,
+               when PCH_DP_D => DP_D);
+      end To_DP;
+      package Training is new DP_Training
+        (TPS3_Supported    => False,
+         T                 => PCH_DP_Port,
+         Aux_T             => DP_Port,
+         Aux_Ch            => DP_Aux_Ch,
+         DP_Info           => DP_Info,
+         To_Aux            => To_DP,
+         Max_V_Swing       => Max_V_Swing,
+         Max_Pre_Emph      => Max_Pre_Emph,
+         Set_Pattern       => Set_Training_Pattern,
+         Set_Signal_Levels => Set_Signal_Levels,
+         Off               => Off);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Write
+        (Register => DP_CTL (Port_Cfg.PCH_Port),
+         Value    => DP_CTL_DISPLAY_PORT_ENABLE or
+                     DP_CTL_PORT_WIDTH (Port_Cfg.DP.Lane_Count) or
+                     DP_CTL_LINK_TRAIN_PAT1);
+
+      Training.Train_DP
+        (Port     => Port_Cfg.PCH_Port,
+         Link     => Port_Cfg.DP,
+         Success  => Success);
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure All_Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      for Port in PCH_DP_Port loop
+         Off (Port);
+      end loop;
+   end All_Off;
+
+end HW.GFX.GMA.PCH.DP;
diff --git a/common/ironlake/hw-gfx-gma-pch-dp.ads b/common/ironlake/hw-gfx-gma-pch-dp.ads
new file mode 100644
index 0000000..ee77e30
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-pch-dp.ads
@@ -0,0 +1,26 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.PCH.DP
+is
+
+   procedure On
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   with
+      Pre => Port_Cfg.PCH_Port in PCH_DP_Port;
+
+   procedure Off (Port : PCH_DP_Port);
+   procedure All_Off;
+
+end HW.GFX.GMA.PCH.DP;
diff --git a/common/ironlake/hw-gfx-gma-pch-hdmi.adb b/common/ironlake/hw-gfx-gma-pch-hdmi.adb
new file mode 100644
index 0000000..8bf99db
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-pch-hdmi.adb
@@ -0,0 +1,94 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PCH.HDMI
+is
+
+   PCH_HDMI_ENABLE               : constant := 1 * 2 ** 31;
+   PCH_HDMI_COLOR_FORMAT_8BPC    : constant := 0 * 2 ** 26;
+   PCH_HDMI_COLOR_FORMAT_12BPC   : constant := 3 * 2 ** 26;
+   PCH_HDMI_COLOR_FORMAT_MASK    : constant := 7 * 2 ** 26;
+   PCH_HDMI_SDVO_ENCODING_SDVO   : constant := 0 * 2 ** 10;
+   PCH_HDMI_SDVO_ENCODING_HDMI   : constant := 2 * 2 ** 10;
+   PCH_HDMI_SDVO_ENCODING_MASK   : constant := 3 * 2 ** 10;
+   PCH_HDMI_VSYNC_ACTIVE_HIGH    : constant := 1 * 2 **  4;
+   PCH_HDMI_HSYNC_ACTIVE_HIGH    : constant := 1 * 2 **  3;
+   PCH_HDMI_PORT_DETECT          : constant := 1 * 2 **  2;
+
+   PCH_HDMI_MASK : constant Word32 :=
+      PCH_TRANSCODER_SELECT_MASK or
+      PCH_HDMI_ENABLE or
+      PCH_HDMI_COLOR_FORMAT_MASK or
+      PCH_HDMI_SDVO_ENCODING_MASK or
+      PCH_HDMI_HSYNC_ACTIVE_HIGH or
+      PCH_HDMI_VSYNC_ACTIVE_HIGH;
+
+   type PCH_HDMI_Array is array (PCH_HDMI_Port) of Registers.Registers_Index;
+   PCH_HDMI : constant PCH_HDMI_Array := PCH_HDMI_Array'
+     (PCH_HDMI_B => Registers.PCH_HDMIB,
+      PCH_HDMI_C => Registers.PCH_HDMIC,
+      PCH_HDMI_D => Registers.PCH_HDMID);
+
+   ----------------------------------------------------------------------------
+
+   procedure On (Port_Cfg : Port_Config; FDI_Port : FDI_Port_Type)
+   is
+      Polarity : constant Word32 :=
+        (if Port_Cfg.Mode.H_Sync_Active_High then
+            PCH_HDMI_HSYNC_ACTIVE_HIGH else 0) or
+        (if Port_Cfg.Mode.V_Sync_Active_High then
+            PCH_HDMI_VSYNC_ACTIVE_HIGH else 0);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      -- registers are just sufficient for setup with DVI adaptor
+
+      Registers.Unset_And_Set_Mask
+         (Register   => PCH_HDMI (Port_Cfg.PCH_Port),
+          Mask_Unset => PCH_HDMI_MASK,
+          Mask_Set   => PCH_HDMI_ENABLE or
+                        PCH_TRANSCODER_SELECT (FDI_Port) or
+                        PCH_HDMI_SDVO_ENCODING_HDMI or
+                        Polarity);
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off (Port : PCH_HDMI_Port)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+         (Register   => PCH_HDMI (Port),
+          Mask_Unset => PCH_HDMI_MASK,
+          Mask_Set   => PCH_HDMI_HSYNC_ACTIVE_HIGH or
+                        PCH_HDMI_VSYNC_ACTIVE_HIGH);
+   end Off;
+
+   procedure All_Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      for Port in PCH_HDMI_Port loop
+         Off (Port);
+      end loop;
+   end All_Off;
+
+end HW.GFX.GMA.PCH.HDMI;
diff --git a/common/ironlake/hw-gfx-gma-pch-hdmi.ads b/common/ironlake/hw-gfx-gma-pch-hdmi.ads
new file mode 100644
index 0000000..9853610
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-pch-hdmi.ads
@@ -0,0 +1,24 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.PCH.HDMI
+is
+
+   procedure On (Port_Cfg : Port_Config; FDI_Port : FDI_Port_Type)
+   with
+      Pre => Port_Cfg.PCH_Port in PCH_HDMI_Port;
+
+   procedure Off (Port : PCH_HDMI_Port);
+   procedure All_Off;
+
+end HW.GFX.GMA.PCH.HDMI;
diff --git a/common/ironlake/hw-gfx-gma-pch-lvds.adb b/common/ironlake/hw-gfx-gma-pch-lvds.adb
new file mode 100644
index 0000000..6ad334e
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-pch-lvds.adb
@@ -0,0 +1,58 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PCH.LVDS is
+
+   PCH_LVDS_ENABLE               : constant :=  1 * 2 ** 31;
+   PCH_LVDS_TWO_CHANNEL          : constant := 15 * 2 **  2;
+
+   PCH_LVDS_MASK : constant Word32 :=
+      PCH_TRANSCODER_SELECT_MASK or
+      PCH_LVDS_ENABLE or
+      PCH_LVDS_TWO_CHANNEL;
+
+   ----------------------------------------------------------------------------
+
+   procedure On (Port_Cfg : Port_Config; FDI_Port : FDI_Port_Type)
+   is
+      Two_Channel : constant Word32 :=
+        (if Port_Cfg.Mode.Dotclock >= Config.LVDS_Dual_Threshold then
+            PCH_LVDS_TWO_CHANNEL else 0);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+         (Register   => Registers.PCH_LVDS,
+          Mask_Unset => PCH_LVDS_MASK,
+          Mask_Set   => PCH_LVDS_ENABLE or
+                        PCH_TRANSCODER_SELECT (FDI_Port) or
+                        Two_Channel);
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_Mask (Registers.PCH_LVDS, PCH_LVDS_ENABLE);
+   end Off;
+
+end HW.GFX.GMA.PCH.LVDS;
diff --git a/common/ironlake/hw-gfx-gma-pch-lvds.ads b/common/ironlake/hw-gfx-gma-pch-lvds.ads
new file mode 100644
index 0000000..f4a5f26
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-pch-lvds.ads
@@ -0,0 +1,21 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.PCH.LVDS
+is
+
+   procedure On (Port_Cfg : Port_Config; FDI_Port : FDI_Port_Type);
+
+   procedure Off;
+
+end HW.GFX.GMA.PCH.LVDS;
diff --git a/common/ironlake/hw-gfx-gma-plls.adb b/common/ironlake/hw-gfx-gma-plls.adb
new file mode 100644
index 0000000..7ecfb66
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-plls.adb
@@ -0,0 +1,570 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PLLs
+with
+   Refined_State => (State => PLLs)
+is
+
+   Debug_Clocks : constant Boolean := False;
+
+   type Count_Range is new Natural range 0 .. 2;
+
+   type PLL_State is record
+      Use_Count   : Count_Range;
+      Used_For_DP : Boolean;
+      Link_Rate   : DP_Bandwidth;
+      Mode        : Mode_Type;
+   end record;
+
+   type PLL_State_Array is array (DPLLs) of PLL_State;
+
+   PLLs : PLL_State_Array;
+
+   ----------------------------------------------------------------------------
+
+   subtype N_Range     is Int64 range          3 ..          8;
+   subtype M_Range     is Int64 range         79 ..        128;
+   subtype M1_Range    is Int64 range         14 ..         25;
+   subtype M2_Range    is Int64 range          7 ..         12;
+   subtype P_Range     is Int64 range          5 ..        112;
+   subtype P1_Range    is Int64 range          1 ..          8;
+   subtype P2_Range    is Int64 range          5 ..         14;
+   subtype VCO_Range   is Int64 range 1760000000 .. 3510000000;
+   subtype Clock_Range is HW.GFX.Frequency_Type;
+
+   type Clock_Type is
+      record
+         N               : N_Range;
+         M1              : M1_Range;
+         M2              : M2_Range;
+         P1              : P1_Range;
+         P2              : P2_Range;
+         M               : M_Range;
+         P               : P_Range;
+         VCO             : VCO_Range;
+         Reference_Clock : Clock_Range;
+         Dotclock        : Clock_Range;
+      end record;
+
+   Invalid_Clock : constant Clock_Type := Clock_Type'
+      (N               => N_Range'Last,
+       M1              => M1_Range'Last,
+       M2              => M2_Range'Last,
+       P1              => P1_Range'Last,
+       P2              => P2_Range'Last,
+       Reference_Clock => Clock_Range'Last,
+       M               => M_Range'Last,
+       P               => P_Range'Last,
+       VCO             => VCO_Range'Last,
+       Dotclock        => Clock_Range'Last);
+
+   type Limits_Type is
+      record
+         N_Lower      : N_Range;
+         N_Upper      : N_Range;
+         M_Lower      : M_Range;
+         M_Upper      : M_Range;
+         M1_Lower     : M1_Range;
+         M1_Upper     : M1_Range;
+         M2_Lower     : M2_Range;
+         M2_Upper     : M2_Range;
+         P_Lower      : P_Range;
+         P_Upper      : P_Range;
+         P1_Lower     : P1_Range;
+         P1_Upper     : P1_Range;
+         P2_Fast      : P2_Range;
+         P2_Slow      : P2_Range;
+         P2_Threshold : Clock_Range;
+         VCO_Lower    : VCO_Range;
+         VCO_Upper    : VCO_Range;
+      end record;
+
+   LVDS_Single_Limits : constant Limits_Type := Limits_Type'
+     (N_Lower      =>   3,           N_Upper   =>   5,
+      M_Lower      =>  79,           M_Upper   => 118,
+      M1_Lower     =>  14,           M1_Upper  =>  22, -- this is capped by M_Upper >= 5 * M1 + M2_Lower
+      M2_Lower     =>   7,           M2_Upper  =>  11,
+      P_Lower      =>  28,           P_Upper   => 112,
+      P1_Lower     =>   2,           P1_Upper  =>   8,
+      P2_Fast      =>  14,           P2_Slow   =>  14,
+      P2_Threshold => Clock_Range'First,
+      VCO_Lower    => 1_760_000_000, VCO_Upper => 3_510_000_000);
+   LVDS_Dual_Limits : constant Limits_Type := Limits_Type'
+     (N_Lower      =>   3,           N_Upper   =>   5,
+      M_Lower      =>  79,           M_Upper   => 127,
+      M1_Lower     =>  14,           M1_Upper  =>  24,
+      M2_Lower     =>   7,           M2_Upper  =>  11,
+      P_Lower      =>  14,           P_Upper   =>  56,
+      P1_Lower     =>   2,           P1_Upper  =>   8,
+      P2_Fast      =>   7,           P2_Slow   =>   7,
+      P2_Threshold => Clock_Range'First,
+      VCO_Lower    => 1_760_000_000, VCO_Upper => 3_510_000_000);
+   All_Other_Limits : constant Limits_Type := Limits_Type'
+     (N_Lower      =>   3,           N_Upper   =>   7,
+      M_Lower      =>  79,           M_Upper   => 127,
+      M1_Lower     =>  14,           M1_Upper  =>  24,
+      M2_Lower     =>   7,           M2_Upper  =>  11,
+      P_Lower      =>   5,           P_Upper   =>  80,
+      P1_Lower     =>   1,           P1_Upper  =>   8,
+      -- use P2_Slow if Dotclock <= P2_Threshold, P2_Fast otherwise
+      P2_Fast      =>   5,           P2_Slow   =>  10,
+      P2_Threshold =>   225_000_000,
+      VCO_Lower    => 1_760_000_000, VCO_Upper => 3_510_000_000);
+
+   ----------------------------------------------------------------------------
+
+   type Regs is array (DPLLs) of Registers.Registers_Index;
+
+   DPLL : constant Regs := Regs'(Registers.PCH_DPLL_A, Registers.PCH_DPLL_B);
+   DPLL_VCO_ENABLE         : constant := 1 * 2 ** 31;
+   DPLL_P2_10_OR_14        : constant := 0 * 2 ** 24;
+   DPLL_P2_5_OR_7          : constant := 1 * 2 ** 24;
+   DPLL_P1_DIVIDER_SHIFT   : constant := 16;
+   DPLL_SDVOCLK            : constant := 2 * 2 ** 13;
+
+   DPLL_HIGH_SPEED : constant := 1 * 2 ** 30;
+   DPLL_MODE_LVDS  : constant := 2 * 2 ** 26;
+   DPLL_MODE_DAC   : constant := 1 * 2 ** 26;
+   DPLL_DREFCLK    : constant := 0 * 2 ** 13;
+   DPLL_SSC        : constant := 3 * 2 ** 13;
+
+   MODE_DPLL_DAC_HDMI : constant Word32 := Word32'
+      (DPLL_MODE_DAC or DPLL_DREFCLK or DPLL_HIGH_SPEED);
+
+   MODE_DPLL_LVDS : constant Word32 := Word32'
+      (DPLL_MODE_LVDS or DPLL_SSC);
+
+   MODE_DPLL_DP : constant Word32 := Word32'
+      (DPLL_MODE_DAC or DPLL_SSC or DPLL_HIGH_SPEED);
+
+   type DPLL_Mode_Array is array (Display_Type) of Word32;
+
+   DPLL_Mode : constant DPLL_Mode_Array := DPLL_Mode_Array'
+     (LVDS     => MODE_DPLL_LVDS,
+      DP       => MODE_DPLL_DP,
+      others   => MODE_DPLL_DAC_HDMI);
+
+   FP0 : constant Regs := Regs'(Registers.PCH_FPA0, Registers.PCH_FPB0);
+   FP1 : constant Regs := Regs'(Registers.PCH_FPA1, Registers.PCH_FPB1);
+   FP_DOUBLE_CLOCK       : constant := 1 * 2 ** 27;
+   FP_N_SHIFT            : constant := 16;
+   FP_M1_SHIFT           : constant := 8;
+   FP_M2_SHIFT           : constant := 0;
+
+   ----------------------------------------------------------------------------
+
+   procedure Verify_Parameters
+      (N               : in     N_Range;
+       M1              : in     M1_Range;
+       M2              : in     M2_Range;
+       P1              : in     P1_Range;
+       P2              : in     P2_Range;
+       Reference_Clock : in     Clock_Range;
+       Current_Limits  : in     Limits_Type;
+       Result          :    out Clock_Type;
+       Valid           :    out Boolean)
+   with
+      Global => null,
+      Pre => True,
+      Post => True
+   is
+      M        : Int64;
+      P        : Int64;
+      VCO      : Int64;
+      Dotclock : Int64;
+   begin
+      pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      M        := 5 * M1 + M2;
+      P        := P1 * P2;
+      VCO      := (Int64 (Reference_Clock) * M) / N;
+      Dotclock := VCO / P;
+
+      pragma Debug (Debug_Clocks and not (Current_Limits.P1_Lower  <= P1  and P1  <= Current_Limits.P1_Upper ), Debug.Put_Line ("P1 out of range."));
+      pragma Debug (Debug_Clocks and     (Current_Limits.P2_Fast   /= P2  and P2  /= Current_Limits.P2_Slow  ), Debug.Put_Line ("P2 out of range."));
+      pragma Debug (Debug_Clocks and not (Current_Limits.P_Lower   <= P   and P   <= Current_Limits.P_Upper  ), Debug.Put_Line ("P out of range."));
+      pragma Debug (Debug_Clocks and not (Current_Limits.M1_Lower  <= M1  and M1  <= Current_Limits.M1_Upper ), Debug.Put_Line ("M1 out of range."));
+      pragma Debug (Debug_Clocks and not (Current_Limits.M2_Lower  <= M2  and M2  <= Current_Limits.M2_Upper ), Debug.Put_Line ("M2 out of range."));
+      -- pragma Debug (Debug_Clocks and not (M2 <= M1                                           ), Debug.Put_Line ("M1 greater thant M2."));
+      pragma Debug (Debug_Clocks and not (Current_Limits.N_Lower   <= N   and N   <= Current_Limits.N_Upper  ), Debug.Put_Line ("N out of range."));
+      pragma Debug (Debug_Clocks and not (Current_Limits.M_Lower   <= M   and M   <= Current_Limits.M_Upper  ), Debug.Put_Line ("M out of range."));
+      pragma Debug (Debug_Clocks and not (Current_Limits.VCO_Lower <= VCO and VCO <= Current_Limits.VCO_Upper), Debug.Put_Line ("VCO out of range."));
+
+      pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock),       Debug.Put_Line ("Dotclock too low."));
+      pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock),       Debug.Put_Int64 (Dotclock));
+      pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock),       Debug.New_Line);
+
+      pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)),        Debug.Put_Line ("Dotclock too high."));
+      pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)),        Debug.Put_Int64 (Dotclock));
+      pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)),        Debug.New_Line);
+
+      Valid :=
+         Current_Limits.P1_Lower  <= P1  and P1  <= Current_Limits.P1_Upper  and
+         (Current_Limits.P2_Fast   = P2   or P2   = Current_Limits.P2_Slow)  and
+         Current_Limits.P_Lower   <= P   and P   <= Current_Limits.P_Upper   and
+         Current_Limits.M1_Lower  <= M1  and M1  <= Current_Limits.M1_Upper  and
+         Current_Limits.M2_Lower  <= M2  and M2  <= Current_Limits.M2_Upper  and
+         -- M2 <= M1                                                            and
+         Current_Limits.N_Lower   <= N   and N   <= Current_Limits.N_Upper   and
+         Current_Limits.M_Lower   <= M   and M   <= Current_Limits.M_Upper   and
+         Current_Limits.VCO_Lower <= VCO and VCO <= Current_Limits.VCO_Upper and
+         Int64 (Clock_Range'First) <= Dotclock                               and
+         Dotclock <= Int64 (Clock_Range'Last);
+
+      if Valid
+      then
+         Result := Clock_Type'
+            (N               => N,
+             M1              => M1,
+             M2              => M2,
+             P1              => P1,
+             P2              => P2,
+             Reference_Clock => Reference_Clock,
+             M               => M,
+             P               => P,
+             VCO             => VCO,
+             Dotclock        => Clock_Range (Dotclock));
+      else
+         Result := Invalid_Clock;
+      end if;
+
+   end Verify_Parameters;
+
+   procedure Calculate_Clock_Parameters
+      (Display         : in     Display_Type;
+       Target_Dotclock : in     Clock_Range;
+       Reference_Clock : in     Clock_Range;
+       Best_Clock      :    out Clock_Type;
+       Valid           :    out Boolean)
+   with
+      Global => null,
+      Pre => True,
+      Post => True
+   is
+      Limits : constant Limits_Type :=
+        (if Display = LVDS then
+           (if Target_Dotclock >= Config.LVDS_Dual_Threshold then
+               LVDS_Dual_Limits
+            else
+               LVDS_Single_Limits)
+         else
+            All_Other_Limits);
+
+      P2               : P2_Range;
+      Best_Delta       : Int64 := Int64'Last;
+      Current_Delta    : Int64;
+      Current_Clock    : Clock_Type;
+      Registers_Valid  : Boolean;
+   begin
+      pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Valid      := False;
+      Best_Clock := Invalid_Clock;
+
+      if Target_Dotclock <= Limits.P2_Threshold then
+         P2 := Limits.P2_Slow;
+      else
+         P2 := Limits.P2_Fast;
+      end if;
+
+      for N in N_Range range Limits.N_Lower .. Limits.N_Upper
+      loop
+         -- reverse loops as hardware prefers higher values
+         for M1 in reverse M1_Range range Limits.M1_Lower .. Limits.M1_Upper
+         loop
+            for M2 in reverse M2_Range range Limits.M2_Lower .. Limits.M2_Upper
+            loop
+               for P1 in reverse P1_Range range Limits.P1_Lower .. Limits.P1_Upper
+               loop
+                  Verify_Parameters
+                     (N               => N,
+                      M1              => M1,
+                      M2              => M2,
+                      P1              => P1,
+                      P2              => P2,
+                      Reference_Clock => Reference_Clock,
+                      Current_Limits  => Limits,
+                      Result          => Current_Clock,
+                      Valid           => Registers_Valid);
+
+                  if Registers_Valid
+                  then
+                     if Current_Clock.Dotclock > Target_Dotclock
+                     then
+                        Current_Delta := Current_Clock.Dotclock - Target_Dotclock;
+                     else
+                        Current_Delta := Target_Dotclock - Current_Clock.Dotclock;
+                     end if;
+
+                     if Current_Delta < Best_Delta
+                     then
+                        Best_Delta := Current_Delta;
+                        Best_Clock := Current_Clock;
+                        Valid      := True;
+                     end if;
+
+                     pragma Debug (Debug_Clocks, Debug.Put ("Current/Target/Best_Delta: "));
+                     pragma Debug (Debug_Clocks, Debug.Put_Int64 (Current_Clock.Dotclock));
+                     pragma Debug (Debug_Clocks, Debug.Put ("/"));
+                     pragma Debug (Debug_Clocks, Debug.Put_Int64 (Target_Dotclock));
+                     pragma Debug (Debug_Clocks, Debug.Put ("/"));
+                     pragma Debug (Debug_Clocks, Debug.Put_Int64 (Best_Delta));
+                     pragma Debug (Debug_Clocks, Debug.Put_Line ("."));
+
+                  end if;
+               end loop;
+            end loop;
+         end loop;
+      end loop;
+
+      pragma Debug (Valid,     Debug.Put_Line ("Valid clock found."));
+      pragma Debug (Valid,     Debug.Put ("Best/Target/Delta: "));
+      pragma Debug (Valid,     Debug.Put_Int64 (Best_Clock.Dotclock));
+      pragma Debug (Valid,     Debug.Put ("/"));
+      pragma Debug (Valid,     Debug.Put_Int64 (Target_Dotclock));
+      pragma Debug (Valid,     Debug.Put ("/"));
+      pragma Debug (Valid,     Debug.Put_Int64 (Best_Delta));
+      pragma Debug (Valid,     Debug.Put_Line ("."));
+      pragma Debug (not Valid, Debug.Put_Line ("No valid clock found."));
+
+   end Calculate_Clock_Parameters;
+
+   procedure Program_DPLL
+     (PLL      : DPLLs;
+      Display  : Display_Type;
+      Clk      : Clock_Type)
+   with
+      Global => (In_Out => Registers.Register_State),
+      Pre => True,
+      Post => True
+   is
+      FP, Encoded_P1, Encoded_P2 : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      FP :=
+         Shift_Left (Word32 (Clk.N - 2), FP_N_SHIFT)     or
+         Shift_Left (Word32 (Clk.M1 - 2), FP_M1_SHIFT)   or
+         Shift_Left (Word32 (Clk.M2 - 2), FP_M2_SHIFT);
+
+      Registers.Write (FP0 (PLL), FP);
+      Registers.Write (FP1 (PLL), FP);
+
+      Encoded_P1 := Shift_Left (1, Natural (Clk.P1) - 1);
+
+      if Clk.P2 = 5 or Clk.P2 = 7
+      then
+         Encoded_P2 := DPLL_P2_5_OR_7;
+      else
+         Encoded_P2 := DPLL_P2_10_OR_14;
+      end if;
+
+      Registers.Write
+         (Register => DPLL (PLL),
+          Value    => DPLL_Mode (Display)                            or
+                      Encoded_P2                                     or
+                      Shift_Left (Encoded_P1, DPLL_P1_DIVIDER_SHIFT) or
+                      Encoded_P1);
+   end Program_DPLL;
+
+   procedure On
+     (PLL      : in     T;
+      Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   is
+      Target_Clock : constant Frequency_Type :=
+        (if Port_Cfg.Display = DP then
+            DP_Symbol_Rate (Port_Cfg.DP.Bandwidth)
+         else
+            Port_Cfg.Mode.Dotclock);
+      Clk : Clock_Type;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Success := PLL in DPLLs;
+      Clk := Invalid_Clock;
+
+      if Success then
+         if Port_Cfg.Display = DP then
+            Success := True;
+            -- we use static values for DP
+            case Port_Cfg.DP.Bandwidth is
+               when DP_Bandwidth_1_62 =>
+                  Clk.N    :=  3;
+                  Clk.M1   := 14;
+                  Clk.M2   := 11;
+                  Clk.P1   :=  2;
+                  Clk.P2   := 10;
+               when DP_Bandwidth_2_7 =>
+                  Clk.N    :=  4;
+                  Clk.M1   := 16;
+                  Clk.M2   := 10;
+                  Clk.P1   :=  1;
+                  Clk.P2   := 10;
+               when others =>
+                  Success := False;
+            end case;
+         elsif Target_Clock <= 340_000_000 then
+            Calculate_Clock_Parameters
+              (Display           => Port_Cfg.Display,
+               Target_Dotclock   => Target_Clock,
+               -- should be, but doesn't has to be always the same:
+               Reference_Clock   => 120_000_000,
+               Best_Clock        => Clk,
+               Valid             => Success);
+         else
+            Success := False;
+            pragma Debug (Debug.Put ("WARNING: Targeted clock too high: "));
+            pragma Debug (Debug.Put_Int64 (Target_Clock));
+            pragma Debug (Debug.Put (" > "));
+            pragma Debug (Debug.Put_Int32 (340_000_000));
+            pragma Debug (Debug.New_Line);
+            pragma Debug (Debug.New_Line);
+         end if;
+      end if;
+
+      if Success then
+         Program_DPLL (PLL, Port_Cfg.Display, Clk);
+
+         Registers.Set_Mask (DPLL (PLL), DPLL_VCO_ENABLE);
+         Registers.Posting_Read (DPLL (PLL));
+         Time.U_Delay (150);
+      end if;
+   end On;
+
+   procedure Off (PLL : T)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if PLL in DPLLs then
+         Registers.Unset_Mask (DPLL (PLL), DPLL_VCO_ENABLE);
+      end if;
+   end Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Initialize
+   is
+   begin
+      PLLs :=
+        (DPLLs =>
+           (Use_Count   => 0,
+            Used_For_DP => False,
+            Link_Rate   => DP_Bandwidth'First,
+            Mode        => Invalid_Mode));
+   end Initialize;
+
+   procedure Alloc_Configurable
+     (Port_Cfg : in     Port_Config;
+      PLL      :    out T;
+      Success  :    out Boolean)
+   with
+      Pre => True
+   is
+      function Config_Matches (PE : PLL_State) return Boolean
+      is
+      begin
+         return
+            PE.Used_For_DP = (Port_Cfg.Display = DP) and
+            ((PE.Used_For_DP and PE.Link_Rate = Port_Cfg.DP.Bandwidth) or
+             (not PE.Used_For_DP and PE.Mode = Port_Cfg.Mode));
+      end Config_Matches;
+   begin
+      -- try to find shareable PLL
+      for P in DPLLs loop
+         Success := PLLs (P).Use_Count /= 0 and
+                     PLLs (P).Use_Count /= Count_Range'Last and
+                     Config_Matches (PLLs (P));
+         if Success then
+            PLL := P;
+            PLLs (PLL).Use_Count := PLLs (PLL).Use_Count + 1;
+            return;
+         end if;
+      end loop;
+
+      -- try to find free PLL
+      for P in DPLLs loop
+         if PLLs (P).Use_Count = 0 then
+            PLL := P;
+            On (PLL, Port_Cfg, Success);
+            if Success then
+               PLLs (PLL) :=
+                 (Use_Count   => 1,
+                  Used_For_DP => Port_Cfg.Display = DP,
+                  Link_Rate   => Port_Cfg.DP.Bandwidth,
+                  Mode        => Port_Cfg.Mode);
+            end if;
+            return;
+         end if;
+      end loop;
+
+      PLL := Invalid;
+   end Alloc_Configurable;
+
+   procedure Alloc
+     (Port_Cfg : in     Port_Config;
+      PLL      :    out T;
+      Success  :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Port = DIGI_A then
+         PLL := Invalid;
+         Success := True;
+      else
+         Alloc_Configurable (Port_Cfg, PLL, Success);
+      end if;
+   end Alloc;
+
+   procedure Free (PLL : T)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if PLL in DPLLs then
+         if PLLs (PLL).Use_Count /= 0 then
+            PLLs (PLL).Use_Count := PLLs (PLL).Use_Count - 1;
+            if PLLs (PLL).Use_Count = 0 then
+               Off (PLL);
+            end if;
+         end if;
+      end if;
+   end Free;
+
+   procedure All_Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      for PLL in DPLLs loop
+         Off (PLL);
+      end loop;
+   end All_Off;
+
+   function Register_Value (PLL : T) return Word32
+   is
+   begin
+      return (if PLL = DPLL_B then 1 else 0);
+   end Register_Value;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/ironlake/hw-gfx-gma-plls.ads b/common/ironlake/hw-gfx-gma-plls.ads
new file mode 100644
index 0000000..8e7325e
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-plls.ads
@@ -0,0 +1,39 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs
+with
+   Abstract_State => (State with Part_Of => GMA.State)
+is
+
+   -- XXX: Types should be private (but that triggers a bug in SPARK GPL 2016)
+   type T is (Invalid_PLL, DPLL_A, DPLL_B);
+   subtype DPLLs is T range DPLL_A .. DPLL_B;
+   Invalid : constant T := Invalid_PLL;
+
+   procedure Initialize
+   with
+      Global => (Output => State);
+
+   procedure Alloc
+     (Port_Cfg : in     Port_Config;
+      PLL      :    out T;
+      Success  :    out Boolean);
+
+   procedure Free (PLL : T);
+
+   procedure All_Off;
+
+   function Register_Value (PLL : T) return Word32;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/ironlake/hw-gfx-gma-port_detect.adb b/common/ironlake/hw-gfx-gma-port_detect.adb
new file mode 100644
index 0000000..4e26100
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-port_detect.adb
@@ -0,0 +1,160 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Port_Detect
+is
+
+   PCH_ADPA_CRT_HPD_CHANNEL_MASK       : constant := 3 * 2 ** 24;
+   PCH_ADPA_CRT_HPD_ENABLE             : constant := 1 * 2 ** 23;
+
+   DP_PORT_DETECTED                    : constant := 1 * 2 **  2;
+   PCH_DIGI_PORT_DETECTED              : constant := 1 * 2 **  2;
+   PCH_LVDS_PORT_DETECTED              : constant := 1 * 2 **  1;
+
+   SHOTPLUG_CTL_DETECT_MASK            : constant := 16#0003_0303#;
+
+   type PCH_Digital_Port_Value is array (PCH_HDMI_Port) of Word32;
+   SHOTPLUG_CTL_HPD_INPUT_ENABLE : constant PCH_Digital_Port_Value :=
+     (PCH_HDMI_B => 1 * 2 **  4,
+      PCH_HDMI_C => 1 * 2 ** 12,
+      PCH_HDMI_D => 1 * 2 ** 20);
+   SHOTPLUG_CTL_SHORT_PULSE_MASK : constant PCH_Digital_Port_Value :=
+     (PCH_HDMI_B => 3 * 2 **  2,
+      PCH_HDMI_C => 3 * 2 ** 10,
+      PCH_HDMI_D => 3 * 2 ** 18);
+   SHOTPLUG_CTL_HPD_STATUS : constant PCH_Digital_Port_Value :=
+     (PCH_HDMI_B => 3 * 2 **  0,
+      PCH_HDMI_C => 3 * 2 **  8,
+      PCH_HDMI_D => 3 * 2 ** 16);
+   SHOTPLUG_CTL_LONG_DETECT : constant PCH_Digital_Port_Value :=
+     (PCH_HDMI_B => 1 * 2 **  1,
+      PCH_HDMI_C => 1 * 2 **  9,
+      PCH_HDMI_D => 1 * 2 ** 17);
+
+   type PCH_Digital_Regs is array (PCH_HDMI_Port) of Registers.Registers_Index;
+   PCH_HDMI : constant PCH_Digital_Regs :=
+     (PCH_HDMI_B => Registers.PCH_HDMIB,
+      PCH_HDMI_C => Registers.PCH_HDMIC,
+      PCH_HDMI_D => Registers.PCH_HDMID);
+   PCH_DP : constant PCH_Digital_Regs :=
+     (PCH_HDMI_B => Registers.PCH_DP_B,
+      PCH_HDMI_C => Registers.PCH_DP_C,
+      PCH_HDMI_D => Registers.PCH_DP_D);
+
+   procedure Initialize
+   is
+      Internal_Detected,
+      HDMI_Detected,
+      DP_Detected : Boolean;
+
+      type PCH_Port_To_GMA_Port is array (PCH_HDMI_Port) of Port_Type;
+      To_Digital_Port : constant PCH_Port_To_GMA_Port :=
+        (PCH_HDMI_B => Digital1,
+         PCH_HDMI_C => Digital2,
+         PCH_HDMI_D => Digital3);
+      To_DP_Port : constant PCH_Port_To_GMA_Port :=
+        (PCH_HDMI_B => DP1,
+         PCH_HDMI_C => DP2,
+         PCH_HDMI_D => DP3);
+   begin
+      -- PCH_DAC (_A)
+      Registers.Set_Mask
+        (Register => Registers.PCH_ADPA,
+         Mask     => PCH_ADPA_CRT_HPD_CHANNEL_MASK or   -- clear status
+                     PCH_ADPA_CRT_HPD_ENABLE);
+
+      case Config.Internal_Display is
+         when LVDS =>
+            -- PCH_LVDS
+            Registers.Is_Set_Mask
+              (Register => Registers.PCH_LVDS,
+               Mask     => PCH_LVDS_PORT_DETECTED,
+               Result   => Internal_Detected);
+         when DP =>
+            -- eDP
+            Registers.Is_Set_Mask
+              (Register => Registers.DP_CTL_A,
+               Mask     => DP_PORT_DETECTED,
+               Result   => Internal_Detected);
+         when None =>
+            Internal_Detected := False;
+      end case;
+      Config.Valid_Port (Internal) := Internal_Detected;
+
+      -- PCH_HDMI_[BCD], PCH_DP_[BCD] share hotplug registers
+      for PCH_Port in PCH_HDMI_Port loop
+         Registers.Is_Set_Mask
+           (Register => PCH_HDMI (PCH_Port),
+            Mask     => PCH_DIGI_PORT_DETECTED,
+            Result   => HDMI_Detected);
+         Config.Valid_Port (To_Digital_Port (PCH_Port)) := HDMI_Detected;
+
+         Registers.Is_Set_Mask
+           (Register => PCH_DP (PCH_Port),
+            Mask     => PCH_DIGI_PORT_DETECTED,
+            Result   => DP_Detected);
+         Config.Valid_Port (To_DP_Port (PCH_Port)) := DP_Detected;
+
+         if HDMI_Detected or DP_Detected then
+            Registers.Unset_And_Set_Mask
+              (Register    => Registers.SHOTPLUG_CTL,
+               Mask_Unset  => SHOTPLUG_CTL_DETECT_MASK or
+                              SHOTPLUG_CTL_SHORT_PULSE_MASK (PCH_Port),
+               Mask_Set    => SHOTPLUG_CTL_HPD_INPUT_ENABLE (PCH_Port) or
+                              SHOTPLUG_CTL_HPD_STATUS (PCH_Port));   -- clear
+         else
+            Registers.Unset_Mask
+              (Register => Registers.SHOTPLUG_CTL,
+               Mask     => SHOTPLUG_CTL_DETECT_MASK or
+                           SHOTPLUG_CTL_HPD_INPUT_ENABLE (PCH_Port));
+         end if;
+      end loop;
+   end Initialize;
+
+   procedure Hotplug_Detect (Port_Cfg : in Port_Config; Detected : out Boolean)
+   is
+      Ctl32 : Word32;
+      PCH_Port : constant GMA.PCH_Port :=
+        (case Port_Cfg.PCH_Port is
+            when PCH_DP_B  => PCH_HDMI_B,
+            when PCH_DP_C  => PCH_HDMI_C,
+            when PCH_DP_D  => PCH_HDMI_D,
+            when others    => Port_Cfg.PCH_Port);
+   begin
+      case PCH_Port is
+         when PCH_DAC =>
+            Registers.Read (Registers.PCH_ADPA, Ctl32, Verbose => False);
+            Ctl32 := Ctl32 and PCH_ADPA_CRT_HPD_CHANNEL_MASK;
+            Detected := Ctl32 = PCH_ADPA_CRT_HPD_CHANNEL_MASK;
+            if Ctl32 /= 0 then
+               Registers.Set_Mask (Registers.PCH_ADPA, Ctl32);
+            end if;
+         when PCH_HDMI_B .. PCH_HDMI_D =>
+            Registers.Read (Registers.SHOTPLUG_CTL, Ctl32, Verbose => False);
+            Detected := (Ctl32 and SHOTPLUG_CTL_LONG_DETECT (PCH_Port)) /= 0;
+
+            if (Ctl32 and SHOTPLUG_CTL_HPD_STATUS (PCH_Port)) /= 0 then
+               Registers.Unset_And_Set_Mask
+                 (Register    => Registers.SHOTPLUG_CTL,
+                  Mask_Unset  => SHOTPLUG_CTL_DETECT_MASK,
+                  Mask_Set    => SHOTPLUG_CTL_HPD_STATUS (PCH_Port));
+            end if;
+         when others =>
+            Detected := False;
+      end case;
+   end Hotplug_Detect;
+
+end HW.GFX.GMA.Port_Detect;
diff --git a/common/ironlake/hw-gfx-gma-power_and_clocks.ads b/common/ironlake/hw-gfx-gma-power_and_clocks.ads
new file mode 100644
index 0000000..644c0c7
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-power_and_clocks.ads
@@ -0,0 +1,17 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Power_And_Clocks_Ironlake;
+
+private package HW.GFX.GMA.Power_And_Clocks
+   renames HW.GFX.GMA.Power_And_Clocks_Ironlake;
diff --git a/common/ironlake/hw-gfx-gma-power_and_clocks_ironlake.adb b/common/ironlake/hw-gfx-gma-power_and_clocks_ironlake.adb
new file mode 100644
index 0000000..e6fa3aa
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-power_and_clocks_ironlake.adb
@@ -0,0 +1,54 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.Time;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Power_And_Clocks_Ironlake is
+
+   PCH_DREF_CONTROL_120MHZ_CPU_OUTPUT_MASK      : constant := 3 * 2 ** 13;
+   PCH_DREF_CONTROL_120MHZ_CPU_OUTPUT_SSC       : constant := 2 * 2 ** 13;
+   PCH_DREF_CONTROL_120MHZ_CPU_OUTPUT_NONSSC    : constant := 3 * 2 ** 13;
+   PCH_DREF_CONTROL_120MHZ_SSC_EN_MASK          : constant := 3 * 2 ** 11;
+   PCH_DREF_CONTROL_120MHZ_SSC_EN               : constant := 2 * 2 ** 11;
+   PCH_DREF_CONTROL_120MHZ_NONSSC_EN_MASK       : constant := 3 * 2 **  9;
+   PCH_DREF_CONTROL_120MHZ_NONSSC_EN            : constant := 2 * 2 **  9;
+   PCH_DREF_CONTROL_120MHZ_SSC4_EN_MASK         : constant := 3 * 2 **  7;
+   PCH_DREF_CONTROL_120MHZ_SSC4_EN              : constant := 2 * 2 **  7;
+   PCH_DREF_CONTROL_120MHZ_SSC4_DOWNSPREAD      : constant := 0 * 2 **  6;
+   PCH_DREF_CONTROL_120MHZ_SSC4_CENTERSPREAD    : constant := 1 * 2 **  6;
+   PCH_DREF_CONTROL_120MHZ_SSC_MODULATION_EN    : constant := 1 * 2 **  1;
+   PCH_DREF_CONTROL_120MHZ_SSC4_MODULATION_EN   : constant := 1 * 2 **  0;
+
+   procedure Initialize is
+   begin
+      -- ILK: enable non-spread spectrum clock, enable spread spectrum clock
+      Registers.Write
+        (Register => Registers.PCH_DREF_CONTROL,
+         Value    => PCH_DREF_CONTROL_120MHZ_SSC_EN or
+                     PCH_DREF_CONTROL_120MHZ_NONSSC_EN or
+                     PCH_DREF_CONTROL_120MHZ_SSC_MODULATION_EN);
+      Registers.Posting_Read (Registers.PCH_DREF_CONTROL);
+      Time.U_Delay (1);
+      if Config.Internal_Is_EDP then   -- TODO: check for presence
+         -- always use spread spectrum clock for CPU output
+         Registers.Set_Mask
+           (Register => Registers.PCH_DREF_CONTROL,
+            Mask     => PCH_DREF_CONTROL_120MHZ_CPU_OUTPUT_SSC);
+         Registers.Posting_Read (Registers.PCH_DREF_CONTROL);
+         Time.U_Delay (20);   -- DMI latency
+      end if;
+   end Initialize;
+
+end HW.GFX.GMA.Power_And_Clocks_Ironlake;
diff --git a/common/ironlake/hw-gfx-gma-power_and_clocks_ironlake.ads b/common/ironlake/hw-gfx-gma-power_and_clocks_ironlake.ads
new file mode 100644
index 0000000..f50388c
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-power_and_clocks_ironlake.ads
@@ -0,0 +1,29 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Power_And_Clocks_Ironlake is
+
+   procedure Initialize;
+
+   procedure Pre_All_Off is null;
+
+   procedure Post_All_Off is null;
+
+   procedure Power_Set_To (Configs : Configs_Type) is null;
+
+   procedure Power_Up (Old_Configs, New_Configs : Configs_Type) is null;
+
+   procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type)
+   is null;
+
+end HW.GFX.GMA.Power_And_Clocks_Ironlake;
diff --git a/common/skylake/Makefile.inc b/common/skylake/Makefile.inc
new file mode 100644
index 0000000..2dec2ef
--- /dev/null
+++ b/common/skylake/Makefile.inc
@@ -0,0 +1,10 @@
+gfxinit-y += hw-gfx-gma-plls-dpll.adb
+gfxinit-y += hw-gfx-gma-plls-dpll.ads
+gfxinit-y += hw-gfx-gma-plls-dpll_0.adb
+gfxinit-y += hw-gfx-gma-plls-dpll_0.ads
+gfxinit-y += hw-gfx-gma-plls.adb
+gfxinit-y += hw-gfx-gma-plls.ads
+gfxinit-y += hw-gfx-gma-power_and_clocks.ads
+gfxinit-y += hw-gfx-gma-power_and_clocks_skylake.adb
+gfxinit-y += hw-gfx-gma-power_and_clocks_skylake.ads
+gfxinit-y += hw-gfx-gma-spll.ads
diff --git a/common/skylake/hw-gfx-gma-plls-dpll.adb b/common/skylake/hw-gfx-gma-plls-dpll.adb
new file mode 100644
index 0000000..c48f4ff
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-plls-dpll.adb
@@ -0,0 +1,357 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.PLLs.DPLL is
+
+   -- NOTE: Order of DPLLs is twisted => always use named associations!
+
+   type Regs is array (Configurable_DPLLs) of Registers.Registers_Index;
+
+   DPLL_CTL : constant Regs := Regs'
+     (DPLL1 => Registers.LCPLL2_CTL,
+      DPLL2 => Registers.WRPLL_CTL_1,
+      DPLL3 => Registers.WRPLL_CTL_2);
+   DPLL_CTL_PLL_ENABLE                 : constant := 1 * 2 ** 31;
+
+   ----------------------------------------------------------------------------
+
+   DPLL_CFGR1 : constant Regs := Regs'
+     (DPLL1 => Registers.DPLL1_CFGR1,
+      DPLL2 => Registers.DPLL2_CFGR1,
+      DPLL3 => Registers.DPLL3_CFGR1);
+   DPLL_CFGR1_FREQUENCY_ENABLE         : constant :=        1 * 2 ** 31;
+   DPLL_CFGR1_DCO_FRACTION_SHIFT       : constant :=                  9;
+   DPLL_CFGR1_DCO_FRACTION_MASK        : constant := 16#7fff# * 2 **  9;
+   DPLL_CFGR1_DCO_INTEGER_MASK         : constant := 16#01ff# * 2 **  0;
+
+   DPLL_CFGR2 : constant Regs := Regs'
+     (DPLL1 => Registers.DPLL1_CFGR2,
+      DPLL2 => Registers.DPLL2_CFGR2,
+      DPLL3 => Registers.DPLL3_CFGR2);
+   DPLL_CFGR2_QDIV_RATIO_SHIFT         : constant :=            8;
+   DPLL_CFGR2_QDIV_RATIO_MASK          : constant := 255 * 2 ** 8;
+   DPLL_CFGR2_QDIV_MODE                : constant :=   1 * 2 ** 7;
+   DPLL_CFGR2_KDIV_SHIFT               : constant :=            5;
+   DPLL_CFGR2_KDIV_MASK                : constant :=   3 * 2 ** 5;
+   DPLL_CFGR2_PDIV_SHIFT               : constant :=            2;
+   DPLL_CFGR2_PDIV_MASK                : constant :=   7 * 2 ** 2;
+   DPLL_CFGR2_CENTRAL_FREQ_MASK        : constant :=   3 * 2 ** 0;
+   DPLL_CFGR2_CENTRAL_FREQ_9600MHZ     : constant :=   0 * 2 ** 0;
+   DPLL_CFGR2_CENTRAL_FREQ_9000MHZ     : constant :=   1 * 2 ** 0;
+   DPLL_CFGR2_CENTRAL_FREQ_8400MHZ     : constant :=   3 * 2 ** 0;
+
+   ----------------------------------------------------------------------------
+
+   HDMI_MODE            : constant := 1 * 2 **  5;
+   SSC                  : constant := 1 * 2 **  4;
+   LINK_RATE_MASK       : constant := 7 * 2 **  1;
+   LINK_RATE_2700MHZ    : constant := 0 * 2 **  1;
+   LINK_RATE_1350MHZ    : constant := 1 * 2 **  1;
+   LINK_RATE_810MHZ     : constant := 2 * 2 **  1;
+   LINK_RATE_1620MHZ    : constant := 3 * 2 **  1;
+   LINK_RATE_1080MHZ    : constant := 4 * 2 **  1;
+   LINK_RATE_2160MHZ    : constant := 5 * 2 **  1;
+   OVERRIDE             : constant := 1 * 2 **  0;
+
+   LOCK                 : constant := 1 * 2 **  0;
+
+   type Shifts is array (Configurable_DPLLs) of Natural;
+   DPLL_CTRL1_SHIFT : constant Shifts :=
+     (DPLL1 => 6, DPLL2 => 12, DPLL3 => 18);
+   DPLL_STATUS_SHIFT : constant Shifts :=
+     (DPLL1 => 8, DPLL2 => 16, DPLL3 => 24);
+
+   function LINK_RATE (Link_Rate : DP_Bandwidth) return Word32 is
+   begin
+      return (case Link_Rate is
+               when DP_Bandwidth_5_4   => LINK_RATE_2700MHZ,
+               when DP_Bandwidth_2_7   => LINK_RATE_1350MHZ,
+               when DP_Bandwidth_1_62  => LINK_RATE_810MHZ);
+   end LINK_RATE;
+
+   function DPLL_CTRL1_DPLLx
+     (Value : Word32;
+      PLL   : Configurable_DPLLs)
+      return Word32 is
+   begin
+      return Shift_Left (Value, DPLL_CTRL1_SHIFT (PLL));
+   end DPLL_CTRL1_DPLLx;
+
+   function DPLL_STATUS_DPLLx_LOCK (PLL : Configurable_DPLLs) return Word32 is
+   begin
+      return Shift_Left (LOCK, DPLL_STATUS_SHIFT (PLL));
+   end DPLL_STATUS_DPLLx_LOCK;
+
+   ----------------------------------------------------------------------------
+
+   subtype PDiv_Range is Pos64 range 1 ..   7;
+   subtype QDiv_Range is Pos64 range 1 .. 255;
+   subtype KDiv_Range is Pos64 range 1 ..   5;
+
+   type Central_Frequency is (CF_INVALID, CF_9600MHZ, CF_9000MHZ, CF_8400MHZ);
+   subtype Valid_Central_Freq is
+      Central_Frequency range CF_9600MHZ .. CF_8400MHZ;
+
+   type CF_Pos is array (Valid_Central_Freq) of Pos64;
+   CF_Pos64 : constant CF_Pos := CF_Pos'
+     (CF_9600MHZ  => 9_600_000_000,
+      CF_9000MHZ  => 9_000_000_000,
+      CF_8400MHZ  => 8_400_000_000);
+
+   subtype DCO_Frequency is
+      Pos64 range 1 .. CF_Pos64 (CF_9600MHZ) + CF_Pos64 (CF_9600MHZ) / 100;
+
+   function DPLL_CFGR1_DCO_FRACTION (DCO_Freq : DCO_Frequency) return Word32
+   with
+      Pre => True
+   is
+   begin
+      return Shift_Left
+        (Word32 ((DCO_Freq * 2 ** 15) / 24_000_000) and 16#7fff#,
+         DPLL_CFGR1_DCO_FRACTION_SHIFT);
+   end DPLL_CFGR1_DCO_FRACTION;
+
+   function DPLL_CFGR1_DCO_INTEGER (DCO_Freq : DCO_Frequency) return Word32
+   with
+      Pre => True
+   is
+   begin
+      return Word32 (DCO_Freq / 24_000_000);
+   end DPLL_CFGR1_DCO_INTEGER;
+
+   function DPLL_CFGR2_PDIV (PDiv : PDiv_Range) return Word32 is
+   begin
+      return Shift_Left
+        ((case PDiv is
+            when      1 => 0,
+            when      2 => 1,
+            when      3 => 2,
+            when      7 => 4,
+            when others => 4),
+         DPLL_CFGR2_PDIV_SHIFT);
+   end DPLL_CFGR2_PDIV;
+
+   function DPLL_CFGR2_QDIV (QDiv : QDiv_Range) return Word32 is
+   begin
+      return Shift_Left (Word32 (QDiv), DPLL_CFGR2_QDIV_RATIO_SHIFT) or
+             (if QDiv /= 1 then DPLL_CFGR2_QDIV_MODE else 0);
+   end DPLL_CFGR2_QDIV;
+
+   function DPLL_CFGR2_KDIV (KDiv : KDiv_Range) return Word32 is
+   begin
+      return Shift_Left
+        ((case KDiv is
+            when      5 => 0,
+            when      2 => 1,
+            when      3 => 2,
+            when      1 => 3,
+            when others => 0),
+         DPLL_CFGR2_KDIV_SHIFT);
+   end DPLL_CFGR2_KDIV;
+
+   function DPLL_CFGR2_CENTRAL_FREQ (CF : Valid_Central_Freq) return Word32 is
+   begin
+      return (case CF is
+               when CF_9600MHZ   => DPLL_CFGR2_CENTRAL_FREQ_9600MHZ,
+               when CF_9000MHZ   => DPLL_CFGR2_CENTRAL_FREQ_9000MHZ,
+               when CF_8400MHZ   => DPLL_CFGR2_CENTRAL_FREQ_8400MHZ);
+   end DPLL_CFGR2_CENTRAL_FREQ;
+
+   ----------------------------------------------------------------------------
+
+   procedure Calculate_DPLL
+     (Dotclock       : in     Frequency_Type;
+      Central_Freq   :    out Central_Frequency;
+      DCO_Freq       :    out DCO_Frequency;
+      PDiv           :    out PDiv_Range;
+      QDiv           :    out QDiv_Range;
+      KDiv           :    out KDiv_Range)
+   with
+      Pre => True
+   is
+      Max_Pos_Deviation : constant := 1;
+      Max_Neg_Deviation : constant := 6;
+
+      subtype Div_Range is Pos64 range 1 .. 98;
+      subtype Candidate_Index is Positive range 1 .. 36;
+      type Candidate_Array is array (Candidate_Index) of Div_Range;
+      type Candidate_List is record
+         Divs  : Candidate_Array;
+         Count : Candidate_Index;
+      end record;
+      type Parity_Type is (Even, Odd);
+      type Candidates_Type is array (Parity_Type) of Candidate_List;
+
+      Candidates : constant Candidates_Type := Candidates_Type'
+        (Even  => Candidate_List'
+           (Divs  => Candidate_Array'
+              (4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 30, 32, 36, 40, 42, 44,
+               48, 52, 54, 56, 60, 64, 66, 68, 70, 72, 76, 78, 80, 84, 88, 90,
+               92, 96, 98),
+            Count => 36),
+         Odd   => Candidate_List'
+           (Divs  => Candidate_Array'(3, 5, 7, 9, 15, 21, 35, others => 1),
+            Count => 7));
+
+      Temp_Freq,
+      Allowed_Deviation : Pos64;
+      Deviation         : Int64;
+      Temp_Central      : DCO_Frequency;
+      Min_Deviation     : Int64 := Int64'Last;
+      Div               : Div_Range := Div_Range'Last;
+   begin
+      Central_Freq   := CF_INVALID;
+      DCO_Freq       := 1;
+      PDiv           := 1;
+      QDiv           := 1;
+      KDiv           := 1;
+
+      for Parity in Parity_Type loop
+         for CF in Valid_Central_Freq loop
+            Temp_Central := CF_Pos64 (CF);
+            for I in Candidate_Index range 1 .. Candidates (Parity).Count loop
+               Temp_Freq := Candidates (Parity).Divs (I) * 5 * Dotclock;
+               if Temp_Freq > Temp_Central then
+                  Deviation         := Temp_Freq - Temp_Central;
+                  Allowed_Deviation := (Max_Pos_Deviation * Temp_Central) / 100;
+               else
+                  Deviation         := Temp_Central - Temp_Freq;
+                  Allowed_Deviation := (Max_Neg_Deviation * Temp_Central) / 100;
+               end if;
+               if Deviation < Min_Deviation and
+                  Deviation < Allowed_Deviation
+               then
+                  Min_Deviation  := Deviation;
+                  Central_Freq   := CF;
+                  DCO_Freq       := Temp_Freq;
+                  Div            := Candidates (Parity).Divs (I);
+               end if;
+            end loop;
+         end loop;
+         exit when Central_Freq /= CF_INVALID;
+      end loop;
+
+      if Central_Freq /= CF_INVALID then
+         if Div mod 2 = 0 then
+            pragma Assert (Div /= 1);
+            pragma Assert (Div > 1);
+            Div := Div / 2;
+            if Div = 1 or Div = 3 or Div = 5 then
+               -- 2, 6 and 10
+               PDiv := 2;
+               QDiv := 1;
+               KDiv := Div;
+            elsif Div mod 2 = 0 then
+               -- divisible by 4
+               PDiv := 2;
+               QDiv := Div / 2;
+               KDiv := 2;
+            elsif Div mod 3 = 0 then
+               -- divisible by 6
+               PDiv := 3;
+               QDiv := Div / 3;
+               KDiv := 2;
+            elsif Div mod 7 = 0  then
+               -- divisible by 14
+               PDiv := 7;
+               QDiv := Div / 7;
+               KDiv := 2;
+            end if;
+         elsif Div = 7 or Div = 21 or Div = 35 then
+            -- 7, 21 and 35
+            PDiv := 7;
+            QDiv := 1;
+            KDiv := Div / 7;
+         elsif Div = 3 or Div = 9 or Div = 15 then
+            -- 3, 9 and 15
+            PDiv := 3;
+            QDiv := 1;
+            KDiv := Div / 3;
+         elsif Div = 5 then
+            -- 5
+            PDiv := 5;
+            QDiv := 1;
+            KDiv := 1;
+         end if;
+      end if;
+   end Calculate_DPLL;
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (PLL      : in     Configurable_DPLLs;
+      Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   is
+      Central_Freq   : Central_Frequency;
+      DCO_Freq       : DCO_Frequency;
+      PDiv           : PDiv_Range;
+      QDiv           : QDiv_Range;
+      KDiv           : KDiv_Range;
+   begin
+      if Port_Cfg.Display = DP then
+         Registers.Unset_And_Set_Mask
+           (Register    => Registers.DPLL_CTRL1,
+            Mask_Unset  => DPLL_CTRL1_DPLLx (HDMI_MODE, PLL) or
+                           DPLL_CTRL1_DPLLx (SSC, PLL) or
+                           DPLL_CTRL1_DPLLx (LINK_RATE_MASK, PLL),
+            Mask_Set    => DPLL_CTRL1_DPLLx (LINK_RATE
+                             (Port_Cfg.DP.Bandwidth), PLL) or
+                           DPLL_CTRL1_DPLLx (OVERRIDE, PLL));
+         Registers.Posting_Read (Registers.DPLL_CTRL1);
+         Success := True;
+      else
+         Calculate_DPLL
+           (Port_Cfg.Mode.Dotclock, Central_Freq, DCO_Freq, PDiv, QDiv, KDiv);
+         Success := Central_Freq /= CF_INVALID;
+         if Success then
+            Registers.Unset_And_Set_Mask
+              (Register    => Registers.DPLL_CTRL1,
+               Mask_Unset  => DPLL_CTRL1_DPLLx (SSC, PLL),
+               Mask_Set    => DPLL_CTRL1_DPLLx (HDMI_MODE, PLL) or
+                              DPLL_CTRL1_DPLLx (OVERRIDE, PLL));
+            Registers.Write
+              (Register    => DPLL_CFGR1 (PLL),
+               Value       => DPLL_CFGR1_FREQUENCY_ENABLE or
+                              DPLL_CFGR1_DCO_FRACTION (DCO_Freq) or
+                              DPLL_CFGR1_DCO_INTEGER (DCO_Freq));
+            Registers.Write
+              (Register    => DPLL_CFGR2 (PLL),
+               Value       => DPLL_CFGR2_QDIV (QDiv) or
+                              DPLL_CFGR2_KDIV (KDiv) or
+                              DPLL_CFGR2_PDIV (PDiv) or
+                              DPLL_CFGR2_CENTRAL_FREQ (Central_Freq));
+            Registers.Posting_Read (Registers.DPLL_CTRL1);
+            Registers.Posting_Read (DPLL_CFGR1 (PLL));
+            Registers.Posting_Read (DPLL_CFGR2 (PLL));
+         end if;
+      end if;
+
+      if Success then
+         Registers.Write
+           (Register => DPLL_CTL (PLL),
+            Value    => DPLL_CTL_PLL_ENABLE);
+         Registers.Wait_Set_Mask
+           (Register => Registers.DPLL_STATUS,
+            Mask     => DPLL_STATUS_DPLLx_LOCK (PLL));
+      end if;
+   end On;
+
+   procedure Off (PLL : Configurable_DPLLs) is
+   begin
+      Registers.Unset_Mask (DPLL_CTL (PLL), DPLL_CTL_PLL_ENABLE);
+   end Off;
+
+end HW.GFX.GMA.PLLs.DPLL;
diff --git a/common/skylake/hw-gfx-gma-plls-dpll.ads b/common/skylake/hw-gfx-gma-plls-dpll.ads
new file mode 100644
index 0000000..61bd55e
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-plls-dpll.ads
@@ -0,0 +1,27 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs.DPLL is
+
+   type Value_Array is array (Configurable_DPLLs) of Word32;
+   Register_Value : constant Value_Array := Value_Array'
+     (DPLL1 => 1, DPLL2 => 2, DPLL3 => 3);
+
+   procedure On
+     (PLL      : in     Configurable_DPLLs;
+      Port_Cfg : in     Port_Config;
+      Success  :    out Boolean);
+
+   procedure Off (PLL : Configurable_DPLLs);
+
+end HW.GFX.GMA.PLLs.DPLL;
diff --git a/common/skylake/hw-gfx-gma-plls-dpll_0.adb b/common/skylake/hw-gfx-gma-plls-dpll_0.adb
new file mode 100644
index 0000000..f981940
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-plls-dpll_0.adb
@@ -0,0 +1,48 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.PLLs.DPLL_0 is
+
+   DPLL_CTRL1_DPLL0_LINK_RATE_MASK     : constant := 7 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_2700MHZ  : constant := 0 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_1350MHZ  : constant := 1 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_810MHZ   : constant := 2 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_1620MHZ  : constant := 3 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_1080MHZ  : constant := 4 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_2160MHZ  : constant := 5 * 2 **  1;
+   DPLL_CTRL1_DPLL0_OVERRIDE           : constant := 1 * 2 **  0;
+
+   procedure Check_Link_Rate
+     (Link_Rate   : in     DP_Bandwidth;
+      Success     :    out Boolean)
+   is
+      DPLL_Ctrl1 : Word32;
+   begin
+      Registers.Read (Registers.DPLL_CTRL1, DPLL_Ctrl1);
+
+      case DPLL_Ctrl1 and DPLL_CTRL1_DPLL0_LINK_RATE_MASK is
+         when DPLL_CTRL1_DPLL0_LINK_RATE_2700MHZ =>
+            Success := Link_Rate = DP_Bandwidth_5_4;
+         when DPLL_CTRL1_DPLL0_LINK_RATE_1350MHZ =>
+            Success := Link_Rate = DP_Bandwidth_2_7;
+         when DPLL_CTRL1_DPLL0_LINK_RATE_810MHZ =>
+            Success := Link_Rate = DP_Bandwidth_1_62;
+         when others =>
+            Success := False;
+      end case;
+      Success := Success and (DPLL_Ctrl1 and DPLL_CTRL1_DPLL0_OVERRIDE) /= 0;
+   end Check_Link_Rate;
+
+end HW.GFX.GMA.PLLs.DPLL_0;
diff --git a/common/skylake/hw-gfx-gma-plls-dpll_0.ads b/common/skylake/hw-gfx-gma-plls-dpll_0.ads
new file mode 100644
index 0000000..a2fecfa
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-plls-dpll_0.ads
@@ -0,0 +1,22 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs.DPLL_0 is
+
+   Register_Value : constant Word32 := 0;
+
+   procedure Check_Link_Rate
+     (Link_Rate   : in     DP_Bandwidth;
+      Success     :    out Boolean);
+
+end HW.GFX.GMA.PLLs.DPLL_0;
diff --git a/common/skylake/hw-gfx-gma-plls.adb b/common/skylake/hw-gfx-gma-plls.adb
new file mode 100644
index 0000000..561f8ad
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-plls.adb
@@ -0,0 +1,151 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.PLLs.DPLL_0;
+with HW.GFX.GMA.PLLs.DPLL;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PLLs
+with
+   Refined_State => (State => PLLs)
+is
+
+   type Count_Range is new Natural range 0 .. 2;
+
+   type PLL_State is record
+      Use_Count   : Count_Range;
+      Used_For_DP : Boolean;
+      Link_Rate   : DP_Bandwidth;
+      Mode        : Mode_Type;
+   end record;
+
+   type PLL_State_Array is array (Configurable_DPLLs) of PLL_State;
+
+   PLLs : PLL_State_Array;
+
+   procedure Initialize
+   is
+   begin
+      PLLs :=
+        (Configurable_DPLLs =>
+           (Use_Count   => 0,
+            Used_For_DP => False,
+            Link_Rate   => DP_Bandwidth'First,
+            Mode        => Invalid_Mode));
+   end Initialize;
+
+   procedure Alloc_Configurable
+     (Port_Cfg : in     Port_Config;
+      PLL      :    out T;
+      Success  :    out Boolean)
+   with
+      Pre => True
+   is
+      function Config_Matches (PE : PLL_State) return Boolean
+      is
+      begin
+         return
+            PE.Used_For_DP = (Port_Cfg.Display = DP) and
+            ((PE.Used_For_DP and PE.Link_Rate = Port_Cfg.DP.Bandwidth) or
+             (not PE.Used_For_DP and PE.Mode = Port_Cfg.Mode));
+      end Config_Matches;
+   begin
+      -- try to find shareable PLL
+      for P in Configurable_DPLLs loop
+         Success := PLLs (P).Use_Count /= 0 and
+                     PLLs (P).Use_Count /= Count_Range'Last and
+                     Config_Matches (PLLs (P));
+         if Success then
+            PLL := P;
+            PLLs (PLL).Use_Count := PLLs (PLL).Use_Count + 1;
+            return;
+         end if;
+      end loop;
+
+      -- try to find free PLL
+      for P in Configurable_DPLLs loop
+         if PLLs (P).Use_Count = 0 then
+            PLL := P;
+            DPLL.On (PLL, Port_Cfg, Success);
+            if Success then
+               PLLs (PLL) :=
+                 (Use_Count   => 1,
+                  Used_For_DP => Port_Cfg.Display = DP,
+                  Link_Rate   => Port_Cfg.DP.Bandwidth,
+                  Mode        => Port_Cfg.Mode);
+            end if;
+            return;
+         end if;
+      end loop;
+
+      PLL := Invalid;
+   end Alloc_Configurable;
+
+   procedure Alloc
+     (Port_Cfg : in     Port_Config;
+      PLL      :    out T;
+      Success  :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Port = DIGI_A then
+         DPLL_0.Check_Link_Rate (Port_Cfg.DP.Bandwidth, Success);
+      else
+         Success := False;
+      end if;
+
+      if Success then
+         PLL := DPLL0;
+      else
+         Alloc_Configurable (Port_Cfg, PLL, Success);
+      end if;
+   end Alloc;
+
+   procedure Free (PLL : T)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if PLL in Configurable_DPLLs then
+         if PLLs (PLL).Use_Count /= 0 then
+            PLLs (PLL).Use_Count := PLLs (PLL).Use_Count - 1;
+            if PLLs (PLL).Use_Count = 0 then
+               DPLL.Off (PLL);
+            end if;
+         end if;
+      end if;
+   end Free;
+
+   procedure All_Off
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      for PLL in Configurable_DPLLs loop
+         DPLL.Off (PLL);
+      end loop;
+   end All_Off;
+
+   function Register_Value (PLL : T) return Word32
+   is
+   begin
+      return
+        (if    PLL = DPLL0                then DPLL_0.Register_Value
+         elsif PLL in Configurable_DPLLs  then DPLL.Register_Value (PLL)
+         else 0);
+   end Register_Value;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/skylake/hw-gfx-gma-plls.ads b/common/skylake/hw-gfx-gma-plls.ads
new file mode 100644
index 0000000..9407af0
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-plls.ads
@@ -0,0 +1,42 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs
+with
+   Abstract_State => (State with Part_Of => GMA.State)
+is
+
+   -- NOTE: Order of DPLLs is twisted, as DPLL2 (WRPLL1)
+   --       should be selected as last choice.
+
+   -- XXX: Types should be private (but that triggers a bug in SPARK GPL 2016)
+   type T is (Invalid_PLL, DPLL0, DPLL1, DPLL3, DPLL2);
+   subtype Configurable_DPLLs is T range DPLL1 .. DPLL2;
+   Invalid : constant T := Invalid_PLL;
+
+   procedure Initialize
+   with
+      Global => (Output => State);
+
+   procedure Alloc
+     (Port_Cfg : in     Port_Config;
+      PLL      :    out T;
+      Success  :    out Boolean);
+
+   procedure Free (PLL : T);
+
+   procedure All_Off;
+
+   function Register_Value (PLL : T) return Word32;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/skylake/hw-gfx-gma-power_and_clocks.ads b/common/skylake/hw-gfx-gma-power_and_clocks.ads
new file mode 100644
index 0000000..bf54989
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-power_and_clocks.ads
@@ -0,0 +1,17 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with HW.GFX.GMA.Power_And_Clocks_Skylake;
+
+private package HW.GFX.GMA.Power_And_Clocks
+   renames HW.GFX.GMA.Power_And_Clocks_Skylake;
diff --git a/common/skylake/hw-gfx-gma-power_and_clocks_skylake.adb b/common/skylake/hw-gfx-gma-power_and_clocks_skylake.adb
new file mode 100644
index 0000000..521ef8b
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-power_and_clocks_skylake.adb
@@ -0,0 +1,351 @@
+--
+-- Copyright (C) 2014-2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+with GNAT.Source_Info;
+
+with HW.Time;
+with HW.Debug;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.Power_And_Clocks_Haswell;
+
+use type HW.Word64;
+
+package body HW.GFX.GMA.Power_And_Clocks_Skylake is
+
+   type Power_Domain is (MISC_IO, PW1, PW2, DDI_AE, DDI_B, DDI_C, DDI_D);
+   subtype Power_Well is Power_Domain range PW1 .. PW2;
+   subtype Dynamic_Domain is Power_Domain range PW2 .. DDI_D;
+
+   NDE_RSTWRN_OPT_RST_PCH_Handshake_En : constant := 1 * 2 **  4;
+
+   FUSE_STATUS_DOWNLOAD_STATUS         : constant := 1 * 2 ** 31;
+   FUSE_STATUS_PG0_DIST_STATUS         : constant := 1 * 2 ** 27;
+
+   type Power_Domain_Values is array (Power_Domain) of Word32;
+   PWR_WELL_CTL_POWER_REQUEST : constant Power_Domain_Values :=
+     (MISC_IO  => 1 * 2 **  1,
+      DDI_AE   => 1 * 2 **  3,
+      DDI_B    => 1 * 2 **  5,
+      DDI_C    => 1 * 2 **  7,
+      DDI_D    => 1 * 2 **  9,
+      PW1      => 1 * 2 ** 29,
+      PW2      => 1 * 2 ** 31);
+   PWR_WELL_CTL_POWER_STATE : constant Power_Domain_Values :=
+     (MISC_IO  => 1 * 2 **  0,
+      DDI_AE   => 1 * 2 **  2,
+      DDI_B    => 1 * 2 **  4,
+      DDI_C    => 1 * 2 **  6,
+      DDI_D    => 1 * 2 **  8,
+      PW1      => 1 * 2 ** 28,
+      PW2      => 1 * 2 ** 30);
+
+   type Power_Well_Values is array (Power_Well) of Word32;
+   FUSE_STATUS_PGx_DIST_STATUS : constant Power_Well_Values :=
+     (PW1   => 1 * 2 ** 26,
+      PW2   => 1 * 2 ** 25);
+
+   DBUF_CTL_DBUF_POWER_REQUEST         : constant := 1 * 2 ** 31;
+   DBUF_CTL_DBUF_POWER_STATE           : constant := 1 * 2 ** 30;
+
+   ----------------------------------------------------------------------------
+
+   DPLL_CTRL1_DPLL0_LINK_RATE_MASK     : constant := 7 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_2700MHZ  : constant := 0 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_1350MHZ  : constant := 1 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_810MHZ   : constant := 2 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_1620MHZ  : constant := 3 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_1080MHZ  : constant := 4 * 2 **  1;
+   DPLL_CTRL1_DPLL0_LINK_RATE_2160MHZ  : constant := 5 * 2 **  1;
+   DPLL_CTRL1_DPLL0_OVERRIDE           : constant := 1 * 2 **  0;
+
+   LCPLL1_CTL_PLL_ENABLE               : constant := 1 * 2 ** 31;
+   LCPLL1_CTL_PLL_LOCK                 : constant := 1 * 2 ** 30;
+
+   ----------------------------------------------------------------------------
+
+   CDCLK_CTL_CD_FREQ_SELECT_MASK       : constant := 3 * 2 ** 26;
+   CDCLK_CTL_CD_FREQ_SELECT_450MHZ     : constant := 0 * 2 ** 26;
+   CDCLK_CTL_CD_FREQ_SELECT_540MHZ     : constant := 1 * 2 ** 26;
+   CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ   : constant := 2 * 2 ** 26;
+   CDCLK_CTL_CD_FREQ_SELECT_675MHZ     : constant := 3 * 2 ** 26;
+   CDCLK_CTL_CD_FREQ_DECIMAL_MASK      : constant :=     16#7ff#;
+
+   SKL_PCODE_CDCLK_CONTROL             : constant := 7;
+   SKL_CDCLK_PREPARE_FOR_CHANGE        : constant := 3;
+   SKL_CDCLK_READY_FOR_CHANGE          : constant := 1;
+
+   GT_MAILBOX_READY                    : constant := 1 * 2 ** 31;
+
+   function CDCLK_CTL_CD_FREQ_DECIMAL
+     (Freq        : Positive;
+      Plus_Half   : Boolean)
+      return Word32 is
+   begin
+      return Word32 (2 * (Freq - 1)) or (if Plus_Half then 1 else 0);
+   end CDCLK_CTL_CD_FREQ_DECIMAL;
+
+   ----------------------------------------------------------------------------
+
+   procedure GT_Mailbox_Write (MBox : Word32; Value : Word64) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
+      Registers.Write
+        (Registers.GT_MAILBOX_DATA, Word32 (Value and 16#ffff_ffff#));
+      Registers.Write
+        (Registers.GT_MAILBOX_DATA_1, Word32 (Shift_Right (Value, 32)));
+      Registers.Write (Registers.GT_MAILBOX, GT_MAILBOX_READY or MBox);
+
+      Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
+   end GT_Mailbox_Write;
+
+   ----------------------------------------------------------------------------
+
+   procedure PD_Off (PD : Power_Domain)
+   is
+      Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+      Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+      Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+      Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); --  Result for debugging only
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); --  Result for debugging only
+
+      if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+          PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0
+      then
+         Registers.Wait_Set_Mask
+           (Register => Registers.PWR_WELL_CTL_DRIVER,
+            Mask     => PWR_WELL_CTL_POWER_STATE (PD));
+      end if;
+
+      if (Ctl1 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
+         Registers.Unset_Mask
+           (Register => Registers.PWR_WELL_CTL_BIOS,
+            Mask     => PWR_WELL_CTL_POWER_REQUEST (PD));
+      end if;
+
+      if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
+         Registers.Unset_Mask
+           (Register => Registers.PWR_WELL_CTL_DRIVER,
+            Mask     => PWR_WELL_CTL_POWER_REQUEST (PD));
+      end if;
+   end PD_Off;
+
+   procedure PD_On (PD : Power_Domain)
+   with
+      Pre => True
+   is
+      Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+      Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+      Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+      Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); --  Result for debugging only
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); --  Result for debugging only
+
+      if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+          PWR_WELL_CTL_POWER_REQUEST (PD)) = 0
+      then
+         Registers.Wait_Unset_Mask
+           (Register => Registers.PWR_WELL_CTL_DRIVER,
+            Mask     => PWR_WELL_CTL_POWER_STATE (PD));
+      end if;
+
+      if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) = 0 then
+         Registers.Set_Mask
+           (Register => Registers.PWR_WELL_CTL_DRIVER,
+            Mask     => PWR_WELL_CTL_POWER_REQUEST (PD));
+         Registers.Wait_Set_Mask
+           (Register => Registers.PWR_WELL_CTL_DRIVER,
+            Mask     => PWR_WELL_CTL_POWER_STATE (PD));
+
+         if PD in Power_Well then
+            Registers.Wait_Set_Mask
+              (Register => Registers.FUSE_STATUS,
+               Mask     => FUSE_STATUS_PGx_DIST_STATUS (PD));
+         end if;
+      end if;
+   end PD_On;
+
+   function Need_PD (PD : Dynamic_Domain; Configs : Configs_Type) return Boolean
+   is
+   begin
+      return (case PD is
+         when DDI_AE    => Configs (Primary).Port = Internal or
+                           Configs (Secondary).Port = Internal or
+                           Configs (Tertiary).Port = Internal,
+         when DDI_B     => Configs (Primary).Port = Digital1 or
+                           Configs (Primary).Port = DP1 or
+                           Configs (Secondary).Port = Digital1 or
+                           Configs (Secondary).Port = DP1 or
+                           Configs (Tertiary).Port = Digital1 or
+                           Configs (Tertiary).Port = DP1,
+         when DDI_C     => Configs (Primary).Port = Digital2 or
+                           Configs (Primary).Port = DP2 or
+                           Configs (Secondary).Port = Digital2 or
+                           Configs (Secondary).Port = DP2 or
+                           Configs (Tertiary).Port = Digital2 or
+                           Configs (Tertiary).Port = DP2,
+         when DDI_D     => Configs (Primary).Port = Digital3 or
+                           Configs (Primary).Port = DP3 or
+                           Configs (Secondary).Port = Digital3 or
+                           Configs (Secondary).Port = DP3 or
+                           Configs (Tertiary).Port = Digital3 or
+                           Configs (Tertiary).Port = DP3,
+         when PW2       => (Configs (Primary).Port /= Disabled and
+                            Configs (Primary).Port /= Internal) or
+                           Configs (Secondary).Port /= Disabled or
+                           Configs (Tertiary).Port /= Disabled);
+   end Need_PD;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_All_Off is
+   begin
+      Power_And_Clocks_Haswell.PSR_Off;
+   end Pre_All_Off;
+
+   procedure Post_All_Off is
+   begin
+      for PD in reverse Dynamic_Domain loop
+         PD_Off (PD);
+      end loop;
+
+      Registers.Unset_Mask
+        (Register    => Registers.DBUF_CTL,
+         Mask        => DBUF_CTL_DBUF_POWER_REQUEST);
+      Registers.Wait_Unset_Mask
+        (Register    => Registers.DBUF_CTL,
+         Mask        => DBUF_CTL_DBUF_POWER_STATE);
+
+      Registers.Unset_Mask
+        (Register    => Registers.LCPLL1_CTL,
+         Mask        => LCPLL1_CTL_PLL_ENABLE);
+      Registers.Wait_Unset_Mask
+        (Register    => Registers.LCPLL1_CTL,
+         Mask        => LCPLL1_CTL_PLL_LOCK);
+
+      PD_Off (MISC_IO);
+      PD_Off (PW1);
+   end Post_All_Off;
+
+   procedure Initialize
+   is
+      CDClk_Change_Timeout : Time.T;
+      Timed_Out : Boolean;
+
+      MBox_Data0 : Word32;
+   begin
+      Registers.Set_Mask
+        (Register    => Registers.NDE_RSTWRN_OPT,
+         Mask        => NDE_RSTWRN_OPT_RST_PCH_Handshake_En);
+
+      Registers.Wait_Set_Mask
+        (Register    => Registers.FUSE_STATUS,
+         Mask        => FUSE_STATUS_PG0_DIST_STATUS);
+      PD_On (PW1);
+      PD_On (MISC_IO);
+
+      Registers.Write
+        (Register    => Registers.CDCLK_CTL,
+         Value       => CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ or
+                        CDCLK_CTL_CD_FREQ_DECIMAL (337, True));
+      -- TODO: Set to preferred eDP rate:
+      -- Registers.Unset_And_Set_Mask
+      --   (Register    => Registers.DPLL_CTRL1,
+      --    Unset_Mask  => DPLL_CTRL1_DPLL0_LINK_RATE_MASK,
+      --    Set_Mask    => DPLL_CTRL1_DPLL0_LINK_RATE_...);
+      Registers.Set_Mask
+        (Register    => Registers.LCPLL1_CTL,
+         Mask        => LCPLL1_CTL_PLL_ENABLE);
+      Registers.Wait_Set_Mask
+        (Register    => Registers.LCPLL1_CTL,
+         Mask        => LCPLL1_CTL_PLL_LOCK);
+
+      CDClk_Change_Timeout := Time.MS_From_Now (3);
+      loop
+         GT_Mailbox_Write
+           (MBox        => SKL_PCODE_CDCLK_CONTROL,
+            Value       => SKL_CDCLK_PREPARE_FOR_CHANGE);
+         Timed_Out := Time.Timed_Out (CDClk_Change_Timeout);
+         Registers.Read (Registers.GT_MAILBOX_DATA, MBox_Data0);
+         if (MBox_Data0 and SKL_CDCLK_READY_FOR_CHANGE) =
+            SKL_CDCLK_READY_FOR_CHANGE
+         then
+            Timed_Out := False;
+            exit;
+         end if;
+         exit when Timed_Out;
+      end loop;
+
+      if not Timed_Out then
+         GT_Mailbox_Write
+           (MBox        => SKL_PCODE_CDCLK_CONTROL,
+            Value       => 16#0000_0000#);   -- 0 - 337.5MHz
+                                             -- 1 - 450.0MHz
+                                             -- 2 - 540.0MHz
+                                             -- 3 - 675.0MHz
+         Registers.Set_Mask
+           (Register    => Registers.DBUF_CTL,
+            Mask        => DBUF_CTL_DBUF_POWER_REQUEST);
+         Registers.Wait_Set_Mask
+           (Register    => Registers.DBUF_CTL,
+            Mask        => DBUF_CTL_DBUF_POWER_STATE);
+      end if;
+   end Initialize;
+
+   procedure Power_Set_To (Configs : Configs_Type) is
+   begin
+      for PD in reverse Dynamic_Domain loop
+         if not Need_PD (PD, Configs) then
+            PD_Off (PD);
+         end if;
+      end loop;
+      for PD in Dynamic_Domain loop
+         if Need_PD (PD, Configs) then
+            PD_On (PD);
+         end if;
+      end loop;
+   end Power_Set_To;
+
+   procedure Power_Up (Old_Configs, New_Configs : Configs_Type) is
+   begin
+      for PD in Dynamic_Domain loop
+         if not Need_PD (PD, Old_Configs) and Need_PD (PD, New_Configs) then
+            PD_On (PD);
+         end if;
+      end loop;
+   end Power_Up;
+
+   procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type)
+   is
+   begin
+      for PD in reverse Dynamic_Domain loop
+         if (Need_PD (PD, Old_Configs) or Need_PD (PD, Tmp_Configs)) and
+            not Need_PD (PD, New_Configs)
+         then
+            PD_Off (PD);
+         end if;
+      end loop;
+   end Power_Down;
+
+end HW.GFX.GMA.Power_And_Clocks_Skylake;
diff --git a/common/skylake/hw-gfx-gma-power_and_clocks_skylake.ads b/common/skylake/hw-gfx-gma-power_and_clocks_skylake.ads
new file mode 100644
index 0000000..017ca65
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-power_and_clocks_skylake.ads
@@ -0,0 +1,25 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Power_And_Clocks_Skylake is
+
+   procedure Pre_All_Off;
+   procedure Post_All_Off;
+
+   procedure Initialize;
+
+   procedure Power_Set_To (Configs : Configs_Type);
+   procedure Power_Up (Old_Configs, New_Configs : Configs_Type);
+   procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type);
+
+end HW.GFX.GMA.Power_And_Clocks_Skylake;
diff --git a/common/skylake/hw-gfx-gma-spll.ads b/common/skylake/hw-gfx-gma-spll.ads
new file mode 100644
index 0000000..d9af288
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-spll.ads
@@ -0,0 +1,23 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.SPLL is
+
+   -- Just for a common interface with Haswell's DDI.
+   -- There is no SPLL (no FDI) on Skylake.
+
+   procedure On is null;
+
+   procedure Off is null;
+
+end HW.GFX.GMA.SPLL;
diff --git a/configs/broadwell b/configs/broadwell
new file mode 100644
index 0000000..51b9df7
--- /dev/null
+++ b/configs/broadwell
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU		= Broadwell
+CONFIG_GFX_GMA_CPU_VARIANT	= Normal
+CONFIG_GFX_GMA_INTERNAL_PORT	= DP
+CONFIG_GFX_GMA_DEFAULT_MMIO	= 16#e000_0000#
diff --git a/configs/broadwell_ult b/configs/broadwell_ult
new file mode 100644
index 0000000..f209293
--- /dev/null
+++ b/configs/broadwell_ult
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU		= Broadwell
+CONFIG_GFX_GMA_CPU_VARIANT	= ULT
+CONFIG_GFX_GMA_INTERNAL_PORT	= DP
+CONFIG_GFX_GMA_DEFAULT_MMIO	= 16#e000_0000#
diff --git a/configs/haswell b/configs/haswell
new file mode 100644
index 0000000..724d651
--- /dev/null
+++ b/configs/haswell
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU		= Haswell
+CONFIG_GFX_GMA_CPU_VARIANT	= Normal
+CONFIG_GFX_GMA_INTERNAL_PORT	= DP
+CONFIG_GFX_GMA_DEFAULT_MMIO	= 16#e000_0000#
diff --git a/configs/haswell_ult b/configs/haswell_ult
new file mode 100644
index 0000000..f3deac5
--- /dev/null
+++ b/configs/haswell_ult
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU		= Haswell
+CONFIG_GFX_GMA_CPU_VARIANT	= ULT
+CONFIG_GFX_GMA_INTERNAL_PORT	= DP
+CONFIG_GFX_GMA_DEFAULT_MMIO	= 16#e000_0000#
diff --git a/configs/ironlake b/configs/ironlake
new file mode 100644
index 0000000..7450d35
--- /dev/null
+++ b/configs/ironlake
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU		= Ironlake
+CONFIG_GFX_GMA_CPU_VARIANT	= Normal
+CONFIG_GFX_GMA_INTERNAL_PORT	= LVDS
+CONFIG_GFX_GMA_DEFAULT_MMIO	= 16#e000_0000#
diff --git a/configs/ivybridge_edp b/configs/ivybridge_edp
new file mode 100644
index 0000000..53907a0
--- /dev/null
+++ b/configs/ivybridge_edp
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU		= Ivybridge
+CONFIG_GFX_GMA_CPU_VARIANT	= Normal
+CONFIG_GFX_GMA_INTERNAL_PORT	= DP
+CONFIG_GFX_GMA_DEFAULT_MMIO	= 16#e000_0000#
diff --git a/configs/ivybridge_lvds b/configs/ivybridge_lvds
new file mode 100644
index 0000000..6a77aea
--- /dev/null
+++ b/configs/ivybridge_lvds
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU		= Ivybridge
+CONFIG_GFX_GMA_CPU_VARIANT	= Normal
+CONFIG_GFX_GMA_INTERNAL_PORT	= LVDS
+CONFIG_GFX_GMA_DEFAULT_MMIO	= 16#e000_0000#
diff --git a/configs/sandybridge b/configs/sandybridge
new file mode 100644
index 0000000..50a6c61
--- /dev/null
+++ b/configs/sandybridge
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU		= Sandybridge
+CONFIG_GFX_GMA_CPU_VARIANT	= Normal
+CONFIG_GFX_GMA_INTERNAL_PORT	= LVDS
+CONFIG_GFX_GMA_DEFAULT_MMIO	= 16#e000_0000#
diff --git a/configs/skylake b/configs/skylake
new file mode 100644
index 0000000..7867f79
--- /dev/null
+++ b/configs/skylake
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU		= Skylake
+CONFIG_GFX_GMA_CPU_VARIANT	= Normal
+CONFIG_GFX_GMA_INTERNAL_PORT	= DP
+CONFIG_GFX_GMA_DEFAULT_MMIO	= 16#e000_0000#
diff --git a/configs/skylake_ult b/configs/skylake_ult
new file mode 100644
index 0000000..8c386db
--- /dev/null
+++ b/configs/skylake_ult
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU		= Skylake
+CONFIG_GFX_GMA_CPU_VARIANT	= ULT
+CONFIG_GFX_GMA_INTERNAL_PORT	= DP
+CONFIG_GFX_GMA_DEFAULT_MMIO	= 16#e000_0000#