[coreboot-gerrit] Patch set updated for coreboot: WIP: SPARK: Add driver for Intel GMA initialization

Nico Huber (nico.h@gmx.de) gerrit at coreboot.org
Thu Jun 16 06:15:55 CEST 2016


Nico Huber (nico.h at gmx.de) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/11869

-gerrit

commit 655a4bc43836471307ae6d0a5d28e62fa8bd8f7c
Author: Nico Huber <nico.huber at secunet.com>
Date:   Sun Oct 11 15:27:40 2015 +0200

    WIP: SPARK: Add driver for Intel GMA initialization
    
    This is derived from an experimental branch, which was started to
    support Haswell. It supports many processors in the Core architecture
    line starting with the Ironlake graphics (found first in Nehalem). But
    I had to strip off the FDI (connection between processor and chipset)
    configuration during refactoring, so not everything is working again
    yet.
    
    Also, after the refactoring, I started to work with SPARK 2014. While
    the code is SPARK 2014 compliant, it's pretty much unannotated. Absence
    of runtime errors is automatically provable (with one exception),
    though.
    
    What currently should work: Virtually everything but VGA on Haswell and
    Broadwell. eDP on Ivy Bridge (maybe Sandy Bridge and Nehalem, too, but
    untested). Other connectors would need FDI configuration on these
    older processors.
    
    Integration is most WIP: Configuration is static and hardcoded currently
    (see HW.GFX.GMA.Config). There is one package with an interface to C
    (HW.GFX.GMA.Coreboot) that's hardcoded to bring up an eDP on Ivy Bridge.
    There's another interface in HW.GFX.GMA: Update_Outputs() which supports
    two different, runtime selectable outputs.
    
    Change-Id: I40e548768eeb23fc46e074caf143250199ae89bc
    Signed-off-by: Nico Huber <nico.huber at secunet.com>
---
 src/device/Kconfig                                 |    2 +-
 src/drivers/intel/gma/Kconfig                      |    5 +
 src/drivers/intel/gma/Makefile.inc                 |   77 +-
 .../intel/gma/hw-gfx-dp_aux_ch-provider.ads        |   19 +
 src/drivers/intel/gma/hw-gfx-dp_aux_ch.adb         |  423 ++++++++
 src/drivers/intel/gma/hw-gfx-dp_aux_ch.ads         |   44 +
 src/drivers/intel/gma/hw-gfx-dp_defs.ads           |   34 +
 src/drivers/intel/gma/hw-gfx-dp_info.adb           |  274 +++++
 src/drivers/intel/gma/hw-gfx-dp_info.ads           |  116 +++
 src/drivers/intel/gma/hw-gfx-edid.adb              |  119 +++
 src/drivers/intel/gma/hw-gfx-edid.ads              |   30 +
 .../intel/gma/hw-gfx-framebuffer_filler.adb        |   47 +
 .../intel/gma/hw-gfx-framebuffer_filler.ads        |   36 +
 src/drivers/intel/gma/hw-gfx-gma-config.ads        |  114 ++
 .../intel/gma/hw-gfx-gma-connectors-ddi.adb        |  472 +++++++++
 .../intel/gma/hw-gfx-gma-connectors-ddi.ads        |   94 ++
 .../intel/gma/hw-gfx-gma-connectors-digital.adb    |  102 ++
 .../intel/gma/hw-gfx-gma-connectors-digital.ads    |   49 +
 .../intel/gma/hw-gfx-gma-connectors-irl_edp.adb    |  246 +++++
 .../intel/gma/hw-gfx-gma-connectors-irl_edp.ads    |   58 ++
 .../intel/gma/hw-gfx-gma-connectors-lvds.adb       |   64 ++
 .../intel/gma/hw-gfx-gma-connectors-lvds.ads       |   41 +
 .../intel/gma/hw-gfx-gma-connectors-pch_analog.adb |   80 ++
 .../intel/gma/hw-gfx-gma-connectors-pch_analog.ads |   34 +
 .../intel/gma/hw-gfx-gma-connectors-training.adb   |  465 +++++++++
 .../intel/gma/hw-gfx-gma-connectors-training.ads   |   23 +
 src/drivers/intel/gma/hw-gfx-gma-connectors.adb    |  401 +++++++
 src/drivers/intel/gma/hw-gfx-gma-connectors.ads    |  104 ++
 src/drivers/intel/gma/hw-gfx-gma-coreboot.adb      |   81 ++
 src/drivers/intel/gma/hw-gfx-gma-coreboot.ads      |   52 +
 .../intel/gma/hw-gfx-gma-display_controller.adb    |  690 ++++++++++++
 .../intel/gma/hw-gfx-gma-display_controller.ads    |  278 +++++
 src/drivers/intel/gma/hw-gfx-gma-dp_aux_ch.adb     |  363 +++++++
 src/drivers/intel/gma/hw-gfx-gma-dp_aux_ch.ads     |   32 +
 src/drivers/intel/gma/hw-gfx-gma-i2c.adb           |  234 +++++
 src/drivers/intel/gma/hw-gfx-gma-i2c.ads           |   31 +
 src/drivers/intel/gma/hw-gfx-gma-panel.adb         |  343 ++++++
 src/drivers/intel/gma/hw-gfx-gma-panel.ads         |   58 ++
 src/drivers/intel/gma/hw-gfx-gma-pch-fdi.adb       |  139 +++
 src/drivers/intel/gma/hw-gfx-gma-pch-fdi.ads       |    9 +
 src/drivers/intel/gma/hw-gfx-gma-pch-sideband.adb  |  124 +++
 src/drivers/intel/gma/hw-gfx-gma-pch-sideband.ads  |   36 +
 .../intel/gma/hw-gfx-gma-pch-transcoder.adb        |  115 ++
 .../intel/gma/hw-gfx-gma-pch-transcoder.ads        |   10 +
 src/drivers/intel/gma/hw-gfx-gma-pch-vga.adb       |  172 +++
 src/drivers/intel/gma/hw-gfx-gma-pch-vga.ads       |   12 +
 src/drivers/intel/gma/hw-gfx-gma-pch.ads           |    5 +
 .../intel/gma/hw-gfx-gma-plls-haswell_lcpll.ads    |   32 +
 .../intel/gma/hw-gfx-gma-plls-haswell_spll.adb     |   30 +
 .../intel/gma/hw-gfx-gma-plls-haswell_spll.ads     |   16 +
 .../intel/gma/hw-gfx-gma-plls-haswell_wrpll.adb    |  353 +++++++
 .../intel/gma/hw-gfx-gma-plls-haswell_wrpll.ads    |   33 +
 .../intel/gma/hw-gfx-gma-plls-ironlake_dpll.adb    |  400 +++++++
 .../intel/gma/hw-gfx-gma-plls-ironlake_dpll.ads    |   36 +
 .../intel/gma/hw-gfx-gma-plls-skylake_dpll.adb     |  355 +++++++
 .../intel/gma/hw-gfx-gma-plls-skylake_dpll.ads     |   33 +
 .../intel/gma/hw-gfx-gma-plls-skylake_dpll0.adb    |   50 +
 .../intel/gma/hw-gfx-gma-plls-skylake_dpll0.ads    |   28 +
 src/drivers/intel/gma/hw-gfx-gma-plls.adb          |  284 +++++
 src/drivers/intel/gma/hw-gfx-gma-plls.ads          |   59 ++
 .../gma/hw-gfx-gma-power_and_clocks-broadwell.adb  |  114 ++
 .../gma/hw-gfx-gma-power_and_clocks-broadwell.ads  |   22 +
 .../gma/hw-gfx-gma-power_and_clocks-haswell.adb    |  178 ++++
 .../gma/hw-gfx-gma-power_and_clocks-haswell.ads    |   26 +
 .../gma/hw-gfx-gma-power_and_clocks-ironlake.adb   |   79 ++
 .../gma/hw-gfx-gma-power_and_clocks-ironlake.ads   |   20 +
 .../gma/hw-gfx-gma-power_and_clocks-skylake.adb    |  348 +++++++
 .../gma/hw-gfx-gma-power_and_clocks-skylake.ads    |   27 +
 .../intel/gma/hw-gfx-gma-power_and_clocks.adb      |   87 ++
 .../intel/gma/hw-gfx-gma-power_and_clocks.ads      |   27 +
 src/drivers/intel/gma/hw-gfx-gma-registers.adb     |  296 ++++++
 src/drivers/intel/gma/hw-gfx-gma-registers.ads     | 1097 ++++++++++++++++++++
 src/drivers/intel/gma/hw-gfx-gma.adb               |  440 ++++++++
 src/drivers/intel/gma/hw-gfx-gma.ads               |   99 ++
 src/drivers/intel/gma/hw-gfx-i2c.ads               |   23 +
 src/drivers/intel/gma/hw-gfx.ads                   |  161 +++
 76 files changed, 11108 insertions(+), 2 deletions(-)

diff --git a/src/device/Kconfig b/src/device/Kconfig
index 0113545..2cc73de 100644
--- a/src/device/Kconfig
+++ b/src/device/Kconfig
@@ -33,7 +33,7 @@ config MAINBOARD_HAS_NATIVE_VGA_INIT_TEXTMODECFG
 
 config MAINBOARD_DO_NATIVE_VGA_INIT
 	bool "Use native graphics initialization"
-	depends on MAINBOARD_HAS_NATIVE_VGA_INIT
+	depends on MAINBOARD_HAS_NATIVE_VGA_INIT || INTEL_GMA_SPARKINIT
 	default n
 	help
 	  Some mainboards, such as the Google Link, allow initializing the display
diff --git a/src/drivers/intel/gma/Kconfig b/src/drivers/intel/gma/Kconfig
index 1cfab3d..a209e6b 100644
--- a/src/drivers/intel/gma/Kconfig
+++ b/src/drivers/intel/gma/Kconfig
@@ -36,3 +36,8 @@ config INTEL_INT15
 config INTEL_GMA_ACPI
 	bool
 	default n
+
+config INTEL_GMA_SPARKINIT
+	bool "Use SPARK native graphics init"
+	default n
+	select WITH_SPARK_HW
diff --git a/src/drivers/intel/gma/Makefile.inc b/src/drivers/intel/gma/Makefile.inc
index 2ce83eb..76b94bb 100644
--- a/src/drivers/intel/gma/Makefile.inc
+++ b/src/drivers/intel/gma/Makefile.inc
@@ -19,4 +19,79 @@ ramstage-$(CONFIG_INTEL_EDID) += edid.c vbt.c
 ifeq ($(CONFIG_VGA_ROM_RUN),y)
 ramstage-$(CONFIG_INTEL_INT15) += int15.c
 endif
-ramstage-$(CONFIG_INTEL_GMA_ACPI) += acpi.c
\ No newline at end of file
+ramstage-$(CONFIG_INTEL_GMA_ACPI) += acpi.c
+
+ramstage-$(CONFIG_INTEL_GMA_SPARKINIT) += \
+	hw-gfx-dp_aux_ch-provider.ads \
+	hw-gfx-dp_aux_ch.adb \
+	hw-gfx-dp_aux_ch.ads \
+	hw-gfx-dp_defs.ads \
+	hw-gfx-dp_info.adb \
+	hw-gfx-dp_info.ads \
+	hw-gfx-edid.adb \
+	hw-gfx-edid.ads \
+	hw-gfx-framebuffer_filler.adb \
+	hw-gfx-framebuffer_filler.ads \
+	hw-gfx-gma-config.ads \
+	hw-gfx-gma-connectors-ddi.adb \
+	hw-gfx-gma-connectors-ddi.ads \
+	hw-gfx-gma-connectors-digital.adb \
+	hw-gfx-gma-connectors-digital.ads \
+	hw-gfx-gma-connectors-irl_edp.adb \
+	hw-gfx-gma-connectors-irl_edp.ads \
+	hw-gfx-gma-connectors-lvds.adb \
+	hw-gfx-gma-connectors-lvds.ads \
+	hw-gfx-gma-connectors-pch_analog.adb \
+	hw-gfx-gma-connectors-pch_analog.ads \
+	hw-gfx-gma-connectors-training.adb \
+	hw-gfx-gma-connectors-training.ads \
+	hw-gfx-gma-connectors.adb \
+	hw-gfx-gma-connectors.ads \
+	hw-gfx-gma-coreboot.adb \
+	hw-gfx-gma-coreboot.ads \
+	hw-gfx-gma-display_controller.adb \
+	hw-gfx-gma-display_controller.ads \
+	hw-gfx-gma-dp_aux_ch.adb \
+	hw-gfx-gma-dp_aux_ch.ads \
+	hw-gfx-gma-i2c.adb \
+	hw-gfx-gma-i2c.ads \
+	hw-gfx-gma-panel.adb \
+	hw-gfx-gma-panel.ads \
+	hw-gfx-gma-pch-fdi.adb \
+	hw-gfx-gma-pch-fdi.ads \
+	hw-gfx-gma-pch-sideband.adb \
+	hw-gfx-gma-pch-sideband.ads \
+	hw-gfx-gma-pch-transcoder.adb \
+	hw-gfx-gma-pch-transcoder.ads \
+	hw-gfx-gma-pch-vga.adb \
+	hw-gfx-gma-pch-vga.ads \
+	hw-gfx-gma-pch.ads \
+	hw-gfx-gma-plls-haswell_lcpll.ads \
+	hw-gfx-gma-plls-haswell_spll.adb \
+	hw-gfx-gma-plls-haswell_spll.ads \
+	hw-gfx-gma-plls-haswell_wrpll.adb \
+	hw-gfx-gma-plls-haswell_wrpll.ads \
+	hw-gfx-gma-plls-ironlake_dpll.adb \
+	hw-gfx-gma-plls-ironlake_dpll.ads \
+	hw-gfx-gma-plls-skylake_dpll.adb \
+	hw-gfx-gma-plls-skylake_dpll.ads \
+	hw-gfx-gma-plls-skylake_dpll0.adb \
+	hw-gfx-gma-plls-skylake_dpll0.ads \
+	hw-gfx-gma-plls.adb \
+	hw-gfx-gma-plls.ads \
+	hw-gfx-gma-power_and_clocks-broadwell.adb \
+	hw-gfx-gma-power_and_clocks-broadwell.ads \
+	hw-gfx-gma-power_and_clocks-haswell.adb \
+	hw-gfx-gma-power_and_clocks-haswell.ads \
+	hw-gfx-gma-power_and_clocks-ironlake.adb \
+	hw-gfx-gma-power_and_clocks-ironlake.ads \
+	hw-gfx-gma-power_and_clocks-skylake.adb \
+	hw-gfx-gma-power_and_clocks-skylake.ads \
+	hw-gfx-gma-power_and_clocks.adb \
+	hw-gfx-gma-power_and_clocks.ads \
+	hw-gfx-gma-registers.adb \
+	hw-gfx-gma-registers.ads \
+	hw-gfx-gma.adb \
+	hw-gfx-gma.ads \
+	hw-gfx-i2c.ads \
+	hw-gfx.ads \
diff --git a/src/drivers/intel/gma/hw-gfx-dp_aux_ch-provider.ads b/src/drivers/intel/gma/hw-gfx-dp_aux_ch-provider.ads
new file mode 100644
index 0000000..ff42e6a
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-dp_aux_ch-provider.ads
@@ -0,0 +1,19 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.DP_Aux_Ch;
+
+private package HW.GFX.DP_Aux_Ch.Provider renames HW.GFX.GMA.DP_Aux_Ch;
+
diff --git a/src/drivers/intel/gma/hw-gfx-dp_aux_ch.adb b/src/drivers/intel/gma/hw-gfx-dp_aux_ch.adb
new file mode 100644
index 0000000..c6252d1
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-dp_aux_ch.adb
@@ -0,0 +1,423 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+with HW.GFX.DP_Aux_Ch.Provider;
+
+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     Port_Private;
+      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
+      Pre => Provider.Port_Valid (Port)
+   is
+   begin
+      for Try in Positive range 1 .. 32 loop
+         Provider.Do_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     Port_Private;
+      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
+
+      if Provider.Port_Valid (Port) then
+         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;
+      else
+         Success := False;
+      end if;
+   end Aux_Read;
+
+   procedure Aux_Write
+     (Port     : in     Port_Private;
+      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
+      if Provider.Port_Valid (Port) then
+         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);
+      else
+         Success := False;
+      end if;
+   end Aux_Write;
+
+   ----------------------------------------------------------------------------
+
+   procedure I2C_Out_Packet
+     (Port              : in     Port_Private;
+      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)
+   with
+      Pre => Provider.Port_Valid (Port)
+   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     Port_Private;
+      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)
+   with
+      Pre => Provider.Port_Valid (Port)
+   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));
+
+         -- 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_In_Packet;
+
+   procedure I2C_Empty_Packet
+     (Port              : in     Port_Private;
+      Command           : in     DP_Defs.Aux_Message_Command;
+      Address           : in     DP_Defs.Aux_Message_Address;
+      Success           :    out Boolean)
+   with
+      Pre => Provider.Port_Valid (Port)
+   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     Port_Private;
+      Address  : in     I2C.Transfer_Address;
+      Length   : in     DP_Defs.Aux_Payload_Length;
+      Data     : in     DP_Defs.Aux_Payload;
+      Success  :    out Boolean)
+   with
+      Pre => Provider.Port_Valid (Port)
+   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);
+      end if;
+
+      pragma Warnings
+        (GNATprove, Off, "unused assignment to ""Ignored_Success""",
+         Reason => "Doesn't matter if the transfer itself succeeded");
+      -- 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 Do_I2C_Write;
+
+   procedure Do_I2C_Read
+     (Port     : in     Port_Private;
+      Address  : in     I2C.Transfer_Address;
+      Length   : in out I2C.Transfer_Length;
+      Data     : in out I2C.Transfer_Data;
+      Success  :    out Boolean)
+   with
+      Pre => Provider.Port_Valid (Port)
+   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;
+      Success := Success and then Tries < Max_Tries;
+      Length := Xfered;
+
+      pragma Warnings
+        (GNATprove, Off, "unused assignment to ""Ignored_Success""",
+         Reason => "Doesn't matter if the transfer itself succeeded");
+      -- 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 Do_I2C_Read;
+
+   ----------------------------------------------------------------------------
+
+   procedure I2C_Read
+     (Port     : in     Port_Private;
+      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#);
+
+      if Provider.Port_Valid (Port) then
+         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;
+      else
+         Success := False;
+      end if;
+   end I2C_Read;
+
+end HW.GFX.DP_Aux_Ch;
diff --git a/src/drivers/intel/gma/hw-gfx-dp_aux_ch.ads b/src/drivers/intel/gma/hw-gfx-dp_aux_ch.ads
new file mode 100644
index 0000000..1ace929
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-dp_aux_ch.ads
@@ -0,0 +1,44 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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 package HW.GFX.DP_Aux_Ch is
+
+   procedure Aux_Read
+     (Port     : in     Port_Private;
+      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     Port_Private;
+      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     Port_Private;
+      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/src/drivers/intel/gma/hw-gfx-dp_defs.ads b/src/drivers/intel/gma/hw-gfx-dp_defs.ads
new file mode 100644
index 0000000..c73cad0
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-dp_defs.ads
@@ -0,0 +1,34 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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/src/drivers/intel/gma/hw-gfx-dp_info.adb b/src/drivers/intel/gma/hw-gfx-dp_info.adb
new file mode 100644
index 0000000..54bf634
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-dp_info.adb
@@ -0,0 +1,274 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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 Ada.Unchecked_Conversion;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+with HW.GFX.DP_Defs;
+with HW.GFX.DP_Aux_Ch;
+
+use type HW.Word8;
+
+package body HW.GFX.DP_Info is
+
+   procedure Read_Caps
+     (Link     : in out DP_Link;
+      Port     : in     Port_Private;
+      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;
+      DP_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;
+      Conn_BPC    : in     BPC_Type;
+      Success     :    out Boolean)
+   with
+      Depends => ((Link, Success) => (Link, Mode, Conn_BPC))
+   is
+      function Link_Pixel_Per_Second
+        (Link_Rate : DP_Bandwidth)
+         return Positive 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 (Conn_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;
+      BPC         : in     BPC_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, BPC, 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 (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;
+      BPC         : in     BPC_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, BPC, 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);
+   end Next_Link_Setting;
+
+   ----------------------------------------------------------------------------
+
+   procedure Read_Link_Status
+     (Port     : in     Port_Private;
+      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;
+      DP_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/src/drivers/intel/gma/hw-gfx-dp_info.ads b/src/drivers/intel/gma/hw-gfx-dp_info.ads
new file mode 100644
index 0000000..55cbb95
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-dp_info.ads
@@ -0,0 +1,116 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.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     Port_Private;
+      Success  :    out Boolean);
+
+   procedure Preferred_Link_Setting
+     (Link        : in out DP_Link;
+      Mode        : in     Mode_Type;
+      BPC         : in     BPC_Type;
+      Success     :    out Boolean);
+
+   procedure Next_Link_Setting
+     (Link        : in out DP_Link;
+      Mode        : in     Mode_Type;
+      BPC         : in     BPC_Type;
+      Success     :    out Boolean);
+
+   ----------------------------------------------------------------------------
+
+   procedure Read_Link_Status
+     (Port     : in     Port_Private;
+      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/src/drivers/intel/gma/hw-gfx-edid.adb b/src/drivers/intel/gma/hw-gfx-edid.adb
new file mode 100644
index 0000000..3d1f330
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-edid.adb
@@ -0,0 +1,119 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+
+   ----------------------------------------------------------------------------
+
+   function Read_LE16 (Raw_EDID : Buffer; Offset : Buffer_Range) 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 Read_LE16 (Raw_EDID, 54) /= 16#0000#;
+   end Has_Preferred_Mode;
+
+   function Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Mode_Type
+   is
+      Base : constant := 54;
+      Mode : Mode_Type;
+   begin
+      Mode := Mode_Type'
+        (Dotclock             => Pos64 (Read_LE16 (Raw_EDID, Base)) * 10_000,
+
+         H_Visible            => Pos16 (Raw_EDID (Base +  2)) +
+                                 Pos16 (Shift_Left (Word16 (Raw_EDID (Base +  4) and 16#f0#), 4)),
+
+         H_Sync_Begin         => Pos16 (Raw_EDID (Base +  8)) +
+                                 Pos16 (Shift_Left (Word16 (Raw_EDID (Base + 11) and 16#c0#), 2)),
+
+         H_Sync_End           => Pos16 (Raw_EDID (Base +  9)) +
+                                 Pos16 (Shift_Left (Word16 (Raw_EDID (Base + 11) and 16#30#), 4)),
+
+         H_Total              => Pos16 (Raw_EDID (Base +  3)) +
+                                 Pos16 (Shift_Left (Word16 (Raw_EDID (Base +  4) and 16#0f#), 8)),
+
+         V_Visible            => Pos16 (Raw_EDID (Base +  5)) +
+                                 Pos16 (Shift_Left (Word16 (Raw_EDID (Base +  7) and 16#f0#), 4)),
+
+         V_Sync_Begin         => Pos16 (Shift_Right (Raw_EDID (Base + 10) and 16#f0#, 4)) +
+                                 Pos16 (Shift_Left (Word16 (Raw_EDID (Base + 11) and 16#0c#), 2)),
+
+         V_Sync_End           => Pos16 (Raw_EDID (Base + 10) and 16#0f#) +
+                                 Pos16 (Shift_Left (Word16 (Raw_EDID (Base + 11) and 16#03#), 4)),
+
+         V_Total              => Pos16 (Raw_EDID (Base +  6)) +
+                                 Pos16 (Shift_Left (Word16 (Raw_EDID (Base +  7) and 16#0f#), 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);
+
+      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/src/drivers/intel/gma/hw-gfx-edid.ads b/src/drivers/intel/gma/hw-gfx-edid.ads
new file mode 100644
index 0000000..e5cf2d7
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-edid.ads
@@ -0,0 +1,30 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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
+
+   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;
+
+   function Has_Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Boolean
+   with
+      Pre => Valid (Raw_EDID);
+   function Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Mode_Type
+   with
+      Pre => Has_Preferred_Mode (Raw_EDID);
+
+end HW.GFX.EDID;
diff --git a/src/drivers/intel/gma/hw-gfx-framebuffer_filler.adb b/src/drivers/intel/gma/hw-gfx-framebuffer_filler.adb
new file mode 100644
index 0000000..066dc88
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-framebuffer_filler.adb
@@ -0,0 +1,47 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.MMIO_Range;
+pragma Elaborate_All (HW.MMIO_Range);
+
+package body HW.GFX.Framebuffer_Filler
+with
+   Refined_State =>
+     (Address_State     => FB.Base_Address,
+      Framebuffer_State => FB.State)
+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 (Word32, FB_Index, FB_Range);
+
+   procedure Fill (Linear_FB : System.Address; 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/src/drivers/intel/gma/hw-gfx-framebuffer_filler.ads b/src/drivers/intel/gma/hw-gfx-framebuffer_filler.ads
new file mode 100644
index 0000000..bcc12be
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-framebuffer_filler.ads
@@ -0,0 +1,36 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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 System;
+
+with HW;
+
+use type HW.Int32;
+
+package HW.GFX.Framebuffer_Filler
+with
+   Abstract_State =>
+    (Address_State, (Framebuffer_State with External))
+is
+
+   procedure Fill (Linear_FB : System.Address; Framebuffer : Framebuffer_Type)
+   with
+      Global =>
+        (Output => Address_State,
+         In_Out => Framebuffer_State),
+      Pre =>
+         Framebuffer.Width <= Framebuffer.Stride;
+
+end HW.GFX.Framebuffer_Filler;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-config.ads b/src/drivers/intel/gma/hw-gfx-gma-config.ads
new file mode 100644
index 0000000..46bc8b8
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-config.ads
@@ -0,0 +1,114 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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 is
+
+   CPU : constant CPU_Type := Skylake;
+
+   CPU_Var : constant CPU_Variant := ULT;
+
+   Internal_Port : constant Internal_Type := Int_EDP;
+
+   EDP_Low_Voltage_Swing : constant Boolean := False;
+
+   One_Shot_Defaults : constant Config_Type := Config_Type'
+     (Port        => Internal,
+      Framebuffer => Framebuffer_Type'
+        (Width    => 1920,
+         Height   => 1080,
+         BPC      =>    8,
+         Stride   => 2048,
+         Offset   => 0),
+      Mode        => Mode_Type'
+        (Dotclock             => 152_840_000,
+         H_Visible            => 1920,
+         H_Sync_Begin         => 2010,
+         H_Sync_End           => 2070,
+         H_Total              => 2250,
+         V_Visible            => 1080,
+         V_Sync_Begin         => 1086,
+         V_Sync_End           => 1095,
+         V_Total              => 1132,
+         H_Sync_Active_High   => True,
+         V_Sync_Active_High   => False),
+      DP          => HW.GFX.Default_DP);
+
+   Default_MMIO_Base : constant := 16#e000_0000#;
+
+   ----------------------------------------------------------------------------
+
+   Is_Ironlake             : constant Boolean := CPU = Ironlake;
+
+   Is_Haswell              : constant Boolean := CPU = Haswell;
+   Pre_Haswell             : constant Boolean := CPU < Haswell;
+   Post_Haswell            : constant Boolean := CPU > Haswell;
+
+   Is_Broadwell            : constant Boolean := CPU = Broadwell;
+
+   Is_Skylake              : constant Boolean := CPU = Skylake;
+   Pre_Skylake             : constant Boolean := CPU < Skylake;
+
+   Disable_Trickle_Feed    : constant Boolean := not Is_Haswell and
+                                                 not Is_Broadwell;
+
+   Has_EDP_Pipe            : constant Boolean := not Pre_Haswell;
+
+   Has_Pipe_DDI_Func       : constant Boolean := not Pre_Haswell;
+
+   Has_Trans_Clk_Sel       : constant Boolean := not Pre_Haswell;
+
+   Has_Pipeconf_Misc       : constant Boolean := Post_Haswell;
+
+   Has_PP_Write_Protection : constant Boolean := Pre_Haswell;
+
+   Has_PP_Port_Select      : constant Boolean := Pre_Haswell;
+
+   Use_PP_VDD_Override     : constant Boolean := Pre_Haswell;
+
+   Normal_CPU              : constant Boolean := CPU_Var = Normal;
+   ULT_CPU                 : constant Boolean := CPU_Var = ULT;
+
+   Internal_Is_EDP         : constant Boolean := Internal_Port = Int_EDP;
+   Internal_Is_LVDS_Dual   : constant Boolean := Internal_Port = LVDS_Dual;
+
+   VGA_Has_Sync_Disable    : constant Boolean := Pre_Haswell;
+
+   VGA_Through_FDI         : constant Boolean := Is_Ironlake or Is_Haswell or
+                                                 Is_Broadwell; -- and non-LP PCH
+
+   Use_SPLL_For_FDI        : constant Boolean := not Pre_Haswell;
+
+   Has_Haswell_PLLs        : constant Boolean := Is_Haswell or
+                                                 Is_Broadwell;
+
+   Has_Skylake_PLLs        : constant Boolean := Is_Skylake;
+
+   End_EDP_Training_Late   : constant Boolean := Is_Haswell or
+                                                 Is_Broadwell;
+
+   Has_Per_DDI_Clock_Sel   : constant Boolean := Is_Haswell or
+                                                 Is_Broadwell;
+
+   Has_Plane_Control       : constant Boolean := Is_Skylake;
+
+   Has_PCH_Aux_Channels    : constant Boolean := Pre_Skylake;
+
+   Need_DP_Aux_Mutex       : constant Boolean := False; -- Skylake & (PSR | GTC)
+
+   Ungate_GMBUS_Unit_Level : constant Boolean := Is_Skylake;
+
+   Fold_39Bit_GTT_PTE      : constant Boolean := not Post_Haswell;
+
+end HW.GFX.GMA.Config;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors-ddi.adb b/src/drivers/intel/gma/hw-gfx-gma-connectors-ddi.adb
new file mode 100644
index 0000000..52bc4e0
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors-ddi.adb
@@ -0,0 +1,472 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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_Info;
+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.Debug;
+with GNAT.Source_Info;
+
+use type HW.GFX.DP_Info.Training_Pattern;
+
+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 (Connectors.Digital_Port) of DDI_Registers;
+
+   DDI_Regs : constant DDI_Registers_Array := DDI_Registers_Array'
+     (Connectors.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),
+      Connectors.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),
+      Connectors.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),
+      Connectors.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),
+      Connectors.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;
+
+   ----------------------------------------------------------------------------
+
+   procedure Set_TP_CTL
+     (Connector   : Connector_Type;
+      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 (Connector.Inst).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
+     (Connector   : Connector_Type;
+      Link        : DP_Link;
+      Pattern     : DP_Info.Training_Pattern)
+   is
+   begin
+      if Pattern < DP_Info.TP_Idle then
+         Set_TP_CTL (Connector, Link, Pattern);
+      else
+         -- send at least 5 idle patterns
+         Set_TP_CTL (Connector, Link, DP_Info.TP_Idle);
+
+         -- switch to normal frame delivery
+         if Config.End_EDP_Training_Late and Connector.Inst = 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
+            Registers.Wait_Set_Mask
+              (Register    => DDI_Regs (Connector.Inst).DP_TP_STATUS,
+               Mask        => DP_TP_STATUS_MIN_IDLES_SENT);
+            Set_TP_CTL (Connector, Link, DP_Info.TP_None);
+         end if;
+      end if;
+   end Set_Training_Pattern;
+
+   procedure Set_Signal_Levels
+     (Connector   : Connector_Type;
+      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 (Connector.Inst).BUF_CTL,
+         Mask     => DDI_BUF_CTL_BUFFER_ENABLE,
+         Result   => Was_Enabled);
+
+      -- enable DDI buffer
+      Registers.Unset_And_Set_Mask
+        (Register    => DDI_Regs (Connector.Inst).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 (Connector.Inst).BUF_CTL);
+
+      if not Was_Enabled then
+         Time.U_Delay (600);  -- wait >= 518us (intel spec)
+      end if;
+   end Set_Signal_Levels;
+
+
+   ----------------------------------------------------------------------------
+
+   procedure Train_FDI (PLL_Hint : in Word32; Success : out Boolean) is
+   begin
+      PCH.FDI.Pre_Train (PCH.FDI_A);
+
+      -- direct configured PLL output to this port
+      Registers.Write
+        (Register => DDI_Regs (DIGI_E).PORT_CLK_SEL,
+         Value    => PLL_Hint);
+
+      -- 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_2_LANES);
+         Registers.Posting_Read (DDI_Regs (DIGI_E).BUF_CTL);
+         Time.U_Delay (600);  -- wait >= 518us (intel spec)
+
+         PCH.FDI.Try_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);
+
+         PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Clock_Off);
+      end if;
+   end Train_FDI;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_On
+     (Connector   : in     Connectors.Connector_Type;
+      PLL_Hint    : in     Word32;
+      Success     :    out Boolean) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Connector.Sub = Connectors.VGA then
+         Train_FDI (PLL_Hint, Success);
+      else
+         -- direct configured PLL output to this port
+         if Config.Has_Per_DDI_Clock_Sel then
+            Registers.Write
+              (Register    => DDI_Regs (Connector.Inst).PORT_CLK_SEL,
+               Value       => PLL_Hint);
+         else
+            Registers.Unset_And_Set_Mask
+              (Register    => Registers.DPLL_CTRL2,
+               Mask_Unset  => DPLL_CTRL2_DDIx_CLOCK_OFF (Connector.Inst) or
+                              DPLL_CTRL2_DDIx_SELECT_MASK (Connector.Inst),
+               Mask_Set    => Shift_Left
+                                (PLL_Hint,
+                                 DPLL_CTRL2_DDIx_SELECT_SHIFT (Connector.Inst))
+                              or
+                              DPLL_CTRL2_DDIx_SELECT_OVERRIDE (Connector.Inst));
+         end if;
+         Success := True;
+      end if;
+   end Pre_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Post_On
+     (Connector   : Connectors.Connector_Type;
+      Mode        : Mode_Type) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      case Connector.Sub is
+         when Connectors.EDP =>
+            if Config.End_EDP_Training_Late then
+               Registers.Unset_And_Set_Mask
+                 (Register    => DDI_Regs (Connector.Inst).DP_TP_CTL,
+                  Mask_Unset  => DP_TP_CTL_LINK_TRAIN_MASK,
+                  Mask_Set    => DP_TP_CTL_LINK_TRAIN_NORMAL);
+            end if;
+         when Connectors.HDMI =>
+            Registers.Unset_And_Set_Mask
+              (Register    => DDI_Regs (Connector.Inst).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 Connectors.VGA =>
+            PCH.VGA.Clock_On (Mode);
+            PCH.Transcoder.On (PCH.FDI_A, Mode);
+            PCH.VGA.On
+              (Transcoder_No     => 0,
+               HSync_Active_High => Mode.H_Sync_Active_High,
+               VSync_Active_High => Mode.V_Sync_Active_High);
+         when others =>
+            null;
+      end case;
+   end Post_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off (Connector : Connectors.Connector_Type)
+   is
+      Enabled : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Connector.Sub = Connectors.VGA 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;
+
+      Registers.Is_Set_Mask
+        (Register => DDI_Regs (Connector.Inst).BUF_CTL,
+         Mask     => DDI_BUF_CTL_BUFFER_ENABLE,
+         Result   => Enabled);
+
+      if Enabled then
+         Registers.Unset_Mask
+           (Register => DDI_Regs (Connector.Inst).BUF_CTL,
+            Mask     => DDI_BUF_CTL_BUFFER_ENABLE);
+      end if;
+
+      Registers.Unset_Mask
+        (Register => DDI_Regs (Connector.Inst).DP_TP_CTL,
+         Mask     => DP_TP_CTL_TRANSPORT_ENABLE);
+
+      if Enabled then
+         Registers.Wait_Set_Mask
+           (Register => DDI_Regs (Connector.Inst).BUF_CTL,
+            Mask     => DDI_BUF_CTL_IDLE_STATUS);
+      end if;
+
+      if Config.Has_Per_DDI_Clock_Sel then
+         Registers.Write
+           (Register => DDI_Regs (Connector.Inst).PORT_CLK_SEL,
+            Value    => PORT_CLK_SEL_NONE);
+      else
+         Registers.Set_Mask
+           (Register => Registers.DPLL_CTRL2,
+            Mask     => DPLL_CTRL2_DDIx_CLOCK_OFF (Connector.Inst));
+      end if;
+
+      if Connector.Sub = Connectors.VGA then
+         PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Clock_Off);
+      end if;
+   end Off;
+
+end HW.GFX.GMA.Connectors.DDI;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors-ddi.ads b/src/drivers/intel/gma/hw-gfx-gma-connectors-ddi.ads
new file mode 100644
index 0000000..d717f1a
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors-ddi.ads
@@ -0,0 +1,94 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+with HW.GFX.DP_Info;
+
+private package HW.GFX.GMA.Connectors.DDI
+is
+   type BPC_By_Sub_Type is array (Connectors.Sub_Type) of HW.GFX.BPC_Type;
+   DDI_BPC : constant BPC_By_Sub_Type := BPC_By_Sub_Type'
+     (EDP => 6, others => 8);
+
+   procedure Pre_On
+     (Connector   : in     Connectors.Connector_Type;
+      PLL_Hint    : in     Word32;
+      Success     :    out Boolean);
+
+   procedure Post_On
+     (Connector   : Connectors.Connector_Type;
+      Mode        : Mode_Type);
+
+   procedure Off (Connector : Connectors.Connector_Type);
+
+   procedure Set_Training_Pattern
+     (Connector   : Connector_Type;
+      Link        : DP_Link;
+      Pattern     : DP_Info.Training_Pattern);
+
+   procedure Set_Signal_Levels
+     (Connector   : Connector_Type;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set);
+
+
+   Inst_VGA,
+   HDMI_B, HDMI_C, HDMI_D,
+   DP_B, DP_C, DP_D,
+   Inst_EDP                 : constant Connectors.Connector_Type;
+
+private
+
+   Inst_VGA : constant Connectors.Connector_Type := Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_DDI,
+      Sub   => Connectors.VGA,
+      Inst  => Connectors.DIGI_E);
+
+   HDMI_B : constant Connectors.Connector_Type := Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_DDI,
+      Sub   => Connectors.HDMI,
+      Inst  => Connectors.DIGI_B);
+
+   HDMI_C : constant Connectors.Connector_Type := Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_DDI,
+      Sub   => Connectors.HDMI,
+      Inst  => Connectors.DIGI_C);
+
+   HDMI_D : constant Connectors.Connector_Type := Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_DDI,
+      Sub   => Connectors.HDMI,
+      Inst  => Connectors.DIGI_D);
+
+   DP_B : constant Connectors.Connector_Type := Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_DDI,
+      Sub   => Connectors.DP,
+      Inst  => Connectors.DIGI_B);
+
+   DP_C : constant Connectors.Connector_Type := Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_DDI,
+      Sub   => Connectors.DP,
+      Inst  => Connectors.DIGI_C);
+
+   DP_D : constant Connectors.Connector_Type := Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_DDI,
+      Sub   => Connectors.DP,
+      Inst  => Connectors.DIGI_D);
+
+   Inst_EDP : constant Connectors.Connector_Type := Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_DDI,
+      Sub   => Connectors.EDP,
+      Inst  => Connectors.DIGI_A);
+
+end HW.GFX.GMA.Connectors.DDI;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors-digital.adb b/src/drivers/intel/gma/hw-gfx-gma-connectors-digital.adb
new file mode 100644
index 0000000..eda0792
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors-digital.adb
@@ -0,0 +1,102 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Debug;
+with GNAT.Source_Info;
+
+use type HW.Word64;
+
+package body HW.GFX.GMA.Connectors.Digital is
+
+   PCH_HDMI_ENABLE               : constant := 1 * 2 ** 31;
+   PCH_HDMI_SOURCE_SELECT_MASK   : constant := 3 * 2 ** 29;
+   PCH_HDMI_SOURCE_SELECT_SHIFT  : constant :=     2 ** 29;
+   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_HDMI_ENABLE or
+      PCH_HDMI_SOURCE_SELECT_MASK or
+      PCH_HDMI_COLOR_FORMAT_MASK or
+      PCH_HDMI_SDVO_ENCODING_MASK or
+      PCH_HDMI_HSYNC_ACTIVE_HIGH or
+      PCH_HDMI_VSYNC_ACTIVE_HIGH;
+
+   subtype PCH_HDMI_Port is Connectors.Digital_Port
+      range Connectors.DIGI_B .. Connectors.DIGI_D;
+   type PCH_HDMI_Array is array (PCH_HDMI_Port) of Registers.Registers_Index;
+   PCH_HDMI : constant PCH_HDMI_Array := PCH_HDMI_Array'
+     (Connectors.DIGI_B => Registers.PCH_HDMIB,
+      Connectors.DIGI_C => Registers.PCH_HDMIC,
+      Connectors.DIGI_D => Registers.PCH_HDMID);
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (Connector         : Connectors.Connector_Type;
+      Transcoder_No     : Word32;
+      HSync_Active_High : Boolean;
+      VSync_Active_High : Boolean)
+   is
+      VSync_Polarity    : Word32 := 0;
+      HSync_Polarity    : Word32 := 0;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Connector.Inst in PCH_HDMI_Port then
+         if HSync_Active_High then
+            HSync_Polarity := PCH_HDMI_HSYNC_ACTIVE_HIGH;
+         end if;
+
+         if VSync_Active_High then
+            VSync_Polarity := PCH_HDMI_VSYNC_ACTIVE_HIGH;
+         end if;
+
+         -- registers are just sufficient for setup with DVI adaptor
+
+         Registers.Unset_And_Set_Mask
+            (Register   => PCH_HDMI (Connector.Inst),
+             Mask_Unset => PCH_HDMI_MASK,
+             Mask_Set   => PCH_HDMI_ENABLE or
+                           Transcoder_No * PCH_HDMI_SOURCE_SELECT_SHIFT or
+                           PCH_HDMI_SDVO_ENCODING_HDMI or
+                           HSync_Polarity or
+                           VSync_Polarity);
+      end if;
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off (Connector : Connectors.Connector_Type) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Connector.Inst in PCH_HDMI_Port then
+         Registers.Unset_And_Set_Mask
+            (Register   => PCH_HDMI (Connector.Inst),
+             Mask_Unset => PCH_HDMI_MASK,
+             Mask_Set   => PCH_HDMI_HSYNC_ACTIVE_HIGH or
+                           PCH_HDMI_VSYNC_ACTIVE_HIGH);
+      end if;
+   end Off;
+
+end HW.GFX.GMA.Connectors.Digital;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors-digital.ads b/src/drivers/intel/gma/hw-gfx-gma-connectors-digital.ads
new file mode 100644
index 0000000..c892612
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors-digital.ads
@@ -0,0 +1,49 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Digital
+is
+   procedure On
+      (Connector         : Connectors.Connector_Type;
+       Transcoder_No     : Word32;
+       HSync_Active_High : Boolean;
+       VSync_Active_High : Boolean);
+
+   procedure Off (Connector : Connectors.Connector_Type);
+
+   HDMI_B : constant Connectors.Connector_Type;
+   HDMI_C : constant Connectors.Connector_Type;
+   HDMI_D : constant Connectors.Connector_Type;
+
+private
+
+   HDMI_B : constant Connectors.Connector_Type := Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_Digital,
+      Sub   => Connectors.HDMI,
+      Inst  => Connectors.DIGI_B);
+
+   HDMI_C : constant Connectors.Connector_Type := Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_Digital,
+      Sub   => Connectors.HDMI,
+      Inst  => Connectors.DIGI_C);
+
+   HDMI_D : constant Connectors.Connector_Type := Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_Digital,
+      Sub   => Connectors.HDMI,
+      Inst  => Connectors.DIGI_D);
+
+end HW.GFX.GMA.Connectors.Digital;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors-irl_edp.adb b/src/drivers/intel/gma/hw-gfx-gma-connectors-irl_edp.adb
new file mode 100644
index 0000000..a696e52
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors-irl_edp.adb
@@ -0,0 +1,246 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.DP_Info;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+use type HW.GFX.DP_Info.Training_Pattern;
+
+package body HW.GFX.GMA.Connectors.IRL_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;
+
+   type DP_CTL_VSWING_EMPH_SELECT_T is
+     (DP_CTL_VSWING_0_EMPH_0,
+      DP_CTL_VSWING_0_EMPH_1,
+      DP_CTL_VSWING_0_EMPH_2,
+      DP_CTL_VSWING_1_EMPH_0,
+      DP_CTL_VSWING_1_EMPH_1,
+      DP_CTL_VSWING_2_EMPH_0,
+      DP_CTL_VSWING_2_EMPH_1);
+   type DP_CTL_VSWING_EMPH_SELECT_Array is
+      array (DP_CTL_VSWING_EMPH_SELECT_T) of Word32;
+   DP_CTL_VSWING_EMPH_SELECT : constant DP_CTL_VSWING_EMPH_SELECT_Array :=
+      DP_CTL_VSWING_EMPH_SELECT_Array'
+     (DP_CTL_VSWING_0_EMPH_0  => 1 * 2 ** 27 + 1 * 2 ** 24 + 0 * 2 ** 22,
+      DP_CTL_VSWING_0_EMPH_1  => 1 * 2 ** 27 + 2 * 2 ** 24 + 2 * 2 ** 22,
+      DP_CTL_VSWING_0_EMPH_2  => 1 * 2 ** 27 + 3 * 2 ** 24 + 3 * 2 ** 22,
+      DP_CTL_VSWING_1_EMPH_0  => 1 * 2 ** 27 + 4 * 2 ** 24 + 0 * 2 ** 22,
+      DP_CTL_VSWING_1_EMPH_1  => 1 * 2 ** 27 + 5 * 2 ** 24 + 2 * 2 ** 22,
+      DP_CTL_VSWING_2_EMPH_0  => 1 * 2 ** 27 + 6 * 2 ** 24 + 0 * 2 ** 22,
+      DP_CTL_VSWING_2_EMPH_1  => 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
+      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;
+
+   procedure Set_Training_Pattern
+     (Connector   : Connector_Type;
+      Link        : DP_Link;
+      Pattern     : DP_Info.Training_Pattern)
+   is
+   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
+     (Connector   : Connector_Type;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set)
+   is
+      -- TODO: Removing unneeded indirection through _T
+      VSwing_Emph : DP_CTL_VSWING_EMPH_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  => VSwing_Emph := DP_CTL_VSWING_0_EMPH_0;
+               when DP_Info.Emph_Level_1  => VSwing_Emph := DP_CTL_VSWING_0_EMPH_1;
+               when DP_Info.Emph_Level_2  => VSwing_Emph := DP_CTL_VSWING_0_EMPH_2;
+               when others                => VSwing_Emph := DP_CTL_VSWING_0_EMPH_0;
+            end case;
+         when DP_Info.VS_Level_1 =>
+            case Train_Set.Pre_Emph is
+               when DP_Info.Emph_Level_0  => VSwing_Emph := DP_CTL_VSWING_1_EMPH_0;
+               when DP_Info.Emph_Level_1  => VSwing_Emph := DP_CTL_VSWING_1_EMPH_1;
+               when others                => VSwing_Emph := DP_CTL_VSWING_1_EMPH_0;
+            end case;
+         when DP_Info.VS_Level_2 =>
+            case Train_Set.Pre_Emph is
+               when DP_Info.Emph_Level_0  => VSwing_Emph := DP_CTL_VSWING_2_EMPH_0;
+               when DP_Info.Emph_Level_1  => VSwing_Emph := DP_CTL_VSWING_2_EMPH_1;
+               when others                => VSwing_Emph := DP_CTL_VSWING_2_EMPH_0;
+            end case;
+         when others                      => VSwing_Emph := DP_CTL_VSWING_0_EMPH_0;
+      end case;
+
+      Registers.Unset_And_Set_Mask
+        (Register    => Registers.DP_CTL_A,
+         Mask_Unset  => DP_CTL_VSWING_EMPH_SET_MASK,
+         Mask_Set    => DP_CTL_VSWING_EMPH_SELECT (VSwing_Emph));
+   end Set_Signal_Levels;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_On
+     (Config      : Config_Type;
+      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 (Config.DP.Lane_Count);
+
+      if Config.DP.Enhanced_Framing then
+         DP_CTL_Set := DP_CTL_Set or DP_CTL_ENHANCED_FRAMING_ENABLE;
+      end if;
+
+      case Config.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 Config.Mode.V_Sync_Active_High then
+         DP_CTL_Set := DP_CTL_Set or DP_CTL_VSYNC_ACTIVE_HIGH;
+      end if;
+      if Config.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);
+      Time.U_Delay (20);
+   end Pre_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Post_On 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_NORMAL);
+   end Post_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off
+   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.Unset_Mask
+        (Register => Registers.DP_CTL_A,
+         Mask     => DP_CTL_DISPLAYPORT_ENABLE);
+      -- TODO: wait?
+
+      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#);
+
+      if Enabled then
+         Time.U_Delay (20);
+      end if;
+   end Off;
+
+end HW.GFX.GMA.Connectors.IRL_EDP;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors-irl_edp.ads b/src/drivers/intel/gma/hw-gfx-gma-connectors-irl_edp.ads
new file mode 100644
index 0000000..ff50189
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors-irl_edp.ads
@@ -0,0 +1,58 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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_Info;
+
+private package HW.GFX.GMA.Connectors.IRL_EDP
+is
+   EDP_BPC : constant BPC_Type := 6;
+
+   procedure Pre_On
+     (Config      : Config_Type;
+      Pipe_Hint   : Word32);
+
+   procedure Post_On;
+
+   procedure Off;
+
+   procedure Pre_Training;
+
+   pragma Warnings (GNATprove, Off, "unused variable ""Connector""",
+                    Reason => "Needed for a common interface");
+   pragma Warnings (GNATprove, Off, "unused variable ""Link""",
+                    Reason => "Needed for a common interface");
+   procedure Set_Training_Pattern
+     (Connector   : Connector_Type;
+      Link        : DP_Link;
+      Pattern     : DP_Info.Training_Pattern);
+
+   procedure Set_Signal_Levels
+     (Connector   : Connector_Type;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set);
+   pragma Warnings (GNATprove, On, "unused variable ""Connector""");
+   pragma Warnings (GNATprove, On, "unused variable ""Link""");
+
+   Connector_Inst : constant Connectors.Connector_Type;
+
+private
+
+   Connector_Inst : constant Connectors.Connector_Type :=
+      Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_EDP,
+      Sub   => Connectors.EDP,
+      Inst  => Connectors.DIGI_A);
+
+end HW.GFX.GMA.Connectors.IRL_EDP;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors-lvds.adb b/src/drivers/intel/gma/hw-gfx-gma-connectors-lvds.adb
new file mode 100644
index 0000000..a8fa894
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors-lvds.adb
@@ -0,0 +1,64 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors.LVDS is
+
+   PCH_LVDS_ENABLE               : constant :=  1 * 2 ** 31;
+   PCH_LVDS_SOURCE_SELECT_MASK   : constant :=  3 * 2 ** 29;
+   PCH_LVDS_SOURCE_SELECT_SHIFT  : constant :=      2 ** 29;
+   PCH_LVDS_TWO_CHANNEL          : constant := 15 * 2 **  2;
+
+   PCH_LVDS_MASK : constant Word32 :=
+      PCH_LVDS_ENABLE or
+      PCH_LVDS_SOURCE_SELECT_MASK or
+      PCH_LVDS_TWO_CHANNEL;
+
+   ----------------------------------------------------------------------------
+
+   procedure On (Transcoder_No : Word32)
+   is
+      Two_Channel       : Word32 := 0;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if GMA.Config.Internal_Is_LVDS_Dual then
+         Two_Channel := PCH_LVDS_TWO_CHANNEL;
+      end if;
+
+      Registers.Unset_And_Set_Mask
+         (Register   => Registers.PCH_LVDS,
+          Mask_Unset => PCH_LVDS_MASK,
+          Mask_Set   => PCH_LVDS_ENABLE or
+                        Transcoder_No * PCH_LVDS_SOURCE_SELECT_SHIFT 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.Connectors.LVDS;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors-lvds.ads b/src/drivers/intel/gma/hw-gfx-gma-connectors-lvds.ads
new file mode 100644
index 0000000..2381da5
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors-lvds.ads
@@ -0,0 +1,41 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Connectors.LVDS is
+
+   LVDS_BPC : constant HW.GFX.BPC_Type := 6;
+
+   procedure On (Transcoder_No : Word32);
+
+   procedure Off;
+
+   Connector_Single : constant Connectors.Connector_Type;
+   Connector_Dual   : constant Connectors.Connector_Type;
+
+private
+
+   Connector_Single : constant Connectors.Connector_Type :=
+      Connectors.Connector_Type'
+        (Kind  => Connectors.Conn_LVDS,
+         Sub   => Connectors.LVDS_Single,
+         Inst  => Connectors.Digital_Port'Last);
+
+   Connector_Dual : constant Connectors.Connector_Type :=
+      Connectors.Connector_Type'
+        (Kind  => Connectors.Conn_LVDS,
+         Sub   => Connectors.LVDS_Dual,
+         Inst  => Connectors.Digital_Port'Last);
+
+end HW.GFX.GMA.Connectors.LVDS;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors-pch_analog.adb b/src/drivers/intel/gma/hw-gfx-gma-connectors-pch_analog.adb
new file mode 100644
index 0000000..8facfbe
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors-pch_analog.adb
@@ -0,0 +1,80 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors.PCH_Analog
+is
+   PCH_ADPA_DAC_ENABLE           : constant := 1 * 2 ** 31;
+   PCH_ADPA_SOURCE_SELECT_MASK   : constant := 3 * 2 ** 29;
+   PCH_ADPA_SOURCE_SELECT_SHIFT  : constant :=     2 ** 29;
+   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_ADPA_DAC_ENABLE           or
+      PCH_ADPA_SOURCE_SELECT_MASK   or
+      PCH_ADPA_VSYNC_DISABLE        or
+      PCH_ADPA_HSYNC_DISABLE        or
+      PCH_ADPA_VSYNC_ACTIVE_HIGH    or
+      PCH_ADPA_HSYNC_ACTIVE_HIGH;
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (Transcoder_No     : Word32;
+      HSync_Active_High : Boolean;
+      VSync_Active_High : Boolean)
+   is
+      VSync_Polarity    : Word32 := 0;
+      HSync_Polarity    : Word32 := 0;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if HSync_Active_High then
+         HSync_Polarity := PCH_ADPA_HSYNC_ACTIVE_HIGH;
+      end if;
+
+      if VSync_Active_High then
+         VSync_Polarity := 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
+                       Transcoder_No * PCH_ADPA_SOURCE_SELECT_SHIFT or
+                       HSync_Polarity or
+                       VSync_Polarity);
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+        (Register   => Registers.PCH_ADPA,
+         Mask_Unset => PCH_ADPA_MASK,
+         Mask_Set   => PCH_ADPA_HSYNC_DISABLE or PCH_ADPA_VSYNC_DISABLE);
+   end Off;
+
+end HW.GFX.GMA.Connectors.PCH_Analog;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors-pch_analog.ads b/src/drivers/intel/gma/hw-gfx-gma-connectors-pch_analog.ads
new file mode 100644
index 0000000..31ed76c
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors-pch_analog.ads
@@ -0,0 +1,34 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Connectors.PCH_Analog is
+
+   procedure On
+     (Transcoder_No     : Word32;
+      HSync_Active_High : Boolean;
+      VSync_Active_High : Boolean);
+
+   procedure Off;
+
+   Connector : constant Connectors.Connector_Type;
+
+private
+
+   Connector : constant Connectors.Connector_Type := Connectors.Connector_Type'
+     (Kind  => Connectors.Conn_Analog,
+      Sub   => Connectors.VGA,
+      Inst  => Connectors.Digital_Port'Last);
+
+end HW.GFX.GMA.Connectors.PCH_Analog;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors-training.adb b/src/drivers/intel/gma/hw-gfx-gma-connectors-training.adb
new file mode 100644
index 0000000..920d56b
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors-training.adb
@@ -0,0 +1,465 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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 Ada.Unchecked_Conversion;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+with HW.Time;
+with HW.GFX.DP_Defs;
+with HW.GFX.DP_Info;
+with HW.GFX.DP_Aux_Ch;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Connectors.DDI;
+with HW.GFX.GMA.Connectors.IRL_EDP;
+
+use type HW.Word8;
+use type HW.Word64;
+use type HW.GFX.DP_Info.Training_Pattern;
+use type HW.GFX.DP_Info.DP_Voltage_Swing;
+use type HW.GFX.DP_Info.DP_Pre_Emph;
+
+package body HW.GFX.GMA.Connectors.Training is
+
+   function Highest_Voltage_Swing
+     (Connector   : Connector_Type)
+      return DP_Info.DP_Voltage_Swing
+   is
+      Voltage_Swing : DP_Info.DP_Voltage_Swing;
+   begin
+      case GMA.Config.CPU is
+         when Skylake =>
+            if Connector.Inst = DIGI_A and Config.EDP_Low_Voltage_Swing then
+               Voltage_Swing := DP_Info.VS_Level_3;
+            else
+               Voltage_Swing := DP_Info.VS_Level_2;
+            end if;
+         when Haswell | Broadwell =>
+            Voltage_Swing := DP_Info.VS_Level_2;
+         when Ironlake =>
+            case Connector.Inst is
+               when DIGI_A => Voltage_Swing := DP_Info.VS_Level_2;
+               when others => Voltage_Swing := DP_Info.VS_Level_3;
+            end case;
+      end case;
+      return Voltage_Swing;
+   end Highest_Voltage_Swing;
+
+   function Highest_Pre_Emph
+     (Connector   : Connector_Type;
+      Train_Set   : DP_Info.Train_Set)
+      return DP_Info.DP_Pre_Emph
+   is
+      Pre_Emph : DP_Info.DP_Pre_Emph;
+   begin
+      case GMA.Config.CPU is
+         when Haswell | Broadwell | Skylake =>
+            case Train_Set.Voltage_Swing is
+               when DP_Info.VS_Level_0 => Pre_Emph := DP_Info.Emph_Level_3;
+               when DP_Info.VS_Level_1 => Pre_Emph := DP_Info.Emph_Level_2;
+               when DP_Info.VS_Level_2 => Pre_Emph := DP_Info.Emph_Level_1;
+               when others             => Pre_Emph := DP_Info.Emph_Level_0;
+            end case;
+         when Ironlake =>
+            if Connector.Inst = DIGI_A then
+               case Train_Set.Voltage_Swing is
+                  when DP_Info.VS_Level_0 => Pre_Emph := DP_Info.Emph_Level_2;
+                  when DP_Info.VS_Level_1 |
+                       DP_Info.VS_Level_2 => Pre_Emph := DP_Info.Emph_Level_1;
+                  when others             => Pre_Emph := DP_Info.Emph_Level_0;
+               end case;
+            else
+               case Train_Set.Voltage_Swing is
+                  when DP_Info.VS_Level_0 |
+                       DP_Info.VS_Level_1 => Pre_Emph := DP_Info.Emph_Level_2;
+                  when DP_Info.VS_Level_2 => Pre_Emph := DP_Info.Emph_Level_1;
+                  when others             => Pre_Emph := DP_Info.Emph_Level_0;
+               end case;
+            end if;
+      end case;
+      return Pre_Emph;
+   end Highest_Pre_Emph;
+
+   function Training_Set
+     (Connector   : Connector_Type;
+      Train_Set   : DP_Info.Train_Set)
+      return Word8
+   is
+      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 = Highest_Voltage_Swing (Connector) 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 = Highest_Pre_Emph (Connector, Train_Set) then
+         Value := Value or 16#20#;
+      end if;
+
+      return Value;
+   end Training_Set;
+
+   ----------------------------------------------------------------------------
+
+   function Lane_Count (Link : DP_Link) return Positive is
+   begin
+      return Positive (Lane_Count_As_Integer (Link.Lane_Count));
+   end Lane_Count;
+
+   procedure Sink_Init
+     (Connector   : in     Connector_Type;
+      Link        : in     DP_Link;
+      Success     :    out Boolean)
+   is
+      function Link_Rate_As_Word8 is new Ada.Unchecked_Conversion
+        (Source => DP_Bandwidth, Target => Word8);
+      Data : DP_Defs.Aux_Payload;
+   begin
+      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;
+
+      DP_Aux_Ch.Aux_Write
+        (Port     => Get_Port_Private (Connector),
+         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
+
+         DP_Aux_Ch.Aux_Write
+           (Port     => Get_Port_Private (Connector),
+            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
+     (Connector   : in     Connector_Type;
+      Link        : in     DP_Link;
+      Pattern     : in     DP_Info.Training_Pattern;
+      Success     :    out Boolean)
+   is
+      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
+      Data :=
+        (0      => TP (Pattern),
+         others => 0);  -- Don't care
+
+      if Pattern < DP_Info.TP_Idle then
+         Length := Length + Lane_Count (Link);
+      end if;
+      DP_Aux_Ch.Aux_Write
+        (Port     => Get_Port_Private (Connector),
+         Address  => 16#00102#,     -- TRAINING_PATTERN_SET
+         Length   => Length,
+         Data     => Data,
+         Success  => Success);
+   end Sink_Set_Training_Pattern;
+
+   procedure Sink_Set_Signal_Levels
+     (Connector   : in     Connector_Type;
+      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 (Connector, Train_Set);
+   begin
+      Data                                := (others => 0); -- Initialize
+      Data (0 .. Lane_Count (Link) - 1)   := (others => T_Set);
+
+      DP_Aux_Ch.Aux_Write
+        (Port     => Get_Port_Private (Connector),
+         Address  => 16#00103#,     -- TRAINING_LANEx_SET
+         Length   => Lane_Count (Link),
+         Data     => Data,
+         Success  => Success);
+   end Sink_Set_Signal_Levels;
+
+   procedure Sink_Adjust_Training
+     (Connector   : in     Connector_Type;
+      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
+      Status : DP_Info.Link_Status;
+      CR_Was_Done : constant Boolean := CR_Done;
+   begin
+      DP_Info.Read_Link_Status
+        (Port     => Get_Port_Private (Connector),
+         Status   => Status,
+         Success  => Success);
+
+      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 > Highest_Voltage_Swing (Connector)
+         then
+            Train_Set.Voltage_Swing := Highest_Voltage_Swing (Connector);
+         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 > Highest_Pre_Emph (Connector, Train_Set)
+         then
+            Train_Set.Pre_Emph := Highest_Pre_Emph (Connector, Train_Set);
+         end if;
+      end if;
+   end Sink_Adjust_Training;
+
+   ----------------------------------------------------------------------------
+
+   procedure Set_Training_Pattern
+     (Connector   : in     Connector_Type;
+      Link        : in     DP_Link;
+      Pattern     : in     DP_Info.Training_Pattern)
+   is
+   begin
+      pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity));
+      pragma Debug (Debug.Put (": "));
+      pragma Debug (Debug.Put_Int8
+        (Int8 (DP_Info.Training_Pattern'Pos (Pattern) + 1)));
+      pragma Debug (Debug.New_Line);
+
+      case GMA.Config.CPU is
+         when Haswell | Broadwell | Skylake =>
+            DDI.Set_Training_Pattern (Connector, Link, Pattern);
+         when Ironlake =>
+            if Connector.Kind = Conn_EDP then
+               IRL_EDP.Set_Training_Pattern (Connector, Link, Pattern);
+            end if;
+      end case;
+   end Set_Training_Pattern;
+
+   procedure Set_Signal_Levels
+     (Connector   : in     Connector_Type;
+      Link        : in     DP_Link;
+      Train_Set   : in     DP_Info.Train_Set)
+   is
+   begin
+      pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity));
+      pragma Debug (Debug.Put (": "));
+      pragma Debug (Debug.Put_Word32
+        (Word32 (Training_Set (Connector, Train_Set))));
+      pragma Debug (Debug.New_Line);
+
+
+      case GMA.Config.CPU is
+         when Haswell | Broadwell | Skylake =>
+            DDI.Set_Signal_Levels (Connector, Link, Train_Set);
+         when Ironlake =>
+            if Connector.Kind = Conn_EDP then
+               IRL_EDP.Set_Signal_Levels (Connector, Link, Train_Set);
+            end if;
+      end case;
+   end Set_Signal_Levels;
+
+   procedure Off (Connector : Connector_Type) is
+   begin
+      case GMA.Config.CPU is
+         when Haswell | Broadwell | Skylake =>
+            DDI.Off (Connector);
+         when Ironlake =>
+            if Connector.Kind = Conn_EDP then
+               IRL_EDP.Off;
+            end if;
+      end case;
+   end Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Train_DP
+     (Connector   : in     Connector_Type;
+      Link        : in     DP_Link;
+      Success     :    out Boolean)
+   is
+      Retries : Natural;
+      Max_Retry : constant := 4;
+      CR_Done, EQ_Done : Boolean := False;
+
+      EQ_Pattern : DP_Info.Training_Pattern;
+
+      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));
+
+      if Link.Bandwidth < DP_Bandwidth_5_4 and
+         not Link.Receiver_Caps.TPS3_Supported
+      then
+         EQ_Pattern := DP_Info.TP_2;
+      else
+         EQ_Pattern := DP_Info.TP_3;
+      end if;
+
+      Train_Set.Voltage_Swing := DP_Info.DP_Voltage_Swing'First;
+      Train_Set.Pre_Emph      := DP_Info.DP_Pre_Emph'First;
+
+      Set_Training_Pattern (Connector, Link, DP_Info.TP_1);
+      Set_Signal_Levels (Connector, 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 (Connector, Link, Success);
+      pragma Warnings
+        (GNATprove, On, """Success"" modified by call, but value overwritten*");
+      if Success then
+         Sink_Set_Training_Pattern (Connector, 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
+              (Connector, 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 =
+                  Highest_Voltage_Swing (Connector);
+               Retries := 0;
+            end if;
+
+            Set_Signal_Levels (Connector, Link, Train_Set);
+            Sink_Set_Signal_Levels (Connector, Link, Train_Set, Success);
+            exit when not Success;
+         end loop;
+      end if;
+
+      Success := Success and CR_Done;
+
+      if Success then
+         Set_Training_Pattern (Connector, Link, EQ_Pattern);
+         Sink_Set_Training_Pattern (Connector, 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
+              (Connector, 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 =
+                  Highest_Pre_Emph (Connector, Last_Train_Set);
+               Retries := 0;
+            end if;
+
+            Set_Signal_Levels (Connector, Link, Train_Set);
+            Sink_Set_Signal_Levels (Connector, Link, Train_Set, Success);
+            exit when not Success;
+         end loop;
+      end if;
+
+      if Success then
+         if EQ_Done then
+            -- Set_Training_Pattern (TP_None) includes sending the Idle Pattern,
+            -- so tell sink first.
+            Sink_Set_Training_Pattern
+              (Connector, Link, DP_Info.TP_None, Success);
+         else
+            Success := False;
+         end if;
+      end if;
+
+      if Success then
+         Set_Training_Pattern (Connector, Link, DP_Info.TP_None);
+      else
+         Off (Connector);
+      end if;
+   end Train_DP;
+
+end HW.GFX.GMA.Connectors.Training;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors-training.ads b/src/drivers/intel/gma/hw-gfx-gma-connectors-training.ads
new file mode 100644
index 0000000..d04d41e
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors-training.ads
@@ -0,0 +1,23 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Connectors.Training is
+
+   procedure Train_DP
+     (Connector   : in     Connector_Type;
+      Link        : in     DP_Link;
+      Success     :    out Boolean);
+
+end HW.GFX.GMA.Connectors.Training;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors.adb b/src/drivers/intel/gma/hw-gfx-gma-connectors.adb
new file mode 100644
index 0000000..c3c4e57
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors.adb
@@ -0,0 +1,401 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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_Info;
+with HW.GFX.DP_Aux_Ch;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.I2C;
+with HW.GFX.GMA.Connectors.Digital;
+with HW.GFX.GMA.Connectors.PCH_Analog;
+with HW.GFX.GMA.Connectors.LVDS;
+with HW.GFX.GMA.Connectors.DDI;
+with HW.GFX.GMA.Connectors.IRL_EDP;
+with HW.GFX.GMA.Connectors.Training;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+use type HW.Word16;
+
+package body HW.GFX.GMA.Connectors is
+
+   function Is_DP (Connector : Connector_Type) return Boolean is
+   begin
+      return Connector.Sub = DP or Connector.Sub = EDP;
+   end Is_DP;
+
+   function Is_Internal (Connector : Connector_Type) return Boolean is
+   begin
+      return Connector.Sub = EDP or Connector.Kind = Conn_LVDS;
+   end Is_Internal;
+
+   function Get_Port_Private (Connector : Connector_Type) return Port_Private is
+   begin
+      return
+        (if Is_DP (Connector) then PORT_PRIVATE_DP else 0)
+         or
+        (case Connector.Kind is
+            when Conn_Analog           => PORT_PRIVATE_ANALOG,
+            when Conn_LVDS | Conn_EDP  => PORT_PRIVATE_DIGI_A,
+            when others                =>
+              (case Connector.Inst is
+                  when DIGI_A => PORT_PRIVATE_DIGI_A,
+                  when DIGI_B => PORT_PRIVATE_DIGI_B,
+                  when DIGI_C => PORT_PRIVATE_DIGI_C,
+                  when DIGI_D => PORT_PRIVATE_DIGI_D,
+                  when others => 0));
+   end Get_Port_Private;
+
+   procedure Read_EDID
+     (Raw_EDID    :    out EDID.Raw_EDID_Data;
+      Connector   : in     Connector_Type;
+      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));
+
+      Raw_EDID := (others => 16#00#);
+
+      for I in 1 .. 2 loop
+         if Is_DP (Connector) then
+            DP_Aux_Ch.I2C_Read
+              (Port     => Get_Port_Private (Connector),
+               Address  => 16#50#,
+               Length   => Raw_EDID_Length,
+               Data     => Raw_EDID,
+               Success  => Success);
+         elsif I2C.Port_Valid (Get_Port_Private (Connector)) then
+            I2C.I2C_Read
+              (Port     => Get_Port_Private (Connector),
+               Address  => 16#50#,
+               Length   => Raw_EDID_Length,
+               Data     => Raw_EDID,
+               Success  => Success);
+         else
+            Success := False;
+         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
+     (Dsp_Config  : in out Config_Type;
+      Connector   : in     Connector_Type;
+      Success     :    out Boolean) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.VGA_Through_FDI and Connector.Sub = VGA then
+         Dsp_Config.DP.Lane_Count         := DP_Lane_Count_2;
+         Dsp_Config.DP.Bandwidth          := DP_Bandwidth_2_7;
+         Dsp_Config.DP.Enhanced_Framing   := True;
+         Success := True;
+      elsif Is_DP (Connector) then
+         if Is_Internal (Connector) then
+            if GMA.Config.Use_PP_VDD_Override then
+               Panel.VDD_Override;
+            else
+               Panel.On;
+            end if;
+         end if;
+
+         DP_Info.Read_Caps
+           (Link     => Dsp_Config.DP,
+            Port     => Get_Port_Private (Connector),
+            Success  => Success);
+         if Success then
+            DP_Info.Preferred_Link_Setting
+              (Link     => Dsp_Config.DP,
+               Mode     => Dsp_Config.Mode,
+               BPC      => BPC (Connector),
+               Success  => Success);
+         end if;
+      else
+         Success := True;
+      end if;
+
+   end Preferred_Link_Setting;
+
+   procedure Next_Link_Setting
+     (Dsp_Config  : in out Config_Type;
+      Connector   : in     Connector_Type;
+      Success     :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Is_DP (Connector) then
+         DP_Info.Next_Link_Setting
+           (Link     => Dsp_Config.DP,
+            Mode     => Dsp_Config.Mode,
+            BPC      => BPC (Connector),
+            Success  => Success);
+      else
+         Success := False;
+      end if;
+   end Next_Link_Setting;
+
+   procedure Pre_On
+     (Dsp_Config  : in     Config_Type;
+      Connector   : in     Connector_Type;
+      PLL_Hint    : in     Word32;
+      Pipe_Hint   : in     Word32;
+      Success     :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Success := True;
+      case Connector.Kind is
+         when Conn_Invalid => null;
+         when Conn_Analog  => null;
+         when Conn_Digital => null;
+         when Conn_LVDS    => null;
+         when Conn_EDP     =>
+            IRL_EDP.Pre_On (Dsp_Config, Pipe_Hint);
+         when Conn_DDI     =>
+            DDI.Pre_On (Connector, PLL_Hint, Success);
+            if Is_DP (Connector) and Success then
+               Training.Train_DP (Connector, Dsp_Config.DP, Success);
+            end if;
+      end case;
+   end Pre_On;
+
+   procedure Post_On
+     (Dsp_Config  : Config_Type;
+      Connector   : Connector_Type;
+      Pipe_Hint   : Word32)
+   is
+      Success : Boolean := True; -- FIXME
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      case Connector.Kind is
+         when Conn_Invalid =>
+            null;
+         when Conn_Analog =>
+            PCH_Analog.On
+              (Pipe_Hint,
+               Dsp_Config.Mode.H_Sync_Active_High,
+               Dsp_Config.Mode.V_Sync_Active_High);
+         when Conn_Digital =>
+            Digital.On
+              (Connector, Pipe_Hint,
+               Dsp_Config.Mode.H_Sync_Active_High,
+               Dsp_Config.Mode.V_Sync_Active_High);
+         when Conn_LVDS =>
+            LVDS.On (Pipe_Hint);
+            Panel.On;
+         when Conn_EDP =>
+            IRL_EDP.Pre_Training;
+            Panel.On;
+            Training.Train_DP (Connector, Dsp_Config.DP, Success);
+            if Success then
+               IRL_EDP.Post_On;
+            end if;
+         when Conn_DDI =>
+            DDI.Post_On (Connector, Dsp_Config.Mode);
+      end case;
+      if Is_Internal (Connector) and Success then
+         Panel.Backlight_On;
+      end if;
+   end Post_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_Off (Connector : Connector_Type) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Is_Internal (Connector) then
+         Panel.Backlight_Off;
+         Panel.Off;
+      end if;
+   end Pre_Off;
+
+   procedure Post_Off (Connector : Connector_Type) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      case Connector.Kind is
+         when Conn_Invalid => null;
+         when Conn_Analog  => PCH_Analog.Off;
+         when Conn_Digital => Digital.Off (Connector);
+         when Conn_LVDS    => LVDS.Off;
+         when Conn_EDP     => IRL_EDP.Off;
+         when Conn_DDI     => DDI.Off (Connector);
+      end case;
+   end Post_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_All_Off is
+   begin
+      Pre_Off (Connector_Analog);
+      Pre_Off (Connector_Digital (DIGI_B));
+      Pre_Off (Connector_Digital (DIGI_C));
+      Pre_Off (Connector_Digital (DIGI_D));
+      Pre_Off (Connector_Internal);
+   end Pre_All_Off;
+
+   procedure Post_All_Off is
+   begin
+      Post_Off (Connector_Analog);
+      Post_Off (Connector_Digital (DIGI_B));
+      Post_Off (Connector_Digital (DIGI_C));
+      Post_Off (Connector_Digital (DIGI_D));
+      Post_Off (Connector_Internal);
+   end Post_All_Off;
+
+   ----------------------------------------------------------------------------
+
+   function BPC (Connector : Connector_Type) return HW.GFX.BPC_Type
+   is
+      Result : HW.GFX.BPC_Type;
+   begin
+      case Connector.Kind is
+         when Conn_LVDS => Result := LVDS.LVDS_BPC;
+         when Conn_EDP  => Result := IRL_EDP.EDP_BPC;
+         when Conn_DDI  => Result := DDI.DDI_BPC (Connector.Sub);
+         when others    => Result := 8;
+      end case;
+      return Result;
+   end BPC;
+
+   ----------------------------------------------------------------------------
+
+   function Connector_Analog return Connector_Type
+   is
+      Connector : Connector_Type;
+   begin
+      case GMA.Config.CPU is
+         when Ironlake =>
+            Connector := PCH_Analog.Connector;
+         when Haswell | Broadwell =>
+            if GMA.Config.Normal_CPU then
+               Connector := DDI.Inst_VGA;
+            else
+               Connector := Invalid_Connector;
+            end if;
+         when Skylake =>
+            Connector := Invalid_Connector;
+      end case;
+      return Connector;
+   end Connector_Analog;
+
+   ----------------------------------------------------------------------------
+
+   function Connector_Digital (Inst : Digital_Port) return Connector_Type
+   is
+      Connector : Connector_Type;
+   begin
+      case GMA.Config.CPU is
+         when Ironlake =>
+            case Inst is
+               when DIGI_B => Connector := Digital.HDMI_B;
+               when DIGI_C => Connector := Digital.HDMI_C;
+               when DIGI_D => Connector := Digital.HDMI_D;
+               when others => Connector := Invalid_Connector;
+            end case;
+         when Haswell | Broadwell | Skylake =>
+            case Inst is
+               when DIGI_B => Connector := DDI.HDMI_B;
+               when DIGI_C => Connector := DDI.HDMI_C;
+               when DIGI_D =>
+                  case GMA.Config.CPU_Var is
+                     when ULT    => Connector := Invalid_Connector;
+                     when others => Connector := DDI.HDMI_D;
+                  end case;
+               when others => Connector := Invalid_Connector;
+            end case;
+      end case;
+      return Connector;
+   end Connector_Digital;
+
+   ----------------------------------------------------------------------------
+
+   function Connector_Internal return Connector_Type
+   is
+      Connector : Connector_Type;
+   begin
+      case GMA.Config.CPU is
+         when Ironlake =>
+            case GMA.Config.Internal_Port is
+               when LVDS_Single  => Connector := LVDS.Connector_Single;
+               when LVDS_Dual    => Connector := LVDS.Connector_Dual;
+               when Int_EDP      => Connector := IRL_EDP.Connector_Inst;
+            end case;
+         when Haswell | Broadwell | Skylake => Connector := DDI.Inst_EDP;
+      end case;
+      return Connector;
+   end Connector_Internal;
+
+   ----------------------------------------------------------------------------
+
+   function Connector_DP (Inst : Digital_Port) return Connector_Type
+   is
+      Connector : Connector_Type;
+   begin
+      case GMA.Config.CPU is
+         when Ironlake =>
+            case Inst is
+               when DIGI_B => Connector := Invalid_Connector;
+               when DIGI_C => Connector := Invalid_Connector;
+               when DIGI_D => Connector := Invalid_Connector;
+               when others => Connector := Invalid_Connector;
+            end case;
+         when Haswell | Broadwell | Skylake =>
+            case Inst is
+               when DIGI_B => Connector := DDI.DP_B;
+               when DIGI_C => Connector := DDI.DP_C;
+               when DIGI_D =>
+                  case GMA.Config.CPU_Var is
+                     when ULT    => Connector := Invalid_Connector;
+                     when others => Connector := DDI.DP_D;
+                  end case;
+               when others => Connector := Invalid_Connector;
+            end case;
+      end case;
+      return Connector;
+   end Connector_DP;
+
+   ----------------------------------------------------------------------------
+
+   function Kind (Connector : Connector_Type) return Class
+   is
+   begin
+      return Connector.Kind;
+   end Kind;
+
+   function Sub (Connector : Connector_Type) return Sub_Type
+   is
+   begin
+      return Connector.Sub;
+   end Sub;
+
+   function Digi (Connector : Connector_Type) return Digital_Port
+   is
+   begin
+      return Connector.Inst;
+   end Digi;
+
+end HW.GFX.GMA.Connectors;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-connectors.ads b/src/drivers/intel/gma/hw-gfx-gma-connectors.ads
new file mode 100644
index 0000000..a060d15
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-connectors.ads
@@ -0,0 +1,104 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.EDID;
+
+private package HW.GFX.GMA.Connectors is
+
+   type Class is
+     (Conn_Invalid,
+      Conn_Analog,
+      Conn_Digital,
+      Conn_LVDS,
+      Conn_EDP,
+      Conn_DDI);
+
+   type Sub_Type is (VGA, LVDS_Single, LVDS_Dual, HDMI, DP, EDP);
+
+   type Digital_Port is (DIGI_A, DIGI_B, DIGI_C, DIGI_D, DIGI_E);
+
+   type Connector_Type is private;
+
+   Invalid_Connector : constant Connector_Type;
+
+   procedure Read_EDID
+     (Raw_EDID    :    out EDID.Raw_EDID_Data;
+      Connector   : in     Connector_Type;
+      Success     :    out Boolean)
+   with
+      Post => (if Success then EDID.Valid (Raw_EDID));
+
+   procedure Preferred_Link_Setting
+     (Dsp_Config  : in out Config_Type;
+      Connector   : in     Connector_Type;
+      Success     :    out Boolean)
+      with
+         Post => (Dsp_Config.Port = Dsp_Config.Port'Old);
+
+   procedure Next_Link_Setting
+     (Dsp_Config  : in out Config_Type;
+      Connector   : in     Connector_Type;
+      Success     :    out Boolean)
+      with
+         Post => (Dsp_Config.Port = Dsp_Config.Port'Old);
+
+   procedure Pre_On
+     (Dsp_Config  : in     Config_Type;
+      Connector   : in     Connector_Type;
+      PLL_Hint    : in     Word32;
+      Pipe_Hint   : in     Word32;
+      Success     :    out Boolean);
+
+   procedure Post_On
+     (Dsp_Config  : Config_Type;
+      Connector   : Connector_Type;
+      Pipe_Hint   : Word32);
+
+   procedure Pre_Off (Connector : Connector_Type);
+   procedure Post_Off (Connector : Connector_Type);
+
+   procedure Pre_All_Off;
+   procedure Post_All_Off;
+
+   function BPC (Connector : Connector_Type) return HW.GFX.BPC_Type;
+
+   function Connector_Analog return Connector_Type;
+   function Connector_Digital (Inst : Digital_Port) return Connector_Type;
+   function Connector_Internal return Connector_Type;
+   function Connector_DP (Inst : Digital_Port) return Connector_Type;
+
+   function Kind (Connector : Connector_Type) return Class;
+   function Sub (Connector : Connector_Type) return Sub_Type;
+   function Digi (Connector : Connector_Type) return Digital_Port;
+
+private
+
+   type Connector_Type is
+      record
+         Kind  : Class;
+         Sub   : Sub_Type;
+         Inst  : Digital_Port;
+      end record;
+
+   Invalid_Connector : constant Connector_Type := Connector_Type'
+     (Kind  => Conn_Invalid,
+      Sub   => Sub_Type'Last,
+      Inst  => Digital_Port'Last);
+
+   function Get_Port_Private (Connector : Connector_Type) return Port_Private
+   with
+      Pre => True;
+
+end HW.GFX.GMA.Connectors;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-coreboot.adb b/src/drivers/intel/gma/hw-gfx-gma-coreboot.adb
new file mode 100644
index 0000000..31a3ddf
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-coreboot.adb
@@ -0,0 +1,81 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Coreboot is
+
+   procedure One_Shot (MMIO, Linear_FB : System.Address; Phys_FB : Word32)
+   is
+      use type GTT_Range;
+
+      Outputs : Configs_Type := Configs_Type'
+        (Primary   => Config_Type'
+           (Port        => Disabled,
+            Framebuffer => HW.GFX.Default_FB,
+            Mode        => HW.GFX.Invalid_Mode,
+            DP          => HW.GFX.Default_DP),
+         Secondary => Config_Type'
+           (Port        => Disabled,
+            Framebuffer => HW.GFX.Default_FB,
+            Mode        => HW.GFX.Invalid_Mode,
+            DP          => HW.GFX.Default_DP),
+         Tertiary => Config_Type'
+           (Port        => Disabled,
+            Framebuffer => HW.GFX.Default_FB,
+            Mode        => HW.GFX.Invalid_Mode,
+            DP          => HW.GFX.Default_DP));
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Set_Register_Base (MMIO);
+
+      Initialize (0);
+
+      Panel.On;
+
+      Auto_Configure (Outputs);
+      Outputs (Primary).Framebuffer.Stride := 2048;
+      Outputs (Secondary).Port   := Disabled;
+      Outputs (Tertiary).Port    := Disabled;
+
+      -- Framebuffer size: 2048 * 2048 * 4 BPP = 16M = 4096 * 4K pages
+      for Idx in GTT_Range range 0 .. 4095 loop
+         Registers.Write_GTT
+           (GTT_Page       => Idx,
+            Device_Address => GTT_Address_Type (Phys_FB + Word32 (Idx * 4096)),
+            Valid          => True);
+      end loop;
+      Framebuffer_Filler.Fill (Linear_FB, Outputs (Primary).Framebuffer);
+
+      Update_Outputs (Outputs);
+
+      pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity));
+      pragma Debug (Debug.Put_Line (": End."));
+   end One_Shot;
+
+   procedure One_Shot_C (MMIO, Phys_FB, Linear_FB : Word32)
+   with
+      SPARK_Mode => Off
+   is
+   begin
+      One_Shot
+        (MMIO        => System'To_Address (MMIO),
+         Linear_FB   => System'To_Address (Linear_FB),
+         Phys_FB     => Phys_FB);
+   end One_Shot_C;
+
+end HW.GFX.GMA.Coreboot;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-coreboot.ads b/src/drivers/intel/gma/hw-gfx-gma-coreboot.ads
new file mode 100644
index 0000000..6f8fcd8
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-coreboot.ads
@@ -0,0 +1,52 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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 System;
+with HW.Time;
+with HW.Port_IO;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.Registers;
+with HW.GFX.Framebuffer_Filler;
+
+private package HW.GFX.GMA.Coreboot is
+
+   procedure One_Shot (MMIO, Linear_FB : System.Address; Phys_FB : Word32)
+   with
+      Global =>
+        (Output =>
+           (Registers.Address_State, Framebuffer_Filler.Address_State,
+            Panel.Panel_State),
+         In_Out =>
+           (Registers.Register_State, Registers.GTT_State,
+            Framebuffer_Filler.Framebuffer_State,
+            Time.State, Port_IO.State));
+
+   procedure One_Shot_C (MMIO, Phys_FB, Linear_FB : Word32)
+   with
+      Global =>
+        (Output =>
+           (Registers.Address_State, Framebuffer_Filler.Address_State,
+            Panel.Panel_State),
+         In_Out =>
+           (Registers.Register_State, Registers.GTT_State,
+            Framebuffer_Filler.Framebuffer_State,
+            Time.State, Port_IO.State)),
+      Pre => True,
+      Post => True,
+      Export,
+      Convention => C,
+      Link_Name => "hw_gfx_gma_oneshot";
+
+end HW.GFX.GMA.Coreboot;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-display_controller.adb b/src/drivers/intel/gma/hw-gfx-gma-display_controller.adb
new file mode 100644
index 0000000..152c8ff
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-display_controller.adb
@@ -0,0 +1,690 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Time;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Connectors;
+with HW.GFX.GMA.Registers;
+
+use type HW.Word64;
+use type HW.Pos16;
+use type HW.Int32;
+use type HW.GFX.GMA.Connectors.Sub_Type;
+use type HW.GFX.GMA.Registers.Registers_Invalid_Index;
+
+package body HW.GFX.GMA.Display_Controller 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 (Connectors.Digital_Port) of Word32;
+   TRANS_CLK_SEL_PORT : constant TRANS_CLK_SEL_PORT_Array :=
+      TRANS_CLK_SEL_PORT_Array'
+     (Connectors.DIGI_A => 0 * 2 ** 29,   -- DDI A is not selectable
+      Connectors.DIGI_B => 2 * 2 ** 29,
+      Connectors.DIGI_C => 3 * 2 ** 29,
+      Connectors.DIGI_D => 4 * 2 ** 29,
+      Connectors.DIGI_E => 5 * 2 ** 29);
+
+   PIPECONF_ENABLE          : constant := 1 * 2 ** 31;
+   PIPECONF_ENABLED_STATUS  : constant := 1 * 2 ** 30;
+   PIPECONF_BPC_MASK        : constant := 7 * 2 **  5;
+   PIPECONF_ENABLE_DITHER   : constant := 1 * 2 **  4;
+   PIPECONF_DITHER_TEMPORAL : constant := 1 * 2 **  2;
+
+   PIPECONF_MASK : constant Word32 :=
+      PIPECONF_ENABLE or
+      PIPECONF_BPC_MASK or
+      PIPECONF_ENABLE_DITHER or
+      PIPECONF_DITHER_TEMPORAL;
+
+   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 (Connectors.Digital_Port) of Word32;
+   PIPE_DDI_FUNC_CTL_DDI_SELECT : constant DDI_Select_Array :=
+      DDI_Select_Array'
+     (Connectors.DIGI_A => PIPE_DDI_FUNC_CTL_DDI_SELECT_NONE,
+      Connectors.DIGI_B => PIPE_DDI_FUNC_CTL_DDI_SELECT_B,
+      Connectors.DIGI_C => PIPE_DDI_FUNC_CTL_DDI_SELECT_C,
+      Connectors.DIGI_D => PIPE_DDI_FUNC_CTL_DDI_SELECT_D,
+      Connectors.DIGI_E => PIPE_DDI_FUNC_CTL_DDI_SELECT_E);
+
+   type DDI_Mode_Array is array (Connectors.Sub_Type) of Word32;
+   PIPE_DDI_FUNC_CTL_MODE_SELECT : constant DDI_Mode_Array :=
+      DDI_Mode_Array'
+     (Connectors.VGA          => PIPE_DDI_FUNC_CTL_MODE_SELECT_FDI,
+      Connectors.LVDS_Single  => PIPE_DDI_FUNC_CTL_MODE_SELECT_FDI,
+      Connectors.LVDS_Dual    => PIPE_DDI_FUNC_CTL_MODE_SELECT_FDI,
+      Connectors.HDMI         => PIPE_DDI_FUNC_CTL_MODE_SELECT_DVI,
+      Connectors.DP           => PIPE_DDI_FUNC_CTL_MODE_SELECT_DP_SST,
+      Connectors.EDP          => PIPE_DDI_FUNC_CTL_MODE_SELECT_DP_SST);
+
+   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;
+
+   PIPE_DATA_N_MAX      : constant := 16#800000#;
+   PIPE_LINK_N_MAX      : constant := 16#100000#;
+   DATA_LINK_M_N_MASK   : constant := 16#ffffff#;
+
+   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;
+      Connector   : Connectors.Connector_Type;
+      Dsp_Config  : Config_Type)
+   with
+      Global => (In_Out => Registers.Register_State),
+      Depends => (Registers.Register_State
+                     =>+
+                        (Head, Connector, Dsp_Config))
+   is
+      subtype M_Type          is Int64 range 0 .. 2 ** 36;
+      subtype N_Type          is Int64 range 0 .. 2 ** 36;
+      subtype N_Rounded_Type  is Int64 range 0 .. PIPE_DATA_N_MAX;
+      M : M_Type;
+      N : N_Type;
+
+      procedure Cancel_M_N
+        (M     : in out M_Type;
+         N     : in out N_Type;
+         N_Max : in     N_Rounded_Type)
+      with
+         Depends => ((M, N) => (M, N, N_max)),
+         Pre => (N > 0 and M in 0 .. M_Type'Last / 2),
+         Post => (M <= DATA_LINK_M_N_MASK and N <= DATA_LINK_M_N_MASK)
+      is
+         Orig_N : constant N_Type := N;
+
+         function Round_N (N : N_Type) return N_Rounded_Type
+         with
+            Post => (Round_N'Result <= N * 2)
+         is
+            RN : N_Type;
+            RN2 : 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 <= M_Type'Last/2 and
+                N <= Orig_N * 2 and
+                Orig_N > 0 and
+                M > 0
+             then
+                M * N / Orig_N <= 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;
+
+         while M > DATA_LINK_M_N_MASK loop
+            pragma Loop_Invariant (N <= DATA_LINK_M_N_MASK);
+            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
+                     * Connectors.BPC (Connector)
+                     * Dsp_Config.Mode.Dotclock
+                     in Pos64);
+      M := 3
+           * M_Type (Connectors.BPC (Connector))
+           * M_Type (Dsp_Config.Mode.Dotclock);
+
+      pragma Assert (8
+                     * DP_Symbol_Rate (Dsp_Config.DP.Bandwidth)
+                     * Lane_Count_As_Integer (Dsp_Config.DP.Lane_Count)
+                     in Pos64);
+      N := 8
+           * DP_Symbol_Rate (Dsp_Config.DP.Bandwidth)
+           * Lane_Count_As_Integer (Dsp_Config.DP.Lane_Count);
+
+      Cancel_M_N (M, N, PIPE_DATA_N_MAX);
+      Registers.Write
+        (Register => Head.PIPE_DATA_M1,
+         Value    => PIPE_DATA_M_TU (64) or
+                     Word32 (M));
+      Registers.Write
+        (Register => Head.PIPE_DATA_N1,
+         Value    => Word32 (N));
+
+      M := Pos64 (Dsp_Config.Mode.Dotclock);
+      N := Pos64 (DP_Symbol_Rate (Dsp_Config.DP.Bandwidth));
+
+      Cancel_M_N (M, N, PIPE_LINK_N_MAX);
+      Registers.Write
+        (Register => Head.PIPE_LINK_M1,
+         Value    => Word32 (M));
+      Registers.Write
+        (Register => Head.PIPE_LINK_N1,
+         Value    => Word32 (N));
+
+      Registers.Write
+        (Register => Head.PIPE_MSA_MISC,
+         Value    => PIPE_MSA_MISC_SYNC_CLK or
+                     PIPE_MSA_MISC_BPC (Connectors.BPC (Connector)));
+   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.Pre_Haswell 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;
+      Connector   : Connectors.Connector_Type;
+      Dsp_Config  : Config_Type)
+   is
+      PIPECONF_Options : Word32;
+   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 (Connectors.Digi (Connector)) or
+                        PIPE_DDI_FUNC_CTL_MODE_SELECT (Connectors.Sub (Connector)) or
+                        PIPE_DDI_FUNC_CTL_BPC (Connectors.BPC (Connector)) or
+                        PIPE_DDI_FUNC_CTL_VSYNC (Dsp_Config.Mode.V_Sync_Active_High) or
+                        PIPE_DDI_FUNC_CTL_HSYNC (Dsp_Config.Mode.H_Sync_Active_High) or
+                        PIPE_DDI_FUNC_CTL_EDP_SELECT (Controller.Kind) or
+                        PIPE_DDI_FUNC_CTL_PORT_WIDTH (Dsp_Config.DP.Lane_Count));
+      end if;
+
+      PIPECONF_Options := PIPECONF_BPC_MAP (Connectors.BPC (Connector));
+
+      -- Enable dithering if framebuffer BPC differs from connector BPC,
+      -- as smooth gradients look really bad without
+      if Dsp_Config.Framebuffer.BPC /= Connectors.BPC (Connector) then
+         PIPECONF_Options := PIPECONF_Options or PIPECONF_ENABLE_DITHER;
+      end if;
+
+      if not Config.Has_Pipeconf_Misc then
+         Registers.Unset_And_Set_Mask
+            (Register   => Head.PIPECONF,
+             Mask_Unset => PIPECONF_MASK,
+             Mask_Set   => PIPECONF_ENABLE or PIPECONF_Options);
+      else
+         Registers.Write
+            (Register   => Controller.PIPEMISC,
+             Value      => PIPECONF_Options);
+         Registers.Write
+            (Register   => Head.PIPECONF,
+             Value      => PIPECONF_ENABLE);
+      end if;
+   end Setup_Head;
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+      (Controller  : in     Controller_Type;
+       Head        : in     Head_Type;
+       Connector   : in     Connectors.Connector_Type;
+       Dsp_Config  : in     Config_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 (Connectors.Digi (Connector)));
+      end if;
+
+      if (Config.VGA_Through_FDI and
+          Connectors.Sub (Connector) = Connectors.VGA) or
+         Connectors.Sub (Connector) = Connectors.DP or
+         Connectors.Sub (Connector) = Connectors.EDP
+      then
+         Setup_Link (Head, Connector, Dsp_Config);
+      end if;
+
+      Setup_Display (Controller, Head, Dsp_Config.Mode, Dsp_Config.Framebuffer);
+
+      Setup_Head (Controller, Head, Connector, Dsp_Config);
+   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 Enabled or not Config.Is_Broadwell then
+         -- synchronously wait until pipe is truly off
+         Time.M_Delay (30);
+         Registers.Wait_Unset_Mask (Head.PIPECONF, PIPECONF_ENABLED_STATUS);
+      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 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.Display_Controller;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-display_controller.ads b/src/drivers/intel/gma/hw-gfx-gma-display_controller.ads
new file mode 100644
index 0000000..e46e90c
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-display_controller.ads
@@ -0,0 +1,278 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Connectors;
+with HW.GFX.GMA.Registers;
+
+
+private package HW.GFX.GMA.Display_Controller
+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  : in     Controller_Type;
+      Head        : in     Head_Type;
+      Connector   : in     Connectors.Connector_Type;
+      Dsp_Config  : in     Config_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.Display_Controller;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-dp_aux_ch.adb b/src/drivers/intel/gma/hw-gfx-gma-dp_aux_ch.adb
new file mode 100644
index 0000000..384fbbe
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-dp_aux_ch.adb
@@ -0,0 +1,363 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+with HW.GFX.GMA.Connectors;
+
+use type HW.Word8;
+use type HW.GFX.Port_Private;
+use type HW.GFX.GMA.Connectors.Digital_Port;
+use type HW.GFX.GMA.Registers.Registers_Invalid_Index;
+
+package body HW.GFX.GMA.DP_Aux_Ch 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;
+
+   subtype DP_Port is Connectors.Digital_Port
+      range Connectors.DIGI_A .. Connectors.DIGI_D;
+   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'
+        (Connectors.DIGI_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),
+         Connectors.DIGI_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),
+         Connectors.DIGI_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),
+         Connectors.DIGI_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'
+        (Connectors.DIGI_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),
+         Connectors.DIGI_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),
+         Connectors.DIGI_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),
+         Connectors.DIGI_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     Connectors.Digital_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, Time.State)),
+      Depends =>
+        ((Registers.Register_State,
+          Time.State,
+          Response,
+          Response_Length,
+          Success)
+             =>
+               (Registers.Register_State,
+                Time.State,
+                Port,
+                Request,
+                Request_Length)),
+      Pre => Port in DP_Port
+   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;
+      DP_AUX_CTL_TIME_OUT_Timer : 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;
+
+         if Port = Connectors.DIGI_A then
+            DP_AUX_CTL_TIME_OUT_Timer := DP_AUX_CTL_TIME_OUT_TIMER_600US;
+         else
+            DP_AUX_CTL_TIME_OUT_Timer := DP_AUX_CTL_TIME_OUT_TIMER_400US;
+         end if;
+
+         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_TIME_OUT_Timer or
+                           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;
+
+   ----------------------------------------------------------------------------
+
+   function Port_Valid (Port : Port_Private) return Boolean is
+   begin
+      return
+         (Port and PORT_PRIVATE_DP) /= 0 and
+         (Port and PORT_PRIVATE_PORT_MASK) in
+            PORT_PRIVATE_DIGI_A .. PORT_PRIVATE_DIGI_D;
+   end Port_Valid;
+
+   procedure Do_Aux_Request
+     (Port              : in     Port_Private;
+      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
+      function To_GMA_Port return DP_Port is
+      begin
+         return (case Port is
+            when PORT_PRIVATE_EDP  => Connectors.DIGI_A,
+            when PORT_PRIVATE_DP_B => Connectors.DIGI_B,
+            when PORT_PRIVATE_DP_C => Connectors.DIGI_C,
+            when PORT_PRIVATE_DP_D => Connectors.DIGI_D,
+            when others            => Connectors.DIGI_A);
+      end To_GMA_Port;
+   begin
+      for Try in Positive range 1 .. 3 loop
+         Aux_Request_Low
+           (Port              => To_GMA_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_Ch;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-dp_aux_ch.ads b/src/drivers/intel/gma/hw-gfx-gma-dp_aux_ch.ads
new file mode 100644
index 0000000..7130166
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-dp_aux_ch.ads
@@ -0,0 +1,32 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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_Defs;
+
+package HW.GFX.GMA.DP_Aux_Ch is
+
+   function Port_Valid (Port : Port_Private) return Boolean;
+
+   procedure Do_Aux_Request
+     (Port              : in     Port_Private;
+      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
+      Pre => Port_Valid (Port);
+
+end HW.GFX.GMA.DP_Aux_Ch;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-i2c.adb b/src/drivers/intel/gma/hw-gfx-gma-i2c.adb
new file mode 100644
index 0000000..769934f
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-i2c.adb
@@ -0,0 +1,234 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+with HW.GFX.GMA.Connectors;
+
+use type HW.Word8;
+use type HW.GFX.Port_Private;
+use type HW.GFX.GMA.Connectors.Digital_Port;
+
+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 : Port_Private) return Word32 is
+   begin
+      return (case (Port and PORT_PRIVATE_PORT_MASK) is
+         when PORT_PRIVATE_ANALOG => GMBUS0_PIN_PAIR_SELECT_DAC,
+         when PORT_PRIVATE_LVDS   => GMBUS0_PIN_PAIR_SELECT_LVDS,
+         when PORT_PRIVATE_HDMI_B => GMBUS0_PIN_PAIR_SELECT_DIGI_B,
+         when PORT_PRIVATE_HDMI_C => GMBUS0_PIN_PAIR_SELECT_DIGI_C,
+         when PORT_PRIVATE_HDMI_D => GMBUS0_PIN_PAIR_SELECT_DIGI_D,
+         when others              => GMBUS0_PIN_PAIR_SELECT_NONE);
+   end GMBUS0_PIN_PAIR_SELECT;
+
+   ----------------------------------------------------------------------------
+
+   function Port_Valid (Port : Port_Private) return Boolean is
+   begin
+      return Port <= PORT_PRIVATE_HDMI_D;
+   end Port_Valid;
+
+   ----------------------------------------------------------------------------
+
+   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 or
+                              GMBUS2_GMBUS_ACTIVE)) = 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 : Port_Private; 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
+      Registers.Wait_Unset_Mask (Registers.PCH_GMBUS2, GMBUS2_GMBUS_ACTIVE);
+      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     Port_Private;
+      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_ENABLE_TIMEOUT or
+                        GMBUS1_BUS_CYCLE_STOP 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  => 50);
+
+            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;
+      end if;
+      Length := Transfered;
+
+      Release_GMBUS;
+   end I2C_Read;
+
+end HW.GFX.GMA.I2C;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-i2c.ads b/src/drivers/intel/gma/hw-gfx-gma-i2c.ads
new file mode 100644
index 0000000..8c604b4
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-i2c.ads
@@ -0,0 +1,31 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+
+package HW.GFX.GMA.I2C is
+
+   function Port_Valid (Port : Port_Private) return Boolean;
+
+   procedure I2C_Read
+     (Port     : in     Port_Private;
+      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)
+   with
+      Pre => Port_Valid (Port);
+
+end HW.GFX.GMA.I2C;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-panel.adb b/src/drivers/intel/gma/hw-gfx-gma-panel.adb
new file mode 100644
index 0000000..4c60599
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-panel.adb
@@ -0,0 +1,343 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Config;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Panel
+with
+   Refined_State => (Panel_State => (Delays_US, Power_Cycle_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.
+   --
+   -- 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;
+
+   ----------------------------------------------------------------------------
+
+   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 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));
+
+      Time.New_Timeout_US (0, Power_Cycle_Timer);
+
+      -- Always initialize Delays_US as SPARK will never learn about arrays.
+      Delays_US := Default_EDP_Delays_US;
+      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 Wait then
+         if not Was_On then
+            Time.U_Delay (Delays_US (Power_Up_Delay));
+         end if;
+         Wait_On;
+      end if;
+   end On;
+
+   procedure Wait_On is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      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
+         Time.New_Timeout_US (Delays_US (Power_Cycle_Delay), Power_Cycle_Timer);
+      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/src/drivers/intel/gma/hw-gfx-gma-panel.ads b/src/drivers/intel/gma/hw-gfx-gma-panel.ads
new file mode 100644
index 0000000..ff2ec75
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-panel.ads
@@ -0,0 +1,58 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+
+private package HW.GFX.GMA.Panel
+with
+   Abstract_State => (Panel_State with Part_Of => GMA.State)
+is
+
+   procedure Setup_PP_Sequencer (Default_Delays : Boolean := False)
+   with
+      Global =>
+        (Input  => Time.State,
+         In_Out => Registers.Register_State,
+         Output => Panel_State),
+      Depends =>
+        (Panel_State =>
+           (Time.State, Registers.Register_State, Default_Delays),
+         Registers.Register_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/src/drivers/intel/gma/hw-gfx-gma-pch-fdi.adb b/src/drivers/intel/gma/hw-gfx-gma-pch-fdi.adb
new file mode 100644
index 0000000..51b2a5e
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-pch-fdi.adb
@@ -0,0 +1,139 @@
+with HW.Time;
+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_PORT_WIDTH_SEL_2            : constant := 1 * 2 ** 19;
+   FDI_RX_CTL_FDI_PLL_ENABLE              : constant := 1 * 2 ** 13;
+   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;
+
+   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 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 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;
+
+   ----------------------------------------------------------------------------
+
+   type FDI_Registers is record
+      RX_CTL      : Registers.Registers_Index;
+      RX_MISC     : Registers.Registers_Index;
+      RX_TUSIZE   : 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),
+      PCH.FDI_B => FDI_Registers'
+        (RX_CTL      => Registers.FDI_RXB_CTL,
+         RX_MISC     => Registers.FDI_RX_MISC_B,
+         RX_TUSIZE   => Registers.FDI_RXB_TUSIZE1),
+      PCH.FDI_C => FDI_Registers'
+        (RX_CTL      => Registers.FDI_RXC_CTL,
+         RX_MISC     => Registers.FDI_RX_MISC_C,
+         RX_TUSIZE   => Registers.FDI_RXC_TUSIZE1));
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_Train (Port : PCH.FDI_Port_Type) is
+   begin
+      -- TODO: check DISPIO_CR_TX_BMU_CR4, seems Linux doesn't know it
+
+      Registers.Write
+        (Register => FDI_Regs (Port).RX_MISC,
+         Value    => FDI_RX_MISC_FDI_RX_PWRDN_LANE1 (2) or
+                     FDI_RX_MISC_FDI_RX_PWRDN_LANE0 (2) or
+                     FDI_RX_MISC_TP1_TO_TP2_TIME_48 or
+                     FDI_RX_MISC_FDI_DELAY_90);
+
+      Registers.Write
+        (Register => FDI_Regs (Port).RX_CTL,
+         Value    => FDI_RX_CTL_PORT_WIDTH_SEL_2 or
+                     FDI_RX_CTL_FDI_PLL_ENABLE or
+                     FDI_RX_CTL_ENHANCED_FRAMING_ENABLE);
+      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 Try_Train (Port : PCH.FDI_Port_Type) is
+   begin
+      Registers.Write
+        (Register => FDI_Regs (Port).RX_TUSIZE,
+         Value    => FDI_RX_TUSIZE (64));
+
+      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);
+      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));
+      Time.U_Delay (5);
+   end Try_Train;
+
+   ----------------------------------------------------------------------------
+
+   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);
+
+      if 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/src/drivers/intel/gma/hw-gfx-gma-pch-fdi.ads b/src/drivers/intel/gma/hw-gfx-gma-pch-fdi.ads
new file mode 100644
index 0000000..3b553a7
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-pch-fdi.ads
@@ -0,0 +1,9 @@
+package HW.GFX.GMA.PCH.FDI is
+
+   procedure Pre_Train (Port : PCH.FDI_Port_Type);
+   procedure Try_Train (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/src/drivers/intel/gma/hw-gfx-gma-pch-sideband.adb b/src/drivers/intel/gma/hw-gfx-gma-pch-sideband.adb
new file mode 100644
index 0000000..ed9a445
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-pch-sideband.adb
@@ -0,0 +1,124 @@
+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/src/drivers/intel/gma/hw-gfx-gma-pch-sideband.ads b/src/drivers/intel/gma/hw-gfx-gma-pch-sideband.ads
new file mode 100644
index 0000000..07715cf
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-pch-sideband.ads
@@ -0,0 +1,36 @@
+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/src/drivers/intel/gma/hw-gfx-gma-pch-transcoder.adb b/src/drivers/intel/gma/hw-gfx-gma-pch-transcoder.adb
new file mode 100644
index 0000000..b127cb9
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-pch-transcoder.adb
@@ -0,0 +1,115 @@
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PCH.Transcoder is
+
+   TRANS_CONF_TRANSCODER_ENABLE     : constant := 1 * 2 ** 31;
+   TRANS_CONF_TRANSCODER_STATE      : constant := 1 * 2 ** 30;
+
+   TRANS_CHICKEN2_TIMING_OVERRIDE   : constant := 1 * 2 ** 31;
+
+   ----------------------------------------------------------------------------
+
+   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;
+      CHICKEN2 : Registers.Registers_Index;
+   end record;
+
+   type Transcoder_Registers_Array is
+      array (PCH.FDI_Port_Type) of Transcoder_Registers;
+
+   TRANS : constant Transcoder_Registers_Array := Transcoder_Registers_Array'
+     (PCH.FDI_A   => Transcoder_Registers'
+        (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,
+         CHICKEN2 => Registers.TRANSA_CHICKEN2),
+      PCH.FDI_B   => Transcoder_Registers'
+        (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,
+         CHICKEN2 => Registers.TRANSB_CHICKEN2),
+      PCH.FDI_C   => Transcoder_Registers'
+        (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,
+         CHICKEN2 => Registers.TRANSC_CHICKEN2));
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (Port  : PCH.FDI_Port_Type;
+      Mode  : Mode_Type)
+   is
+      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));
+
+      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));
+
+      Registers.Set_Mask
+        (Register => TRANS (Port).CHICKEN2,
+         Mask     => TRANS_CHICKEN2_TIMING_OVERRIDE);
+
+      Registers.Write
+        (Register => TRANS (Port).CONF,
+         Value    => TRANS_CONF_TRANSCODER_ENABLE);
+   end On;
+
+   procedure Off (Port : PCH.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);
+
+      Registers.Unset_Mask
+        (Register => TRANS (Port).CHICKEN2,
+         Mask     => TRANS_CHICKEN2_TIMING_OVERRIDE);
+   end Off;
+
+end HW.GFX.GMA.PCH.Transcoder;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-pch-transcoder.ads b/src/drivers/intel/gma/hw-gfx-gma-pch-transcoder.ads
new file mode 100644
index 0000000..a2594f4
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-pch-transcoder.ads
@@ -0,0 +1,10 @@
+package HW.GFX.GMA.PCH.Transcoder is
+
+   procedure On
+     (Port  : PCH.FDI_Port_Type;
+      Mode  : Mode_Type);
+
+   procedure Off
+     (Port  : PCH.FDI_Port_Type);
+
+end HW.GFX.GMA.PCH.Transcoder;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-pch-vga.adb b/src/drivers/intel/gma/hw-gfx-gma-pch-vga.adb
new file mode 100644
index 0000000..4ad2239
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-pch-vga.adb
@@ -0,0 +1,172 @@
+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_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;
+
+   PCH_ADPA_TRANSCODER_SELECT_SHIFT : constant :=
+     (case Config.CPU is
+         when Ironlake                       => 30,   -- FIXME: 29 for SNB/IVB
+         when Haswell | Broadwell | Skylake  =>  0);  -- Transcoder_No must be zero
+
+   PCH_ADPA_TRANSCODER_SELECT_MASK : constant :=
+     (case Config.CPU is
+         when Ironlake                       => 16#4000_0000#, -- FIXME: 6 for SNB/IVB
+         when Haswell | Broadwell | Skylake  => 16#0000_0000#);
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (Transcoder_No     : Word32;
+      HSync_Active_High : Boolean;
+      VSync_Active_High : Boolean)
+   is
+      Polarity : Word32 := 0;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if HSync_Active_High then
+         Polarity := Polarity or PCH_ADPA_HSYNC_ACTIVE_HIGH;
+      end if;
+      if VSync_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 or
+                        PCH_ADPA_TRANSCODER_SELECT_MASK,
+         Mask_Set    => PCH_ADPA_DAC_ENABLE or
+                        Shift_Left
+                          (Transcoder_No, PCH_ADPA_TRANSCODER_SELECT_SHIFT) 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
+        ((Word64'(Refclock * 64) / Word64 (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/src/drivers/intel/gma/hw-gfx-gma-pch-vga.ads b/src/drivers/intel/gma/hw-gfx-gma-pch-vga.ads
new file mode 100644
index 0000000..052a378
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-pch-vga.ads
@@ -0,0 +1,12 @@
+package HW.GFX.GMA.PCH.VGA is
+
+   procedure On
+     (Transcoder_No     : Word32;
+      HSync_Active_High : Boolean;
+      VSync_Active_High : Boolean);
+
+   procedure Off;
+
+   procedure Clock_On (Mode : Mode_Type);
+
+end HW.GFX.GMA.PCH.VGA;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-pch.ads b/src/drivers/intel/gma/hw-gfx-gma-pch.ads
new file mode 100644
index 0000000..6c23344
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-pch.ads
@@ -0,0 +1,5 @@
+private package HW.GFX.GMA.PCH is
+
+   type FDI_Port_Type is (FDI_A, FDI_B, FDI_C);
+
+end HW.GFX.GMA.PCH;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_lcpll.ads b/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_lcpll.ads
new file mode 100644
index 0000000..fc3066a
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_lcpll.ads
@@ -0,0 +1,32 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.PLLs.Haswell_LCPLL
+is
+
+   type Fixed_LCPLLs_Array is array (HW.GFX.DP_Bandwidth) of PLLs.PLL_Type;
+
+   Fixed_LCPLLs : constant Fixed_LCPLLs_Array := Fixed_LCPLLs_Array'
+     (DP_Bandwidth_5_4  => PLLs.PLL_Type'(PLLs.HSW_LCPLL, 0),
+      DP_Bandwidth_2_7  => PLLs.PLL_Type'(PLLs.HSW_LCPLL, 1),
+      DP_Bandwidth_1_62 => PLLs.PLL_Type'(PLLs.HSW_LCPLL, 2));
+
+   subtype HSW_Index is PLLs.Index range 0 .. 2;
+   type PLL_Hint_Array is array (HSW_Index) of Word32;
+   PLL_Hint : constant PLL_Hint_Array := PLL_Hint_Array'
+     (0 => 0 * 2 ** 29, 1 => 1 * 2 ** 29, 2 => 2 * 2 ** 29);
+
+end HW.GFX.GMA.PLLs.Haswell_LCPLL;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_spll.adb b/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_spll.adb
new file mode 100644
index 0000000..f54994c
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_spll.adb
@@ -0,0 +1,30 @@
+with HW.Time;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.PLLs.Haswell_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.PLLs.Haswell_SPLL;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_spll.ads b/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_spll.ads
new file mode 100644
index 0000000..3343d15
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_spll.ads
@@ -0,0 +1,16 @@
+private package HW.GFX.GMA.PLLs.Haswell_SPLL is
+
+   type Fixed_LCPLLs_Array is array (DP_Bandwidth) of PLLs.PLL_Type;
+
+   Fixed_FDI_SPLL : constant PLLs.PLL_Type := PLLs.PLL_Type'(PLLs.HSW_SPLL, 0);
+
+   subtype HSW_Index is PLLs.Index range 0 .. 0;
+   type PLL_Hint_Array is array (HSW_Index) of Word32;
+   PLL_Hint : constant PLL_Hint_Array := PLL_Hint_Array'
+     (0 => 3 * 2 ** 29);
+
+   procedure On;
+
+   procedure Off;
+
+end HW.GFX.GMA.PLLs.Haswell_SPLL;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_wrpll.adb b/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_wrpll.adb
new file mode 100644
index 0000000..5be5f4d
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_wrpll.adb
@@ -0,0 +1,353 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Haswell_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 at 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 (HSW_Index) 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
+     (Inst           : in     HSW_Index;
+      Target_Clock   : in     HW.GFX.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 (Inst),
+         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 (Inst));
+      Time.U_Delay (20);
+
+      Success := True;
+   end On;
+
+   procedure Off (Inst : HSW_Index)
+   is
+   begin
+      Registers.Unset_Mask (WRPLL_CTL (Inst), WRPLL_CTL_PLL_ENABLE);
+   end Off;
+
+end HW.GFX.GMA.PLLs.Haswell_WRPLL;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_wrpll.ads b/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_wrpll.ads
new file mode 100644
index 0000000..0a3a784
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls-haswell_wrpll.ads
@@ -0,0 +1,33 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Connectors;
+
+private package HW.GFX.GMA.PLLs.Haswell_WRPLL
+is
+
+   subtype HSW_Index is PLLs.Index range 0 .. 1;
+   type PLL_Hint_Array is array (HSW_Index) of Word32;
+   PLL_Hint : constant PLL_Hint_Array := PLL_Hint_Array'
+     (0 => 4 * 2 ** 29, 1 => 5 * 2 ** 29);
+
+   procedure On
+     (Inst           : in     HSW_Index;
+      Target_Clock   : in     HW.GFX.Frequency_Type;
+      Success        :    out Boolean);
+
+   procedure Off (Inst : HSW_Index);
+
+end HW.GFX.GMA.PLLs.Haswell_WRPLL;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls-ironlake_dpll.adb b/src/drivers/intel/gma/hw-gfx-gma-plls-ironlake_dpll.adb
new file mode 100644
index 0000000..1856c55
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls-ironlake_dpll.adb
@@ -0,0 +1,400 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PLLs.Ironlake_DPLL is
+
+   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;
+
+   type Limits_Array is array (Ironlake_Connectors) of Limits_Type;
+
+   Limits : constant Limits_Array := Limits_Array'
+     (Connectors.VGA => 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),
+      Connectors.HDMI => 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),
+      Connectors.LVDS_Single => 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),
+      Connectors.LVDS_Dual => 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));
+
+   ----------------------------------------------------------------------------
+
+   type Regs is array (IRL_Index) 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_NON_LVDS : 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);
+
+   type DPLL_Mode_Array is array (Ironlake_Connectors) of Word32;
+
+   DPLL_Mode : constant DPLL_Mode_Array := DPLL_Mode_Array'
+     (Connectors.VGA          => MODE_DPLL_NON_LVDS,
+      Connectors.HDMI         => MODE_DPLL_NON_LVDS,
+      Connectors.LVDS_Single  => MODE_DPLL_LVDS,
+      Connectors.LVDS_Dual    => MODE_DPLL_LVDS);
+
+   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.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 (not (Current_Limits.P1_Lower  <= P1  and P1  <= Current_Limits.P1_Upper ), Debug.Put_Line ("P1 out of range."));
+      pragma Debug (    (Current_Limits.P2_Fast   /= P2  and P2  /= Current_Limits.P2_Slow  ), Debug.Put_Line ("P2 out of range."));
+      pragma Debug (not (Current_Limits.P_Lower   <= P   and P   <= Current_Limits.P_Upper  ), Debug.Put_Line ("P out of range."));
+      pragma Debug (not (Current_Limits.M1_Lower  <= M1  and M1  <= Current_Limits.M1_Upper ), Debug.Put_Line ("M1 out of range."));
+      pragma Debug (not (Current_Limits.M2_Lower  <= M2  and M2  <= Current_Limits.M2_Upper ), Debug.Put_Line ("M2 out of range."));
+      -- pragma Debug (not (M2 <= M1                                           ), Debug.Put_Line ("M1 greater thant M2."));
+      pragma Debug (not (Current_Limits.N_Lower   <= N   and N   <= Current_Limits.N_Upper  ), Debug.Put_Line ("N out of range."));
+      pragma Debug (not (Current_Limits.M_Lower   <= M   and M   <= Current_Limits.M_Upper  ), Debug.Put_Line ("M out of range."));
+      pragma Debug (not (Current_Limits.VCO_Lower <= VCO and VCO <= Current_Limits.VCO_Upper), Debug.Put_Line ("VCO out of range."));
+
+      pragma Debug (not (Int64 (Clock_Range'First) <= Dotclock),       Debug.Put_Line ("Dotclock too low."));
+      pragma Debug (not (Int64 (Clock_Range'First) <= Dotclock),       Debug.Put_Int64 (Dotclock));
+      pragma Debug (not (Int64 (Clock_Range'First) <= Dotclock),       Debug.New_Line);
+
+      pragma Debug (not (Dotclock <= Int64 (Clock_Range'Last)),        Debug.Put_Line ("Dotclock too high."));
+      pragma Debug (not (Dotclock <= Int64 (Clock_Range'Last)),        Debug.Put_Int64 (Dotclock));
+      pragma Debug (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
+      (Connector       : in     Ironlake_Connectors;
+       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
+      P2               : P2_Range;
+      Best_Delta       : Int64 := Int64'Last;
+      Current_Delta    : Int64;
+      Current_Clock    : Clock_Type;
+      Registers_Valid  : Boolean;
+   begin
+      Valid      := False;
+      Best_Clock := Invalid_Clock;
+
+      if Target_Dotclock <= Limits (Connector).P2_Threshold then
+         P2 := Limits (Connector).P2_Slow;
+      else
+         P2 := Limits (Connector).P2_Fast;
+      end if;
+
+      for N in N_Range range Limits (Connector).N_Lower .. Limits (Connector).N_Upper
+      loop
+         -- reverse loops as hardware prefers higher values
+         for M1 in reverse M1_Range range Limits (Connector).M1_Lower .. Limits (Connector).M1_Upper
+         loop
+            for M2 in reverse M2_Range range Limits (Connector).M2_Lower .. Limits (Connector).M2_Upper
+            loop
+               for P1 in reverse P1_Range range Limits (Connector).P1_Lower .. Limits (Connector).P1_Upper
+               loop
+                  Verify_Parameters
+                     (N               => N,
+                      M1              => M1,
+                      M2              => M2,
+                      P1              => P1,
+                      P2              => P2,
+                      Reference_Clock => Reference_Clock,
+                      Current_Limits  => Limits (Connector),
+                      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.Put ("Current/Target/Best_Delta: "));
+                     pragma Debug (Debug.Put_Int64 (Current_Clock.Dotclock));
+                     pragma Debug (Debug.Put ("/"));
+                     pragma Debug (Debug.Put_Int64 (Target_Dotclock));
+                     pragma Debug (Debug.Put ("/"));
+                     pragma Debug (Debug.Put_Int64 (Best_Delta));
+                     pragma Debug (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
+     (Inst        : IRL_Index;
+      Connector   : Ironlake_Connectors;
+      Clk         : Clock_Type)
+   with
+      Global => (In_Out => Registers.Register_State),
+      Pre => True,
+      Post => True
+   is
+      FP, Encoded_P1, Encoded_P2 : Word32;
+   begin
+      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 (Inst), FP);
+      Registers.Write (FP1 (Inst), 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 (Inst),
+          Value    => DPLL_Mode (Connector)                          or
+                      Encoded_P2                                     or
+                      Shift_Left (Encoded_P1, DPLL_P1_DIVIDER_SHIFT) or
+                      Encoded_P1);
+   end Program_DPLL;
+
+   procedure On
+     (Inst           : in     IRL_Index;
+      Connector      : in     Ironlake_Connectors;
+      Target_Clock   : in     Clock_Range;
+      Success        :    out Boolean)
+   is
+      Clk : Clock_Type;
+   begin
+      Calculate_Clock_Parameters
+        (Connector         => Connector,
+         Target_Dotclock   => Target_Clock,
+         -- should be, but doesn't has to be always the same (on IRL/SNB/IVB):
+         Reference_Clock   => 120_000_000,
+         Best_Clock        => Clk,
+         Valid             => Success);
+
+      if Success then
+         Program_DPLL (Inst, Connector, Clk);
+
+         Registers.Set_Mask (DPLL (Inst), DPLL_VCO_ENABLE);
+         Time.U_Delay (150);
+      end if;
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off (Inst : IRL_Index)
+   is
+   begin
+      Registers.Unset_Mask (DPLL (Inst), DPLL_VCO_ENABLE);
+   end Off;
+
+end HW.GFX.GMA.PLLs.Ironlake_DPLL;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls-ironlake_dpll.ads b/src/drivers/intel/gma/hw-gfx-gma-plls-ironlake_dpll.ads
new file mode 100644
index 0000000..e98941a
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls-ironlake_dpll.ads
@@ -0,0 +1,36 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Connectors;
+
+private package HW.GFX.GMA.PLLs.Ironlake_DPLL
+is
+
+   subtype Ironlake_Connectors is Connectors.Sub_Type
+      range Connectors.VGA .. Connectors.HDMI;
+
+   subtype IRL_Index is PLLs.Index range 0 .. 1;
+   type PLL_Hint_Array is array (IRL_Index) of Word32;
+   PLL_Hint : constant PLL_Hint_Array := PLL_Hint_Array'(0 => 0, 1 => 1);
+
+   procedure On
+     (Inst           : in     IRL_Index;
+      Connector      : in     Ironlake_Connectors;
+      Target_Clock   : in     HW.GFX.Frequency_Type;
+      Success        :    out Boolean);
+
+   procedure Off (Inst : IRL_Index);
+
+end HW.GFX.GMA.PLLs.Ironlake_DPLL;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls-skylake_dpll.adb b/src/drivers/intel/gma/hw-gfx-gma-plls-skylake_dpll.adb
new file mode 100644
index 0000000..6b2a11e
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls-skylake_dpll.adb
@@ -0,0 +1,355 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Skylake_DPLL is
+
+   -- NOTE: Order of DPLLs is twisted, as DPLL2 (WRPLL1)
+   --       should be selected as last choice.
+
+   type Regs is array (SKL_Index) of Registers.Registers_Index;
+
+   DPLL_CTL : constant Regs := Regs'
+     (0 => Registers.LCPLL2_CTL,
+      1 => Registers.WRPLL_CTL_2,
+      2 => Registers.WRPLL_CTL_1);
+   DPLL_CTL_PLL_ENABLE                 : constant := 1 * 2 ** 31;
+
+   ----------------------------------------------------------------------------
+
+   DPLL_CFGR1 : constant Regs := Regs'
+     (0 => Registers.DPLL1_CFGR1,
+      1 => Registers.DPLL3_CFGR1,
+      2 => Registers.DPLL2_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'
+     (0 => Registers.DPLL1_CFGR2,
+      1 => Registers.DPLL3_CFGR2,
+      2 => Registers.DPLL2_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 (SKL_Index) of Natural;
+   DPLL_CTRL1_SHIFT : constant Shifts := Shifts'(6, 18, 12);
+   DPLL_STATUS_SHIFT : constant Shifts := Shifts'(8, 24, 16);
+
+   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; Inst : SKL_Index) return Word32 is
+   begin
+      return Shift_Left (Value, DPLL_CTRL1_SHIFT (Inst));
+   end DPLL_CTRL1_DPLLx;
+
+   function DPLL_STATUS_DPLLx_LOCK (Inst : SKL_Index) return Word32 is
+   begin
+      return Shift_Left (LOCK, DPLL_STATUS_SHIFT (Inst));
+   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
+     (Inst        : in     SKL_Index;
+      Dsp_Config  : in     Config_Type;
+      Success     :    out Boolean)
+   is
+      Central_Freq   : Central_Frequency;
+      DCO_Freq       : DCO_Frequency;
+      PDiv           : PDiv_Range;
+      QDiv           : QDiv_Range;
+      KDiv           : KDiv_Range;
+   begin
+      if Is_Display_Port (Dsp_Config.Port) then
+         Registers.Unset_And_Set_Mask
+           (Register    => Registers.DPLL_CTRL1,
+            Mask_Unset  => DPLL_CTRL1_DPLLx (HDMI_MODE, Inst) or
+                           DPLL_CTRL1_DPLLx (SSC, Inst) or
+                           DPLL_CTRL1_DPLLx (LINK_RATE_MASK, Inst),
+            Mask_Set    => DPLL_CTRL1_DPLLx (LINK_RATE
+                             (Dsp_Config.DP.Bandwidth), Inst) or
+                           DPLL_CTRL1_DPLLx (OVERRIDE, Inst));
+         Registers.Posting_Read (Registers.DPLL_CTRL1);
+         Success := True;
+      else
+         Calculate_DPLL
+           (Dsp_Config.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, Inst),
+               Mask_Set    => DPLL_CTRL1_DPLLx (HDMI_MODE, Inst) or
+                              DPLL_CTRL1_DPLLx (OVERRIDE, Inst));
+            Registers.Write
+              (Register    => DPLL_CFGR1 (Inst),
+               Value       => DPLL_CFGR1_FREQUENCY_ENABLE or
+                              DPLL_CFGR1_DCO_FRACTION (DCO_Freq) or
+                              DPLL_CFGR1_DCO_INTEGER (DCO_Freq));
+            Registers.Write
+              (Register    => DPLL_CFGR2 (Inst),
+               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 (Inst));
+            Registers.Posting_Read (DPLL_CFGR2 (Inst));
+         end if;
+      end if;
+
+      if Success then
+         Registers.Write
+           (Register => DPLL_CTL (Inst),
+            Value    => DPLL_CTL_PLL_ENABLE);
+         Registers.Wait_Set_Mask
+           (Register => Registers.DPLL_STATUS,
+            Mask     => DPLL_STATUS_DPLLx_LOCK (Inst));
+      end if;
+   end On;
+
+   procedure Off (Inst : SKL_Index) is
+   begin
+      Registers.Unset_Mask (DPLL_CTL (Inst), DPLL_CTL_PLL_ENABLE);
+   end Off;
+
+end HW.GFX.GMA.PLLs.Skylake_DPLL;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls-skylake_dpll.ads b/src/drivers/intel/gma/hw-gfx-gma-plls-skylake_dpll.ads
new file mode 100644
index 0000000..1deed5e
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls-skylake_dpll.ads
@@ -0,0 +1,33 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Skylake_DPLL is
+
+   -- NOTE: Order of DPLLs is twisted, as DPLL2 (WRPLL1)
+   --       should be selected as last choice.
+
+   subtype SKL_Index is PLLs.Index range 0 .. 2;
+   type PLL_Hint_Array is array (SKL_Index) of Word32;
+   PLL_Hint : constant PLL_Hint_Array := PLL_Hint_Array'
+     (0 => 1, 1 => 3, 2 => 2);
+
+   procedure On
+     (Inst        : in     SKL_Index;
+      Dsp_Config  : in     Config_Type;
+      Success     :    out Boolean);
+
+   procedure Off (Inst : SKL_Index);
+
+end HW.GFX.GMA.PLLs.Skylake_DPLL;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls-skylake_dpll0.adb b/src/drivers/intel/gma/hw-gfx-gma-plls-skylake_dpll0.adb
new file mode 100644
index 0000000..38b1f1b
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls-skylake_dpll0.adb
@@ -0,0 +1,50 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Skylake_DPLL0 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.Skylake_DPLL0;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls-skylake_dpll0.ads b/src/drivers/intel/gma/hw-gfx-gma-plls-skylake_dpll0.ads
new file mode 100644
index 0000000..0d75382
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls-skylake_dpll0.ads
@@ -0,0 +1,28 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Skylake_DPLL0 is
+
+   Fixed_DPLL0 : constant PLL_Type := PLL_Type'(PLLs.SKL_DPLL0, 0);
+
+   subtype SKL_Index is PLLs.Index range 0 .. 0;
+   type PLL_Hint_Array is array (SKL_Index) of Word32;
+   PLL_Hint : constant PLL_Hint_Array := PLL_Hint_Array'(0 => 0);
+
+   procedure Check_Link_Rate
+     (Link_Rate   : in     DP_Bandwidth;
+      Success     :    out Boolean);
+
+end HW.GFX.GMA.PLLs.Skylake_DPLL0;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls.adb b/src/drivers/intel/gma/hw-gfx-gma-plls.adb
new file mode 100644
index 0000000..033b677
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls.adb
@@ -0,0 +1,284 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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 HW.GFX.GMA.Config;
+with HW.GFX.GMA.PLLs.Ironlake_DPLL;
+with HW.GFX.GMA.PLLs.Haswell_SPLL;
+with HW.GFX.GMA.PLLs.Haswell_LCPLL;
+with HW.GFX.GMA.PLLs.Haswell_WRPLL;
+with HW.GFX.GMA.PLLs.Skylake_DPLL;
+with HW.GFX.GMA.PLLs.Skylake_DPLL0;
+
+package body HW.GFX.GMA.PLLs
+   with Refined_State => (PLLs_State => PLL_Entries)
+is
+
+   type Count_Range is new Natural range 0 .. 3;
+
+   type PLL_Entry is record
+      Use_Count   : Count_Range;
+      Mode        : HW.GFX.Mode_Type;
+      Link_Rate   : DP_Bandwidth;
+      Used_For_DP : Boolean;
+   end record;
+
+   type PLL_Entry_Array is array (Class, Index) of PLL_Entry;
+
+   PLL_Entries : PLL_Entry_Array;
+
+   procedure Initialize is
+   begin
+      PLL_Entries :=
+        (others =>
+           (others =>
+              (Use_Count   => 0,
+               Mode        => HW.GFX.Invalid_Mode,
+               Link_Rate   => DP_Bandwidth'First,
+               Used_For_DP => False)));
+   end Initialize;
+
+   function Inst_In_Range (PLL : PLL_Type) return Boolean
+   with
+      Post =>
+        (case PLL.Kind is
+            when IRL_DPLL  =>
+               Inst_In_Range'Result = (PLL.Inst in Ironlake_DPLL.IRL_Index),
+            when HSW_SPLL =>
+               Inst_In_Range'Result = (PLL.Inst in Haswell_SPLL.HSW_Index),
+            when HSW_LCPLL =>
+               Inst_In_Range'Result = (PLL.Inst in Haswell_LCPLL.HSW_Index),
+            when HSW_WRPLL =>
+               Inst_In_Range'Result = (PLL.Inst in Haswell_WRPLL.HSW_Index),
+            when SKL_DPLL0 =>
+               Inst_In_Range'Result = (PLL.Inst in Skylake_DPLL0.SKL_Index),
+            when SKL_DPLL =>
+               Inst_In_Range'Result = (PLL.Inst in Skylake_DPLL.SKL_Index))
+   is
+      Result : Boolean;
+   begin
+      pragma Warnings (Off, "explicit membership test may be optimized away",
+                       Reason => "keep it for future code compatibility");
+      case PLL.Kind is
+         when IRL_DPLL  => Result := PLL.Inst in Ironlake_DPLL.IRL_Index;
+         when HSW_SPLL  => Result := PLL.Inst in Haswell_SPLL.HSW_Index;
+         when HSW_LCPLL => Result := PLL.Inst in Haswell_LCPLL.HSW_Index;
+         when HSW_WRPLL => Result := PLL.Inst in Haswell_WRPLL.HSW_Index;
+         when SKL_DPLL0 => Result := PLL.Inst in Skylake_DPLL0.SKL_Index;
+         when SKL_DPLL  => Result := PLL.Inst in Skylake_DPLL.SKL_Index;
+      end case;
+      return Result;
+      pragma Warnings (On, "explicit membership test may be optimized away");
+   end Inst_In_Range;
+
+   procedure Find
+     (PLL         : in out PLL_Type;
+      Dsp_Config  : in     Config_Type;
+      Success     :    out Boolean)
+   with
+      Global => (Input => PLL_Entries),
+      Depends => ((PLL, Success) => (PLL_Entries, PLL, Dsp_Config)),
+      Post => (if Success then (
+                  Inst_In_Range (PLL) and
+                  PLL_Entries (PLL.Kind, PLL.Inst).Use_Count /= Count_Range'Last
+               ))
+   is
+   begin
+      Success := False;
+
+      -- try to find shareable PLL
+      for Idx in Index loop
+         PLL.Inst := Idx;
+         if Inst_In_Range (PLL) and
+            PLL_Entries (PLL.Kind, PLL.Inst).Use_Count /= 0 and
+            PLL_Entries (PLL.Kind, PLL.Inst).Use_Count /= Count_Range'Last and
+            PLL_Entries (PLL.Kind, PLL.Inst).Used_For_DP = Is_Display_Port (Dsp_Config.Port) and
+            ((PLL_Entries (PLL.Kind, PLL.Inst).Used_For_DP and
+              PLL_Entries (PLL.Kind, PLL.Inst).Link_Rate = Dsp_Config.DP.Bandwidth) or
+             (not PLL_Entries (PLL.Kind, PLL.Inst).Used_For_DP and
+              PLL_Entries (PLL.Kind, PLL.Inst).Mode = Dsp_Config.Mode))
+         then
+            pragma Assert
+              (Inst_In_Range (PLL) and
+               PLL_Entries (PLL.Kind, PLL.Inst).Use_Count /= Count_Range'Last);
+            Success := True;
+            return;
+         end if;
+         pragma Loop_Invariant (not Success);
+      end loop;
+
+      -- try to find free PLL
+      for Idx in Index loop
+         PLL.Inst := Idx;
+         if Inst_In_Range (PLL) and
+            PLL_Entries (PLL.Kind, PLL.Inst).Use_Count = 0
+         then
+            pragma Assert
+              (Inst_In_Range (PLL) and
+               PLL_Entries (PLL.Kind, PLL.Inst).Use_Count /= Count_Range'Last);
+            Success := True;
+            return;
+         end if;
+         pragma Loop_Invariant (not Success);
+      end loop;
+   end Find;
+
+   procedure Alloc_Configurable_PLL
+     (Connector   : in     Connectors.Connector_Type;
+      Dsp_Config  : in     Config_Type;
+      PLL         :    out PLL_Type;
+      Success     :    out Boolean)
+   is
+   begin
+      PLL.Inst := Index'First;
+      case Config.CPU is
+         when Ironlake   => PLL.Kind := IRL_DPLL;
+         when Haswell    |
+              Broadwell  => PLL.Kind := HSW_WRPLL;
+         when Skylake    => PLL.Kind := SKL_DPLL;
+      end case;
+
+      Find (PLL, Dsp_Config, Success);
+
+      if Success and PLL_Entries (PLL.Kind, PLL.Inst).Use_Count = 0 then
+         case PLL.Kind is
+            when IRL_DPLL =>
+               if Connectors.Sub (Connector)
+                  in Ironlake_DPLL.Ironlake_Connectors
+               then
+                  Ironlake_DPLL.On
+                    (PLL.Inst, Connectors.Sub (Connector),
+                     Dsp_Config.Mode.Dotclock, Success);
+               end if;
+            when HSW_SPLL  |
+                 HSW_LCPLL |
+                 SKL_DPLL0 =>
+               null;
+            when HSW_WRPLL =>
+               Haswell_WRPLL.On (PLL.Inst, Dsp_Config.Mode.Dotclock, Success);
+            when SKL_DPLL =>
+               Skylake_DPLL.On (PLL.Inst, Dsp_Config, Success);
+         end case;
+      end if;
+   end Alloc_Configurable_PLL;
+
+   procedure Alloc
+     (Connector   : in     Connectors.Connector_Type;
+      Dsp_Config  : in     Config_Type;
+      PLL         :    out PLL_Type;
+      Success     :    out Boolean)
+   is
+      use type HW.GFX.GMA.Connectors.Sub_Type;
+   begin
+      if Config.Has_Haswell_PLLs and then
+         (Connectors.Sub (Connector) = Connectors.DP or
+          Connectors.Sub (Connector) = Connectors.EDP)
+      then
+         PLL := Haswell_LCPLL.Fixed_LCPLLs (Dsp_Config.DP.Bandwidth);
+         Success := PLL_Entries (PLL.Kind, PLL.Inst).Use_Count /= Count_Range'Last;
+      elsif (Config.VGA_Through_FDI and Config.Use_SPLL_For_FDI) and then
+            Connectors.Sub (Connector) = Connectors.VGA
+      then
+         PLL := Haswell_SPLL.Fixed_FDI_SPLL;
+         if PLL_Entries (PLL.Kind, PLL.Inst).Use_Count = 0 then
+            Haswell_SPLL.On;
+         end if;
+         Success := PLL_Entries (PLL.Kind, PLL.Inst).Use_Count /= Count_Range'Last;
+      elsif Config.Has_Skylake_PLLs and then
+            Connectors.Sub (Connector) = Connectors.EDP
+      then
+         PLL := Skylake_DPLL0.Fixed_DPLL0;
+         Skylake_DPLL0.Check_Link_Rate (Dsp_Config.DP.Bandwidth, Success);
+         if not Success then
+            Alloc_Configurable_PLL (Connector, Dsp_Config, PLL, Success);
+         end if;
+      else
+         Alloc_Configurable_PLL (Connector, Dsp_Config, PLL, Success);
+      end if;
+
+      if Success then
+         PLL_Entries (PLL.Kind, PLL.Inst) := PLL_Entry'
+           (Use_Count   => PLL_Entries (PLL.Kind, PLL.Inst).Use_Count + 1,
+            Mode        => Dsp_Config.Mode,
+            Link_Rate   => Dsp_Config.DP.Bandwidth,
+            Used_For_DP => Is_Display_Port (Dsp_Config.Port));
+      end if;
+   end Alloc;
+
+   procedure Free (PLL : PLL_Type)
+   is
+   begin
+      if PLL_Entries (PLL.Kind, PLL.Inst).Use_Count /= 0 then
+         PLL_Entries (PLL.Kind, PLL.Inst).Use_Count :=
+            PLL_Entries (PLL.Kind, PLL.Inst).Use_Count - 1;
+         if Inst_In_Range (PLL) and
+            PLL_Entries (PLL.Kind, PLL.Inst).Use_Count = 0
+         then
+            case PLL.Kind is
+               when IRL_DPLL  => Ironlake_DPLL.Off (PLL.Inst);
+               when HSW_SPLL  => Haswell_SPLL.Off;
+               when HSW_LCPLL => null; -- LCPLL is always shared and enabled.
+               when HSW_WRPLL => Haswell_WRPLL.Off (PLL.Inst);
+               when SKL_DPLL0 => null; -- DPLL0 is always shared and enabled.
+               when SKL_DPLL  => Skylake_DPLL.Off (PLL.Inst);
+            end case;
+         end if;
+      end if;
+   end Free;
+
+   procedure All_Off is
+   begin
+      case Config.CPU is
+         when Ironlake =>
+            for Inst in Ironlake_DPLL.IRL_Index loop
+               Ironlake_DPLL.Off (Inst);
+            end loop;
+         when Haswell | Broadwell =>
+            Haswell_SPLL.Off;
+            for Inst in Haswell_WRPLL.HSW_Index loop
+               Haswell_WRPLL.Off (Inst);
+            end loop;
+         when Skylake =>
+            for Inst in Skylake_DPLL.SKL_Index loop
+               Skylake_DPLL.Off (Inst);
+            end loop;
+      end case;
+   end All_Off;
+
+   function Get_PLL_Hint (PLL : PLL_Type) return Word32
+   is
+      Result : Word32;
+   begin
+      Result := 0;
+      if Inst_In_Range (PLL) then
+         case PLL.Kind is
+            when IRL_DPLL  =>
+               Result := Ironlake_DPLL.PLL_Hint (PLL.Inst);
+            when HSW_SPLL =>
+               Result := Haswell_SPLL.PLL_Hint (PLL.Inst);
+            when HSW_LCPLL =>
+               Result := Haswell_LCPLL.PLL_Hint (PLL.Inst);
+            when HSW_WRPLL =>
+               Result := Haswell_WRPLL.PLL_Hint (PLL.Inst);
+            when SKL_DPLL0 =>
+               Result := Skylake_DPLL0.PLL_Hint (PLL.Inst);
+            when SKL_DPLL =>
+               Result := Skylake_DPLL.PLL_Hint (PLL.Inst);
+         end case;
+      end if;
+      return Result;
+   end Get_PLL_Hint;
+
+end HW.GFX.GMA.PLLs;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-plls.ads b/src/drivers/intel/gma/hw-gfx-gma-plls.ads
new file mode 100644
index 0000000..16ffa67
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-plls.ads
@@ -0,0 +1,59 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Connectors;
+
+private package HW.GFX.GMA.PLLs
+with
+   Abstract_State => (PLLs_State with Part_Of => HW.GFX.GMA.State)
+is
+
+   type PLL_Type is private;
+
+   Null_PLL : constant PLL_Type;
+
+   procedure Initialize
+   with
+      Global => (Output => PLLs_State);
+
+   procedure Alloc
+     (Connector   : in     Connectors.Connector_Type;
+      Dsp_Config  : in     Config_Type;
+      PLL         :    out PLL_Type;
+      Success     :    out Boolean);
+
+   procedure Free (PLL : PLL_Type);
+
+   procedure All_Off;
+
+   function Get_PLL_Hint (PLL : PLL_Type) return Word32;
+
+private
+
+   type Class is
+     (IRL_DPLL,
+      HSW_SPLL, HSW_LCPLL, HSW_WRPLL,
+      SKL_DPLL0, SKL_DPLL);
+
+   type Index is new Natural range 0 .. 2;
+
+   type PLL_Type is record
+      Kind  : Class;
+      Inst  : Index;
+   end record;
+
+   Null_PLL : constant PLL_Type := (IRL_DPLL, 0);
+
+end HW.GFX.GMA.PLLs;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-broadwell.adb b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-broadwell.adb
new file mode 100644
index 0000000..10727e1
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-broadwell.adb
@@ -0,0 +1,114 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Broadwell is
+
+   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));
+
+      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 for pipe " & Pipe'Image (P) & "."));
+         end if;
+      end loop;
+   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));
+
+      Registers.Is_Set_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE, Enabled);
+      if Enabled then
+         GT_Mailbox_Write (DISPLAY_IPS_CONTROL, 0);
+         -- May take up to 42ms.
+         Registers.Wait_Unset_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE);
+
+         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 IPS_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_All_Off is
+   begin
+      PSR_Off;
+      IPS_Off;
+   end Pre_All_Off;
+
+end HW.GFX.GMA.Power_And_Clocks.Broadwell;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-broadwell.ads b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-broadwell.ads
new file mode 100644
index 0000000..2c6bac0
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-broadwell.ads
@@ -0,0 +1,22 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Broadwell is
+
+   procedure PSR_Off;
+
+   procedure Pre_All_Off;
+
+end HW.GFX.GMA.Power_And_Clocks.Broadwell;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-haswell.adb b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-haswell.adb
new file mode 100644
index 0000000..a0a9a57
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-haswell.adb
@@ -0,0 +1,178 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+
+   IPS_CTL_ENABLE          : constant := 1 * 2 ** 31;
+
+   ----------------------------------------------------------------------------
+
+   procedure PSR_Off
+   is
+      Enabled : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      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 PSR_Off;
+
+   procedure IPS_Off
+   is
+      Enabled : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.ULT_CPU then
+         Registers.Is_Set_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE, Enabled);
+         if Enabled then
+            Registers.Unset_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE);
+
+            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/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-haswell.ads b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-haswell.ads
new file mode 100644
index 0000000..284b335
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-haswell.ads
@@ -0,0 +1,26 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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 Pre_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.Haswell;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-ironlake.adb b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-ironlake.adb
new file mode 100644
index 0000000..03c8681
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-ironlake.adb
@@ -0,0 +1,79 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.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;
+
+   PCH_DPLL_SEL_TRANSCODER_C_DPLL_ENABLE        : constant := 1 * 2 ** 11;
+   PCH_DPLL_SEL_TRANSCODER_C_DPLL_SELECT_MASK   : constant := 1 * 2 **  8;
+   PCH_DPLL_SEL_TRANSCODER_C_DPLL_SELECT_A      : constant := 0 * 2 **  8;
+   PCH_DPLL_SEL_TRANSCODER_C_DPLL_SELECT_B      : constant := 1 * 2 **  8;
+   PCH_DPLL_SEL_TRANSCODER_B_DPLL_ENABLE        : constant := 1 * 2 **  7;
+   PCH_DPLL_SEL_TRANSCODER_B_DPLL_SELECT_MASK   : constant := 1 * 2 **  4;
+   PCH_DPLL_SEL_TRANSCODER_B_DPLL_SELECT_A      : constant := 0 * 2 **  4;
+   PCH_DPLL_SEL_TRANSCODER_B_DPLL_SELECT_B      : constant := 1 * 2 **  4;
+   PCH_DPLL_SEL_TRANSCODER_A_DPLL_ENABLE        : constant := 1 * 2 **  3;
+   PCH_DPLL_SEL_TRANSCODER_A_DPLL_SELECT_MASK   : constant := 1 * 2 **  0;
+   PCH_DPLL_SEL_TRANSCODER_A_DPLL_SELECT_A      : constant := 0 * 2 **  0;
+   PCH_DPLL_SEL_TRANSCODER_A_DPLL_SELECT_B      : 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);
+      Time.U_Delay (1);
+      --      always use spread spectrum clock for CPU output
+      Registers.Write
+        (Register => Registers.PCH_DREF_CONTROL,
+         Value    => PCH_DREF_CONTROL_120MHZ_CPU_OUTPUT_SSC or
+                     PCH_DREF_CONTROL_120MHZ_SSC_EN or
+                     PCH_DREF_CONTROL_120MHZ_NONSSC_EN or
+                     PCH_DREF_CONTROL_120MHZ_SSC_MODULATION_EN);
+      Time.U_Delay (20);   -- DMI latency
+
+      -- ILK: setup simple DPLL mapping
+      --      TRANSA <-> DPLLA, TRANSB <-> DPLLB
+      Registers.Unset_And_Set_Mask
+        (Register    => Registers.PCH_DPLL_SEL,
+         Mask_Unset  => PCH_DPLL_SEL_TRANSCODER_C_DPLL_ENABLE or
+                        PCH_DPLL_SEL_TRANSCODER_B_DPLL_SELECT_MASK or
+                        PCH_DPLL_SEL_TRANSCODER_A_DPLL_SELECT_MASK,
+         Mask_Set    => PCH_DPLL_SEL_TRANSCODER_B_DPLL_ENABLE or
+                        PCH_DPLL_SEL_TRANSCODER_B_DPLL_SELECT_B or
+                        PCH_DPLL_SEL_TRANSCODER_A_DPLL_ENABLE or
+                        PCH_DPLL_SEL_TRANSCODER_A_DPLL_SELECT_A);
+   end Initialize;
+
+end HW.GFX.GMA.Power_And_Clocks.Ironlake;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-ironlake.ads b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-ironlake.ads
new file mode 100644
index 0000000..c17e2f1
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-ironlake.ads
@@ -0,0 +1,20 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+
+end HW.GFX.GMA.Power_And_Clocks.Ironlake;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-skylake.adb b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-skylake.adb
new file mode 100644
index 0000000..1806920
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-skylake.adb
@@ -0,0 +1,348 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Broadwell;
+
+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
+      Broadwell.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 := False;
+
+      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);
+
+      Time.New_Timeout_MS (3, CDClk_Change_Timeout);
+      loop
+         GT_Mailbox_Write
+           (MBox        => SKL_PCODE_CDCLK_CONTROL,
+            Value       => SKL_CDCLK_PREPARE_FOR_CHANGE);
+         Registers.Read (Registers.GT_MAILBOX_DATA, MBox_Data0);
+         exit when (MBox_Data0 and SKL_CDCLK_READY_FOR_CHANGE) = SKL_CDCLK_READY_FOR_CHANGE;
+         Time.Timed_Out (CDClk_Change_Timeout, Timed_Out);
+         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/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-skylake.ads b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-skylake.ads
new file mode 100644
index 0000000..542caf9
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks-skylake.ads
@@ -0,0 +1,27 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks.adb b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks.adb
new file mode 100644
index 0000000..21c3653
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks.adb
@@ -0,0 +1,87 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Power_And_Clocks.Skylake;
+with HW.GFX.GMA.Power_And_Clocks.Broadwell;
+with HW.GFX.GMA.Power_And_Clocks.Haswell;
+with HW.GFX.GMA.Power_And_Clocks.Ironlake;
+
+package body HW.GFX.GMA.Power_And_Clocks is
+
+   procedure Pre_All_Off is
+   begin
+      case Config.CPU is
+         when GMA.Ironlake  => null;
+         when GMA.Haswell   => Haswell.Pre_All_Off;
+         when GMA.Broadwell => Broadwell.Pre_All_Off;
+         when GMA.Skylake   => Skylake.Pre_All_Off;
+      end case;
+   end Pre_All_Off;
+
+   procedure Post_All_Off is
+   begin
+      case Config.CPU is
+         when GMA.Ironlake  |
+              GMA.Haswell   |
+              GMA.Broadwell => null;
+         when GMA.Skylake   => Skylake.Post_All_Off;
+      end case;
+   end Post_All_Off;
+
+   procedure Initialize is
+   begin
+      case Config.CPU is
+         when GMA.Ironlake  => Ironlake.Initialize;
+         when GMA.Haswell   |
+              GMA.Broadwell => Haswell.Initialize;
+         when GMA.Skylake   => Skylake.Initialize;
+      end case;
+   end Initialize;
+
+   procedure Power_Set_To (Configs : Configs_Type) is
+   begin
+      case Config.CPU is
+         when GMA.Ironlake  => null;
+         when GMA.Haswell   |
+              GMA.Broadwell => Haswell.Power_Set_To (Configs);
+         when GMA.Skylake   => Skylake.Power_Set_To (Configs);
+      end case;
+   end Power_Set_To;
+
+   procedure Power_Up (Old_Configs, New_Configs : Configs_Type) is
+   begin
+      case Config.CPU is
+         when GMA.Ironlake  => null;
+         when GMA.Haswell   |
+              GMA.Broadwell => Haswell.Power_Up (Old_Configs, New_Configs);
+         when GMA.Skylake   => Skylake.Power_Up (Old_Configs, New_Configs);
+      end case;
+   end Power_Up;
+
+   procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type)
+   is
+   begin
+      case Config.CPU is
+         when GMA.Ironlake  => null;
+         when GMA.Haswell   |
+              GMA.Broadwell =>
+            Haswell.Power_Down (Old_Configs, Tmp_Configs, New_Configs);
+         when GMA.Skylake   =>
+            Skylake.Power_Down (Old_Configs, Tmp_Configs, New_Configs);
+      end case;
+   end Power_Down;
+
+end HW.GFX.GMA.Power_And_Clocks;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks.ads b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks.ads
new file mode 100644
index 0000000..a4a0660
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-power_and_clocks.ads
@@ -0,0 +1,27 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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 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;
diff --git a/src/drivers/intel/gma/hw-gfx-gma-registers.adb b/src/drivers/intel/gma/hw-gfx-gma-registers.adb
new file mode 100644
index 0000000..c82f039
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-registers.adb
@@ -0,0 +1,296 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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 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 (Word32, Registers_Range, 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 (GTT_PTE_Type, GTT_Range, 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;
+
+   ----------------------------------------------------------------------------
+
+   -- Read a specific register
+   procedure Read
+      (Register : in     Registers_Index;
+       Value    :    out Word32;
+       Verbose  : in     Boolean := True)
+   with
+      SPARK_Mode => Off
+   is
+   begin
+      Regs.Read (Value, Register'Enum_Rep);
+
+      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 : in     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)
+   with
+      SPARK_Mode => Off
+   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 (Register'Enum_Rep, 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)));
+
+      Time.New_Timeout_MS (TOut_MS, Timeout);
+      loop
+         Read (Register, Value, Verbose);
+         exit when (Value and Mask) = Mask;
+         Time.Timeout_U_Delay (Timeout, 0, Timed_Out);
+         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)));
+
+      Time.New_Timeout_MS (TOut_MS, Timeout);
+      loop
+         Read (Register, Value, Verbose);
+         exit when (Value and Mask) = 0;
+         Time.Timeout_U_Delay (Timeout, 0, Timed_Out);
+         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 : System.Address)
+   is
+      use System.Storage_Elements;
+   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/src/drivers/intel/gma/hw-gfx-gma-registers.ads b/src/drivers/intel/gma/hw-gfx-gma-registers.ads
new file mode 100644
index 0000000..cc01fb4
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma-registers.ads
@@ -0,0 +1,1097 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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))
+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,
+      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_TXA_CTL,
+      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_TXB_CTL,
+      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,
+      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,
+      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,
+      QUIRK_C2004,
+      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,
+      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,
+      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_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 => "FIXME: 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_TXA_CTL           => 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_TXB_CTL           => 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,
+      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_RXC_CTL           => 16#0f_200c# / Register_Width,
+      FDI_RX_MISC_C         => 16#0f_2010# / 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,
+
+      -- these registers are not actually used -- the definitions provided
+      -- here are just for the benefit of the tool to generate readable
+      -- traces and dumps
+      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,
+
+      -- 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_DSPCLK_GATE_D     => 16#0c_2020# / 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,
+      SHOTPLUG_CTL          => 16#0c_4030# / 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 : System.Address)
+   with
+      Global   => (Output => Address_State),
+      Depends  => (Address_State => Base),
+      Pre      => True,
+      Post     => True;
+
+end HW.GFX.GMA.Registers;
diff --git a/src/drivers/intel/gma/hw-gfx-gma.adb b/src/drivers/intel/gma/hw-gfx-gma.adb
new file mode 100644
index 0000000..d4cf44d
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma.adb
@@ -0,0 +1,440 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Registers;
+with HW.GFX.GMA.Power_And_Clocks;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.PLLs;
+with HW.GFX.GMA.Connectors;
+with HW.GFX.GMA.Display_Controller;
+
+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.PLLs_State, Panel.Panel_State,
+         Cur_Configs, Allocated_PLLs, Initialized),
+      Device_State =>
+        (Registers.Register_State, Registers.GTT_State))
+is
+
+   type PLLs_Type is array (Config_Index) of PLLs.PLL_Type;
+
+   Cur_Configs : Configs_Type;
+   Allocated_PLLs : PLLs_Type;
+   Initialized : Boolean;
+
+   subtype Active_Port_Type is Port_Type range Port_Type'Succ (Disabled) .. Port_Type'Last;
+
+   ----------------------------------------------------------------------------
+
+   function To_Connector
+     (Port : Active_Port_Type)
+      return Connectors.Connector_Type
+   is
+      Result : Connectors.Connector_Type;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      case Port is
+         when Internal => Result := Connectors.Connector_Internal;
+         when Analog   => Result := Connectors.Connector_Analog;
+         when Digital1 => Result := Connectors.Connector_Digital (Connectors.DIGI_B);
+         when Digital2 => Result := Connectors.Connector_Digital (Connectors.DIGI_C);
+         when Digital3 => Result := Connectors.Connector_Digital (Connectors.DIGI_D);
+         when DP1      => Result := Connectors.Connector_DP (Connectors.DIGI_B);
+         when DP2      => Result := Connectors.Connector_DP (Connectors.DIGI_C);
+         when DP3      => Result := Connectors.Connector_DP (Connectors.DIGI_D);
+      end case;
+      return Result;
+   end To_Connector;
+
+   function Is_Display_Port (Port : Port_Type) return Boolean
+   is
+      Result : Boolean;
+   begin
+      if Port = Disabled then
+         Result := False;
+      else
+         case Connectors.Sub (To_Connector (Port)) is
+            when Connectors.DP | Connectors.EDP => Result := True;
+            when others                         => Result := False;
+         end case;
+      end if;
+      return Result;
+   end Is_Display_Port;
+
+   ----------------------------------------------------------------------------
+
+   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 not 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;
+
+   ----------------------------------------------------------------------------
+
+   procedure Auto_Configure (Configs : in out Configs_Type)
+   is
+      Raw_EDID : EDID.Raw_EDID_Data;
+      Success : Boolean;
+
+      Config_Idx : Config_Index;
+
+      function Port_Configured (Port : Port_Type) return Boolean
+      with
+         Pre => True
+      is
+      begin
+         return Configs (Primary).Port    = Port or
+                Configs (Secondary).Port  = Port or
+                Configs (Tertiary).Port   = Port;
+      end Port_Configured;
+      function Free_Config return Boolean
+      with
+         Pre => True
+      is
+      begin
+         return Port_Configured (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?
+
+      -- Check if displays are still connected
+      for I in Config_Index loop
+         if Configs (I).Port /= Disabled then
+            Connectors.Read_EDID
+              (Raw_EDID    => Raw_EDID,
+               Connector   => To_Connector (Configs (I).Port),
+               Success     => Success);
+            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 (Port) then
+            Config_Idx := First_Free_Config;
+
+            -- Need power to probe port
+            if Is_Display_Port (Port) then
+               Configs (Config_Idx).Port := Port;
+               Power_And_Clocks.Power_Up (Cur_Configs, Configs);
+            end if;
+
+            Connectors.Read_EDID
+              (Raw_EDID    => Raw_EDID,
+               Connector   => To_Connector (Port),
+               Success     => Success);
+            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),
+                  DP          => HW.GFX.Default_DP);
+
+               -- XXX: Hack for testing. Use panel fitter
+               --      instead or let caller handle it.
+               Configs (Config_Idx).Framebuffer.Width :=
+                  Int32 (Configs (Config_Idx).Mode.H_Visible);
+               Configs (Config_Idx).Framebuffer.Height :=
+                  Int32 (Configs (Config_Idx).Mode.V_Visible);
+            else
+               Configs (Config_Idx).Port := Disabled;
+            end if;
+         end if;
+      end loop;
+
+      Power_And_Clocks.Power_Set_To (Cur_Configs);
+   end Auto_Configure;
+
+   ----------------------------------------------------------------------------
+
+   procedure Update_Outputs (Configs : Configs_Type)
+   is
+      Success : Boolean;
+      Connector : Connectors.Connector_Type;
+      Old_Config, New_Config : Config_Type;
+      Old_Configs : Configs_Type;
+   begin
+      Old_Configs := Cur_Configs;
+      Power_And_Clocks.Power_Up (Old_Configs, Configs);
+
+      for I in Config_Index
+      loop
+
+         Old_Config := Cur_Configs (I);
+         New_Config := Configs (I);
+
+         -- Connector changed?
+         if Old_Config.Port /= New_Config.Port or
+            Old_Config.Mode /= New_Config.Mode
+         then
+            pragma Debug (Debug.Put_Line ("Port changed:"));
+
+            if Old_Config.Port /= Disabled then
+               Connectors.Pre_Off (To_Connector (Old_Config.Port));
+
+               -- Disable controller
+               Display_Controller.Off
+                 (To_Controller (I), To_Head (I, Old_Config.Port));
+
+               Connectors.Post_Off (To_Connector (Old_Config.Port));
+
+               -- Free PLL
+               PLLs.Free (Allocated_PLLs (I));
+
+               Cur_Configs (I).Port := Disabled;
+            end if;
+
+            if New_Config.Port /= Disabled then
+               Connector := To_Connector (New_Config.Port);
+
+               Connectors.Preferred_Link_Setting
+                 (Dsp_Config  => New_Config,
+                  Connector   => Connector,
+                  Success     => Success);
+
+               while Success loop
+                  pragma Loop_Invariant (New_Config.Port in Active_Port_Type);
+
+                  PLLs.Alloc
+                    (Connector   => Connector,
+                     Dsp_Config  => New_Config,
+                     PLL         => Allocated_PLLs (I),
+                     Success     => Success);
+                  exit when not Success;
+
+                  for Try in 1 .. 2 loop
+                     pragma Loop_Invariant
+                       (New_Config.Port in Active_Port_Type);
+
+                     Connectors.Pre_On
+                       (Connector   => Connector,
+                        Dsp_Config  => New_Config,
+                        PLL_Hint    => PLLs.Get_PLL_Hint (Allocated_PLLs (I)),
+                        Pipe_Hint   => Display_Controller.Get_Pipe_Hint
+                                         (To_Head (I, New_Config.Port)),
+                        Success     => Success);
+
+                     exit when Success;
+                  end loop;
+                  exit when Success;   -- connection established ==> stop loop
+
+                  -- connection failed
+                  PLLs.Free (Allocated_PLLs (I));
+                  Connectors.Next_Link_Setting
+                    (Dsp_Config  => New_Config,
+                     Connector   => Connector,
+                     Success     => Success);
+               end loop;
+
+               if Success then
+                  Display_Controller.On
+                    (Controller  => To_Controller (I),
+                     Connector   => Connector,
+                     Head        => To_Head (I, New_Config.Port),
+                     Dsp_Config  => New_Config);
+
+                  Connectors.Post_On
+                    (Dsp_Config  => New_Config,
+                     Connector   => Connector,
+                     Pipe_Hint   => Display_Controller.Get_Pipe_Hint
+                                      (To_Head (I, New_Config.Port)));
+
+                  Cur_Configs (I) := New_Config;
+               end if;
+
+               if not Success and New_Config.Port = Internal then
+                  Panel.Off;
+               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;
+
+      Power_And_Clocks.Power_Down (Old_Configs, Configs, Cur_Configs);
+
+   end Update_Outputs;
+
+   ----------------------------------------------------------------------------
+
+   procedure Set_Default_MMIO_Base
+   with
+      Global => (Output => Registers.Address_State);
+
+   procedure Set_Default_MMIO_Base
+   with
+      SPARK_Mode => Off
+   is
+   begin
+      Registers.Set_Register_Base
+        (System'To_Address (Config.Default_MMIO_Base));
+   end Set_Default_MMIO_Base;
+
+   procedure Initialize (Write_Delay : Word64)
+   with
+      Refined_Global =>
+        (In_Out =>
+           (Registers.Register_State, Time.State, Port_IO.State),
+         Output =>
+           (Registers.Address_State,
+            PLLs.PLLs_State, Panel.Panel_State,
+            Cur_Configs, Allocated_PLLs, Initialized))
+   is
+   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));
+
+      Allocated_PLLs := (others => PLLs.Null_PLL);
+      Cur_Configs := Configs_Type'
+        (Primary   => Config_Type'
+           (Port        => Disabled,
+            Framebuffer => HW.GFX.Default_FB,
+            Mode        => HW.GFX.Invalid_Mode,
+            DP          => HW.GFX.Default_DP),
+         Secondary => Config_Type'
+           (Port        => Disabled,
+            Framebuffer => HW.GFX.Default_FB,
+            Mode        => HW.GFX.Invalid_Mode,
+            DP          => HW.GFX.Default_DP),
+         Tertiary => Config_Type'
+           (Port        => Disabled,
+            Framebuffer => HW.GFX.Default_FB,
+            Mode        => HW.GFX.Invalid_Mode,
+            DP          => HW.GFX.Default_DP));
+
+      Set_Default_MMIO_Base;
+      Panel.Setup_PP_Sequencer;
+      PLLs.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;
+
+   ----------------------------------------------------------------------------
+
+   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;
+
+end HW.GFX.GMA;
diff --git a/src/drivers/intel/gma/hw-gfx-gma.ads b/src/drivers/intel/gma/hw-gfx-gma.ads
new file mode 100644
index 0000000..e7a564a
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-gma.ads
@@ -0,0 +1,99 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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.Port_IO;
+
+package HW.GFX.GMA
+with
+   Abstract_State =>
+     (State,
+      (Device_State with External))
+is
+
+   type CPU_Type is
+     (Ironlake,   -- or Sandybridge, or Ivybridge
+      Haswell,
+      Broadwell,
+      Skylake);
+
+   type CPU_Variant is (Normal, ULT);
+
+   type Port_Type is
+     (Disabled,
+      Internal,
+      Digital1,
+      Digital2,
+      Digital3,
+      Analog,
+      DP1,
+      DP2,
+      DP3);
+
+   type Config_Type is record
+      Port        : Port_Type;
+      Framebuffer : Framebuffer_Type;
+      Mode        : Mode_Type;
+      DP          : DP_Link;
+   end record;
+   type Config_Index is (Primary, Secondary, Tertiary);
+   type Configs_Type is array (Config_Index) of Config_Type;
+
+   procedure Initialize (Write_Delay : Word64)
+   with
+      Global =>
+        (In_Out => (Device_State, Time.State, Port_IO.State),
+         Output => (State));
+
+   procedure Legacy_VGA_Off;
+
+   procedure Auto_Configure (Configs : in out Configs_Type);
+   procedure Update_Outputs (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);
+
+   function Is_Display_Port (Port : Port_Type) return Boolean;
+
+private
+   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#;
+
+   ----------------------------------------------------------------------------
+
+   PORT_PRIVATE_DP         : constant Port_Private := 16#8000_0000#;
+   PORT_PRIVATE_PORT_MASK  : constant Port_Private := 16#0000_0007#;
+
+   PORT_PRIVATE_ANALOG     : constant Port_Private := 16#0000_0000#;
+   PORT_PRIVATE_DIGI_A     : constant Port_Private := 16#0000_0001#;
+   PORT_PRIVATE_DIGI_B     : constant Port_Private := 16#0000_0002#;
+   PORT_PRIVATE_DIGI_C     : constant Port_Private := 16#0000_0003#;
+   PORT_PRIVATE_DIGI_D     : constant Port_Private := 16#0000_0004#;
+
+   PORT_PRIVATE_LVDS       : constant Port_Private := PORT_PRIVATE_DIGI_A;
+   PORT_PRIVATE_HDMI_B     : constant Port_Private := PORT_PRIVATE_DIGI_B;
+   PORT_PRIVATE_HDMI_C     : constant Port_Private := PORT_PRIVATE_DIGI_C;
+   PORT_PRIVATE_HDMI_D     : constant Port_Private := PORT_PRIVATE_DIGI_D;
+   PORT_PRIVATE_EDP        : constant Port_Private := PORT_PRIVATE_DP or PORT_PRIVATE_DIGI_A;
+   PORT_PRIVATE_DP_B       : constant Port_Private := PORT_PRIVATE_DP or PORT_PRIVATE_DIGI_B;
+   PORT_PRIVATE_DP_C       : constant Port_Private := PORT_PRIVATE_DP or PORT_PRIVATE_DIGI_C;
+   PORT_PRIVATE_DP_D       : constant Port_Private := PORT_PRIVATE_DP or PORT_PRIVATE_DIGI_D;
+
+end HW.GFX.GMA;
diff --git a/src/drivers/intel/gma/hw-gfx-i2c.ads b/src/drivers/intel/gma/hw-gfx-i2c.ads
new file mode 100644
index 0000000..fc6ce8f
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx-i2c.ads
@@ -0,0 +1,23 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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/src/drivers/intel/gma/hw-gfx.ads b/src/drivers/intel/gma/hw-gfx.ads
new file mode 100644
index 0000000..4ec742b
--- /dev/null
+++ b/src/drivers/intel/gma/hw-gfx.ads
@@ -0,0 +1,161 @@
+--
+-- This file is part of the coreboot project.
+--
+-- 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;
+
+use type HW.Pos64;
+use type HW.Word32;
+
+package HW.GFX is
+
+   type Port_Private is new Word32;
+
+   ----------------------------------------------------------------------------
+
+   -- implementation only supports 4800p for now ;-)
+   subtype Width_Type  is Pos32 range 1 .. 4800;
+   subtype Height_Type is Pos32 range 1 .. 7680;
+
+   subtype BPC_Type       is Pos64 range 6 .. 12;
+
+   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 .. 350_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 Internal_Type is (LVDS_Single, LVDS_Dual, Int_EDP);
+
+   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;
+   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);
+
+   M2560x1440_60 : constant Mode_Type := Mode_Type'
+      (60*(2720*1481), 2560, 2608, 2640, 2720, 1440, 1443, 1448, 1481, True, False);
+
+   M1920x1200_60 : constant Mode_Type := Mode_Type'
+      (60*(2080*1235), 1920, 1968, 2000, 2080, 1200, 1203, 1209, 1235, False, False);
+
+   M1920x1080_60 : constant Mode_Type := Mode_Type'
+      (60*(2185*1135), 1920, 2008, 2052, 2185, 1080, 1084, 1089, 1135, False, False);
+
+   M1680x1050_60 : constant Mode_Type := Mode_Type'
+      (60*(2256*1087), 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, False, True);
+
+   M1600x1200_60 : constant Mode_Type := Mode_Type'
+      (60*(2160*1250), 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, True, True);
+
+   M1600x900_60 : constant Mode_Type := Mode_Type'
+      (60*(2010*912), 1600, 1664, 1706, 2010, 900, 903, 906, 912, False, False);
+
+   M1440x900_60 : constant Mode_Type := Mode_Type'
+      (60*(1834*920), 1440, 1488, 1520, 1834, 900, 903, 909, 920, False, False);
+
+   M1366x768_60 : constant Mode_Type := Mode_Type'
+      (60*(1446*788), 1366, 1414, 1446, 1466, 768, 769, 773, 788, False, False);
+
+   M1280x1024_60 : constant Mode_Type := Mode_Type'
+      (60*(1712*1063), 1280, 1368, 1496, 1712, 1024, 1027, 1034, 1063, False, True);
+
+   M1024x768_60 : constant Mode_Type := Mode_Type'
+      (60*(1344*806), 1024, 1048, 1184, 1344, 768, 771, 777, 806, False, False);
+
+   Invalid_Mode : constant Mode_Type := Mode_Type'
+      (Frequency_Type'First, 1, 1, 1, 1, 1, 1, 1, 1, False, False);
+
+end HW.GFX;



More information about the coreboot-gerrit mailing list