Initial upstream commit

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

Change-Id: I9f5775208f9df6fb29074bf3bc498f68cb17b3a0
Signed-off-by: Nico Huber <nico.huber@secunet.com>
diff --git a/common/haswell_shared/Makefile.inc b/common/haswell_shared/Makefile.inc
new file mode 100644
index 0000000..45b505b
--- /dev/null
+++ b/common/haswell_shared/Makefile.inc
@@ -0,0 +1,6 @@
+gfxinit-y += hw-gfx-gma-connectors-ddi.adb
+gfxinit-y += hw-gfx-gma-connectors-ddi.ads
+gfxinit-y += hw-gfx-gma-connectors.adb
+gfxinit-y += hw-gfx-gma-port_detect.adb
+gfxinit-y += hw-gfx-gma-power_and_clocks_haswell.adb
+gfxinit-y += hw-gfx-gma-power_and_clocks_haswell.ads
diff --git a/common/haswell_shared/hw-gfx-gma-connectors-ddi.adb b/common/haswell_shared/hw-gfx-gma-connectors-ddi.adb
new file mode 100644
index 0000000..0459681
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-connectors-ddi.adb
@@ -0,0 +1,554 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; version 2 of the License.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.Time;
+with HW.GFX.DP_Training;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.PCH.FDI;
+with HW.GFX.GMA.PCH.Transcoder;
+with HW.GFX.GMA.PCH.VGA;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.DP_Aux_Ch;
+with HW.GFX.GMA.SPLL;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors.DDI is
+
+   DDI_BUF_CTL_BUFFER_ENABLE        : constant :=  1 * 2 ** 31;
+   DDI_BUF_CTL_TRANS_SELECT_MASK    : constant := 15 * 2 ** 24;
+   DDI_BUF_CTL_PORT_REVERSAL        : constant :=  1 * 2 ** 16;
+   DDI_BUF_CTL_IDLE_STATUS          : constant :=  1 * 2 **  7;
+   DDI_BUF_CTL_DDI_A_LANE_CAP       : constant :=  1 * 2 **  4;
+   DDI_BUF_CTL_PORT_WIDTH_MASK      : constant :=  7 * 2 **  1;
+   DDI_BUF_CTL_PORT_WIDTH_1_LANE    : constant :=  0 * 2 **  1;
+   DDI_BUF_CTL_PORT_WIDTH_2_LANES   : constant :=  1 * 2 **  1;
+   DDI_BUF_CTL_PORT_WIDTH_4_LANES   : constant :=  3 * 2 **  1;
+   DDI_BUF_CTL_INIT_DISPLAY_DETECT  : constant :=  1 * 2 **  0;
+
+   subtype DDI_BUF_CTL_TRANS_SELECT_T is Natural range 0 .. 9;
+   function DDI_BUF_CTL_TRANS_SELECT
+     (Sel : DDI_BUF_CTL_TRANS_SELECT_T)
+      return Word32;
+
+   type DDI_BUF_CTL_PORT_WIDTH_T is array (HW.GFX.DP_Lane_Count) of Word32;
+   DDI_BUF_CTL_PORT_WIDTH : constant DDI_BUF_CTL_PORT_WIDTH_T :=
+      DDI_BUF_CTL_PORT_WIDTH_T'
+     (HW.GFX.DP_Lane_Count_1 => DDI_BUF_CTL_PORT_WIDTH_1_LANE,
+      HW.GFX.DP_Lane_Count_2 => DDI_BUF_CTL_PORT_WIDTH_2_LANES,
+      HW.GFX.DP_Lane_Count_4 => DDI_BUF_CTL_PORT_WIDTH_4_LANES);
+
+   DP_TP_CTL_TRANSPORT_ENABLE       : constant := 1 * 2 ** 31;
+   DP_TP_CTL_MODE_SST               : constant := 0 * 2 ** 27;
+   DP_TP_CTL_MODE_MST               : constant := 1 * 2 ** 27;
+   DP_TP_CTL_FORCE_ACT              : constant := 1 * 2 ** 25;
+   DP_TP_CTL_ENHANCED_FRAME_ENABLE  : constant := 1 * 2 ** 18;
+   DP_TP_CTL_FDI_AUTOTRAIN          : constant := 1 * 2 ** 15;
+   DP_TP_CTL_LINK_TRAIN_MASK        : constant := 7 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_PAT1        : constant := 0 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_PAT2        : constant := 1 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_PAT3        : constant := 4 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_IDLE        : constant := 2 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_NORMAL      : constant := 3 * 2 **  8;
+   DP_TP_CTL_SCRAMBLE_DISABLE       : constant := 1 * 2 **  7;
+   DP_TP_CTL_ALT_SCRAMBLER_RESET    : constant := 1 * 2 **  6;
+
+   type DP_TP_CTL_LINK_TRAIN_Array is
+      array (DP_Info.Training_Pattern) of Word32;
+   DP_TP_CTL_LINK_TRAIN : constant DP_TP_CTL_LINK_TRAIN_Array :=
+      DP_TP_CTL_LINK_TRAIN_Array'
+     (DP_Info.TP_1      => DP_TP_CTL_LINK_TRAIN_PAT1 or DP_TP_CTL_SCRAMBLE_DISABLE,
+      DP_Info.TP_2      => DP_TP_CTL_LINK_TRAIN_PAT2 or DP_TP_CTL_SCRAMBLE_DISABLE,
+      DP_Info.TP_3      => DP_TP_CTL_LINK_TRAIN_PAT3 or DP_TP_CTL_SCRAMBLE_DISABLE,
+      DP_Info.TP_Idle   => DP_TP_CTL_LINK_TRAIN_IDLE,
+      DP_Info.TP_None   => DP_TP_CTL_LINK_TRAIN_NORMAL);
+
+   DP_TP_STATUS_MIN_IDLES_SENT      : constant := 1 * 2 ** 25;
+   DP_TP_STATUS_FDI_AUTO_TRAIN_DONE : constant := 1 * 2 ** 12;
+
+   PORT_CLK_SEL_LCPLL2700  : constant := 0 * 2 ** 29; -- not on ULX
+   PORT_CLK_SEL_LCPLL1350  : constant := 1 * 2 ** 29;
+   PORT_CLK_SEL_LCPLL810   : constant := 2 * 2 ** 29;
+   PORT_CLK_SEL_SPLL       : constant := 3 * 2 ** 29;
+   PORT_CLK_SEL_WRPLL1     : constant := 4 * 2 ** 29;
+   PORT_CLK_SEL_WRPLL2     : constant := 5 * 2 ** 29;
+   PORT_CLK_SEL_NONE       : constant := 7 * 2 ** 29;
+
+   type PORT_CLK_SEL_LCPLL_T is array (HW.GFX.DP_Bandwidth) of Word32;
+   PORT_CLK_SEL_LCPLL : constant PORT_CLK_SEL_LCPLL_T :=
+      PORT_CLK_SEL_LCPLL_T'
+     (HW.GFX.DP_Bandwidth_1_62 => PORT_CLK_SEL_LCPLL810,
+      HW.GFX.DP_Bandwidth_2_7  => PORT_CLK_SEL_LCPLL1350,
+      HW.GFX.DP_Bandwidth_5_4  => PORT_CLK_SEL_LCPLL2700);
+
+   type DDI_Registers is record
+      BUF_CTL        : Registers.Registers_Index;
+      DP_TP_CTL      : Registers.Registers_Index;
+      DP_TP_STATUS   : Registers.Registers_Invalid_Index;
+      PORT_CLK_SEL   : Registers.Registers_Index;
+   end record;
+
+   type DDI_Registers_Array is array (Digital_Port) of DDI_Registers;
+
+   DDI_Regs : constant DDI_Registers_Array := DDI_Registers_Array'
+     (DIGI_A => DDI_Registers'
+        (BUF_CTL        => Registers.DDI_BUF_CTL_A,
+         DP_TP_CTL      => Registers.DP_TP_CTL_A,
+         DP_TP_STATUS   => Registers.Invalid_Register,
+         PORT_CLK_SEL   => Registers.PORT_CLK_SEL_DDIA),
+      DIGI_B => DDI_Registers'
+        (BUF_CTL        => Registers.DDI_BUF_CTL_B,
+         DP_TP_CTL      => Registers.DP_TP_CTL_B,
+         DP_TP_STATUS   => Registers.DP_TP_STATUS_B,
+         PORT_CLK_SEL   => Registers.PORT_CLK_SEL_DDIB),
+      DIGI_C => DDI_Registers'
+        (BUF_CTL        => Registers.DDI_BUF_CTL_C,
+         DP_TP_CTL      => Registers.DP_TP_CTL_C,
+         DP_TP_STATUS   => Registers.DP_TP_STATUS_C,
+         PORT_CLK_SEL   => Registers.PORT_CLK_SEL_DDIC),
+      DIGI_D => DDI_Registers'
+        (BUF_CTL        => Registers.DDI_BUF_CTL_D,
+         DP_TP_CTL      => Registers.DP_TP_CTL_D,
+         DP_TP_STATUS   => Registers.DP_TP_STATUS_D,
+         PORT_CLK_SEL   => Registers.PORT_CLK_SEL_DDID),
+      DIGI_E => DDI_Registers'
+        (BUF_CTL        => Registers.DDI_BUF_CTL_E,
+         DP_TP_CTL      => Registers.DP_TP_CTL_E,
+         DP_TP_STATUS   => Registers.DP_TP_STATUS_E,
+         PORT_CLK_SEL   => Registers.PORT_CLK_SEL_DDIE));
+
+   ----------------------------------------------------------------------------
+
+   type Values is array (Digital_Port) of Word32;
+   type Shifts is array (Digital_Port) of Natural;
+
+   DPLL_CTRL2_DDIx_CLOCK_OFF : constant Values := Values'
+     (DIGI_A   => 1 * 2 ** 15,
+      DIGI_B   => 1 * 2 ** 16,
+      DIGI_C   => 1 * 2 ** 17,
+      DIGI_D   => 1 * 2 ** 18,
+      DIGI_E   => 1 * 2 ** 19);
+
+   DPLL_CTRL2_DDIx_SELECT_MASK : constant Values := Values'
+     (DIGI_A   => 3 * 2 **  1,
+      DIGI_B   => 3 * 2 **  4,
+      DIGI_C   => 3 * 2 **  7,
+      DIGI_D   => 3 * 2 ** 10,
+      DIGI_E   => 3 * 2 ** 13);
+   DPLL_CTRL2_DDIx_SELECT_SHIFT : constant Shifts := Shifts'
+     (DIGI_A   =>  1,
+      DIGI_B   =>  4,
+      DIGI_C   =>  7,
+      DIGI_D   => 10,
+      DIGI_E   => 13);
+
+   DPLL_CTRL2_DDIx_SELECT_OVERRIDE : constant Values := Values'
+     (DIGI_A   => 1 * 2 **  0,
+      DIGI_B   => 1 * 2 **  3,
+      DIGI_C   => 1 * 2 **  6,
+      DIGI_D   => 1 * 2 **  9,
+      DIGI_E   => 1 * 2 ** 12);
+
+   ----------------------------------------------------------------------------
+
+   function DDI_BUF_CTL_TRANS_SELECT
+     (Sel : DDI_BUF_CTL_TRANS_SELECT_T)
+      return Word32
+   is
+   begin
+      return Word32 (Sel) * 2 ** 24;
+   end DDI_BUF_CTL_TRANS_SELECT;
+
+   ----------------------------------------------------------------------------
+
+   function Max_V_Swing
+     (Port : Digital_Port)
+      return DP_Info.DP_Voltage_Swing
+   is
+   begin
+      return
+        (if (Config.Has_Low_Voltage_Swing and Config.EDP_Low_Voltage_Swing)
+            and then Port = DIGI_A
+         then
+            DP_Info.VS_Level_3
+         else
+            DP_Info.VS_Level_2);
+   end Max_V_Swing;
+
+   pragma Warnings (GNATprove, Off, "unused variable ""Port""",
+                    Reason => "Needed for a common interface");
+   function Max_Pre_Emph
+     (Port        : Digital_Port;
+      Train_Set   : DP_Info.Train_Set)
+      return DP_Info.DP_Pre_Emph
+   is
+   begin
+      return
+        (case Train_Set.Voltage_Swing is
+            when DP_Info.VS_Level_0 => DP_Info.Emph_Level_3,
+            when DP_Info.VS_Level_1 => DP_Info.Emph_Level_2,
+            when DP_Info.VS_Level_2 => DP_Info.Emph_Level_1,
+            when others             => DP_Info.Emph_Level_0);
+   end Max_Pre_Emph;
+   pragma Warnings (GNATprove, On, "unused variable ""Port""");
+
+   ----------------------------------------------------------------------------
+
+   procedure Set_TP_CTL
+     (Port     : Digital_Port;
+      Link     : DP_Link;
+      Pattern  : DP_Info.Training_Pattern)
+   is
+      DP_TP_CTL_Enhanced_Frame : Word32 := 0;
+   begin
+      if Link.Enhanced_Framing then
+         DP_TP_CTL_Enhanced_Frame := DP_TP_CTL_ENHANCED_FRAME_ENABLE;
+      end if;
+
+      Registers.Write
+        (Register => DDI_Regs (Port).DP_TP_CTL,
+         Value    => DP_TP_CTL_TRANSPORT_ENABLE or
+                     DP_TP_CTL_Enhanced_Frame or
+                     DP_TP_CTL_LINK_TRAIN (Pattern));
+   end Set_TP_CTL;
+
+   procedure Set_Training_Pattern
+     (Port     : Digital_Port;
+      Link     : DP_Link;
+      Pattern  : DP_Info.Training_Pattern)
+   is
+      use type DP_Info.Training_Pattern;
+   begin
+      if Pattern < DP_Info.TP_Idle then
+         Set_TP_CTL (Port, Link, Pattern);
+      else
+         -- send at least 5 idle patterns
+         Set_TP_CTL (Port, Link, DP_Info.TP_Idle);
+
+         -- switch to normal frame delivery
+         if Config.End_EDP_Training_Late and then Port = DIGI_A then
+            null; -- do it later in Post_On procedure
+            -- TODO: if there are problems getting the pipe up,
+            --       wait here some time
+            -- Time.U_Delay (100);
+         else
+            if Port /= DIGI_A then
+               Registers.Wait_Set_Mask
+                 (Register    => DDI_Regs (Port).DP_TP_STATUS,
+                  Mask        => DP_TP_STATUS_MIN_IDLES_SENT);
+            end if;
+            Set_TP_CTL (Port, Link, DP_Info.TP_None);
+         end if;
+      end if;
+   end Set_Training_Pattern;
+
+   procedure Set_Signal_Levels
+     (Port        : Digital_Port;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set)
+   is
+      Was_Enabled : Boolean;
+      Trans_Select : DDI_BUF_CTL_TRANS_SELECT_T;
+   begin
+      case Train_Set.Voltage_Swing is
+         when DP_Info.VS_Level_0 =>
+            case Train_Set.Pre_Emph is
+               when DP_Info.Emph_Level_0  => Trans_Select := 0;
+               when DP_Info.Emph_Level_1  => Trans_Select := 1;
+               when DP_Info.Emph_Level_2  => Trans_Select := 2;
+               when DP_Info.Emph_Level_3  => Trans_Select := 3;
+            end case;
+         when DP_Info.VS_Level_1 =>
+            case Train_Set.Pre_Emph is
+               when DP_Info.Emph_Level_0  => Trans_Select := 4;
+               when DP_Info.Emph_Level_1  => Trans_Select := 5;
+               when DP_Info.Emph_Level_2  => Trans_Select := 6;
+               when others                => Trans_Select := 0;
+            end case;
+         when DP_Info.VS_Level_2 =>
+            case Train_Set.Pre_Emph is
+               when DP_Info.Emph_Level_0  => Trans_Select := 7;
+               when DP_Info.Emph_Level_1  => Trans_Select := 8;
+               when others                => Trans_Select := 0;
+            end case;
+         when DP_Info.VS_Level_3 =>
+            case Train_Set.Pre_Emph is
+               when DP_Info.Emph_Level_0  => Trans_Select := 9;
+               when others                => Trans_Select := 0;
+            end case;
+      end case;
+
+      Registers.Is_Set_Mask
+        (Register => DDI_Regs (Port).BUF_CTL,
+         Mask     => DDI_BUF_CTL_BUFFER_ENABLE,
+         Result   => Was_Enabled);
+
+      -- enable DDI buffer
+      Registers.Unset_And_Set_Mask
+        (Register    => DDI_Regs (Port).BUF_CTL,
+         Mask_Unset  => DDI_BUF_CTL_TRANS_SELECT_MASK or
+                        DDI_BUF_CTL_PORT_REVERSAL or
+                        DDI_BUF_CTL_PORT_WIDTH_MASK,
+         Mask_Set    => DDI_BUF_CTL_BUFFER_ENABLE or
+                        DDI_BUF_CTL_TRANS_SELECT (Trans_Select) or
+                        DDI_BUF_CTL_PORT_WIDTH (Link.Lane_Count));
+      Registers.Posting_Read (DDI_Regs (Port).BUF_CTL);
+
+      if not Was_Enabled then
+         Time.U_Delay (600);  -- wait >= 518us (intel spec)
+      end if;
+   end Set_Signal_Levels;
+
+   ----------------------------------------------------------------------------
+
+   procedure Digital_Off (Port : Digital_Port)
+   is
+      Enabled : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Is_Set_Mask
+        (Register => DDI_Regs (Port).BUF_CTL,
+         Mask     => DDI_BUF_CTL_BUFFER_ENABLE,
+         Result   => Enabled);
+
+      if Enabled then
+         Registers.Unset_Mask
+           (Register => DDI_Regs (Port).BUF_CTL,
+            Mask     => DDI_BUF_CTL_BUFFER_ENABLE);
+      end if;
+
+      Registers.Unset_Mask
+        (Register => DDI_Regs (Port).DP_TP_CTL,
+         Mask     => DP_TP_CTL_TRANSPORT_ENABLE);
+
+      if Enabled then
+         Registers.Wait_Set_Mask
+           (Register => DDI_Regs (Port).BUF_CTL,
+            Mask     => DDI_BUF_CTL_IDLE_STATUS);
+      end if;
+
+      if Config.Has_Per_DDI_Clock_Sel then
+         Registers.Write
+           (Register => DDI_Regs (Port).PORT_CLK_SEL,
+            Value    => PORT_CLK_SEL_NONE);
+      else
+         Registers.Set_Mask
+           (Register => Registers.DPLL_CTRL2,
+            Mask     => DPLL_CTRL2_DDIx_CLOCK_OFF (Port));
+      end if;
+   end Digital_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Train_FDI
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   is
+   begin
+      PCH.FDI.Pre_Train (PCH.FDI_A, Port_Cfg);
+
+      -- always use SPLL for FDI
+      SPLL.On;
+      Registers.Write
+        (Register => DDI_Regs (DIGI_E).PORT_CLK_SEL,
+         Value    => PORT_CLK_SEL_SPLL);
+
+      -- try each preemph/voltage pair twice
+      for Trans2 in Natural range 0 .. DDI_BUF_CTL_TRANS_SELECT_T'Last * 2 + 1
+      loop
+         Registers.Write
+           (Register => DDI_Regs (DIGI_E).DP_TP_CTL,
+            Value    => DP_TP_CTL_TRANSPORT_ENABLE or
+                        DP_TP_CTL_ENHANCED_FRAME_ENABLE or
+                        DP_TP_CTL_FDI_AUTOTRAIN or
+                        DP_TP_CTL_LINK_TRAIN_PAT1);
+
+         Registers.Unset_And_Set_Mask
+           (Register    => DDI_Regs (DIGI_E).BUF_CTL,
+            Mask_Unset  => DDI_BUF_CTL_TRANS_SELECT_MASK or
+                           DDI_BUF_CTL_PORT_REVERSAL or
+                           DDI_BUF_CTL_PORT_WIDTH_MASK,
+            Mask_Set    => DDI_BUF_CTL_BUFFER_ENABLE or
+                           DDI_BUF_CTL_TRANS_SELECT (Trans2 / 2) or
+                           DDI_BUF_CTL_PORT_WIDTH (Port_Cfg.FDI.Lane_Count));
+         Registers.Posting_Read (DDI_Regs (DIGI_E).BUF_CTL);
+         Time.U_Delay (600);  -- wait >= 518us (intel spec)
+
+         PCH.FDI.Auto_Train (PCH.FDI_A);
+         Registers.Is_Set_Mask
+           (Register => DDI_Regs (DIGI_E).DP_TP_STATUS,
+            Mask     => DP_TP_STATUS_FDI_AUTO_TRAIN_DONE,
+            Result   => Success);
+         exit when Success;
+
+         Registers.Unset_Mask
+           (Register => DDI_Regs (DIGI_E).BUF_CTL,
+            Mask     => DDI_BUF_CTL_BUFFER_ENABLE);
+         Registers.Posting_Read (DDI_Regs (DIGI_E).BUF_CTL);
+
+         Registers.Unset_And_Set_Mask
+           (Register    => DDI_Regs (DIGI_E).DP_TP_CTL,
+            Mask_Unset  => DP_TP_CTL_TRANSPORT_ENABLE or
+                           DP_TP_CTL_LINK_TRAIN_MASK,
+            Mask_Set    => DP_TP_CTL_LINK_TRAIN_PAT1);
+         Registers.Posting_Read (DDI_Regs (DIGI_E).DP_TP_CTL);
+
+         Registers.Wait_Set_Mask
+           (Register => DDI_Regs (DIGI_E).BUF_CTL,
+            Mask     => DDI_BUF_CTL_IDLE_STATUS);
+
+         PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Lanes_Off);
+      end loop;
+
+      if Success then
+         -- start normal frame delivery
+         Registers.Write
+           (Register => DDI_Regs (DIGI_E).DP_TP_CTL,
+            Value    => DP_TP_CTL_TRANSPORT_ENABLE or
+                        DP_TP_CTL_ENHANCED_FRAME_ENABLE or
+                        DP_TP_CTL_FDI_AUTOTRAIN or
+                        DP_TP_CTL_LINK_TRAIN_NORMAL);
+      else
+         Registers.Write
+           (Register => DDI_Regs (DIGI_E).PORT_CLK_SEL,
+            Value    => PORT_CLK_SEL_NONE);
+         SPLL.Off;
+
+         PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Clock_Off);
+      end if;
+   end Train_FDI;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_On
+     (Port_Cfg : in     Port_Config;
+      PLL_Hint : in     Word32;
+      Success  :    out Boolean)
+   is
+      function To_DP (Port : Digital_Port) return DP_Port
+      is
+      begin
+         return
+           (case Port is
+               when DIGI_A => DP_A,
+               when DIGI_B => DP_B,
+               when DIGI_C => DP_C,
+               when DIGI_D => DP_D,
+               when others => DP_Port'First);
+      end To_DP;
+      package Training is new DP_Training
+        (TPS3_Supported    => True,
+         T                 => Digital_Port,
+         Aux_T             => DP_Port,
+         Aux_Ch            => DP_Aux_Ch,
+         DP_Info           => DP_Info,
+         To_Aux            => To_DP,
+         Max_V_Swing       => Max_V_Swing,
+         Max_Pre_Emph      => Max_Pre_Emph,
+         Set_Pattern       => Set_Training_Pattern,
+         Set_Signal_Levels => Set_Signal_Levels,
+         Off               => Digital_Off);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Display = VGA then
+         Train_FDI (Port_Cfg, Success);
+      else
+         -- direct configured PLL output to this port
+         if Config.Has_Per_DDI_Clock_Sel then
+            Registers.Write
+              (Register    => DDI_Regs (Port_Cfg.Port).PORT_CLK_SEL,
+               Value       => PLL_Hint);
+         else
+            Registers.Unset_And_Set_Mask
+              (Register    => Registers.DPLL_CTRL2,
+               Mask_Unset  => DPLL_CTRL2_DDIx_CLOCK_OFF (Port_Cfg.Port) or
+                              DPLL_CTRL2_DDIx_SELECT_MASK (Port_Cfg.Port),
+               Mask_Set    => Shift_Left
+                                (PLL_Hint,
+                                 DPLL_CTRL2_DDIx_SELECT_SHIFT (Port_Cfg.Port))
+                              or
+                              DPLL_CTRL2_DDIx_SELECT_OVERRIDE (Port_Cfg.Port));
+         end if;
+
+         if Port_Cfg.Display = DP then
+            Training.Train_DP
+              (Port        => Port_Cfg.Port,
+               Link        => Port_Cfg.DP,
+               Success     => Success);
+         else
+            Success := True;
+         end if;
+      end if;
+   end Pre_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Post_On (Port_Cfg : Port_Config)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Port = DIGI_A then
+         if Config.End_EDP_Training_Late then
+            Registers.Unset_And_Set_Mask
+              (Register    => DDI_Regs (DIGI_A).DP_TP_CTL,
+               Mask_Unset  => DP_TP_CTL_LINK_TRAIN_MASK,
+               Mask_Set    => DP_TP_CTL_LINK_TRAIN_NORMAL);
+         end if;
+      end if;
+
+      case Port_Cfg.Display is
+         when HDMI =>
+            Registers.Unset_And_Set_Mask
+              (Register    => DDI_Regs (Port_Cfg.Port).BUF_CTL,
+               Mask_Unset  => DDI_BUF_CTL_TRANS_SELECT_MASK or
+                              DDI_BUF_CTL_PORT_REVERSAL,
+               Mask_Set    => DDI_BUF_CTL_BUFFER_ENABLE);
+            Time.U_Delay (600);  -- wait >= 518us (intel spec)
+         when VGA =>
+            PCH.VGA.Clock_On (Port_Cfg.Mode);
+            PCH.Transcoder.On (Port_Cfg, PCH.FDI_A, 0);
+            PCH.VGA.On
+              (Port => PCH.FDI_A,
+               Mode => Port_Cfg.Mode);
+         when others =>
+            null;
+      end case;
+   end Post_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off (Port : Digital_Port)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port = DIGI_E then
+         PCH.VGA.Off;
+         PCH.Transcoder.Off (PCH.FDI_A);
+         -- PCH.VGA.Clock_Off; -- Can't tell what Linux does, if anything.
+         PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Rx_Off);
+      end if;
+
+      Digital_Off (Port);
+
+      if Port = DIGI_E then
+         SPLL.Off;
+         PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Clock_Off);
+      end if;
+   end Off;
+
+end HW.GFX.GMA.Connectors.DDI;
diff --git a/common/haswell_shared/hw-gfx-gma-connectors-ddi.ads b/common/haswell_shared/hw-gfx-gma-connectors-ddi.ads
new file mode 100644
index 0000000..337e77b
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-connectors-ddi.ads
@@ -0,0 +1,28 @@
+--
+-- Copyright (C) 2015 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; version 2 of the License.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.GFX.GMA.Registers;
+
+private package HW.GFX.GMA.Connectors.DDI
+is
+
+   procedure Pre_On
+     (Port_Cfg : in     Port_Config;
+      PLL_Hint : in     Word32;
+      Success  :    out Boolean);
+
+   procedure Post_On (Port_Cfg : Port_Config);
+
+   procedure Off (Port : Digital_Port);
+
+end HW.GFX.GMA.Connectors.DDI;
diff --git a/common/haswell_shared/hw-gfx-gma-connectors.adb b/common/haswell_shared/hw-gfx-gma-connectors.adb
new file mode 100644
index 0000000..e53902b
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-connectors.adb
@@ -0,0 +1,93 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; version 2 of the License.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.Connectors.DDI;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors is
+
+   procedure Pre_On
+     (Port_Cfg    : in     Port_Config;
+      PLL_Hint    : in     Word32;
+      Pipe_Hint   : in     Word32;
+      Success     :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      DDI.Pre_On (Port_Cfg, PLL_Hint, Success);
+   end Pre_On;
+
+   procedure Post_On
+     (Port_Cfg : in     Port_Config;
+      PLL_Hint : in     Word32;
+      Success  :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      DDI.Post_On (Port_Cfg);
+
+      if Port_Cfg.Port = DIGI_A then
+         Panel.Backlight_On;
+      end if;
+
+      Success := True;
+   end Post_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_Off (Port_Cfg : Port_Config)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Port_Cfg.Port = DIGI_A then
+         Panel.Backlight_Off;
+         Panel.Off;
+      end if;
+   end Pre_Off;
+
+   procedure Post_Off (Port_Cfg : Port_Config)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      DDI.Off (Port_Cfg.Port);
+   end Post_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_All_Off
+   is
+   begin
+      Panel.Backlight_Off;
+      Panel.Off;
+   end Pre_All_Off;
+
+   procedure Post_All_Off
+   is
+   begin
+      for Port in Digital_Port range DIGI_A .. DIGI_D loop
+         DDI.Off (Port);
+      end loop;
+      if Config.FDI_Port (DIGI_E) then
+         DDI.Off (DIGI_E);
+      end if;
+   end Post_All_Off;
+
+end HW.GFX.GMA.Connectors;
diff --git a/common/haswell_shared/hw-gfx-gma-port_detect.adb b/common/haswell_shared/hw-gfx-gma-port_detect.adb
new file mode 100644
index 0000000..0becd2c
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-port_detect.adb
@@ -0,0 +1,189 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; version 2 of the License.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Port_Detect
+is
+
+   PCH_ADPA_CRT_HPD_CHANNEL_MASK       : constant := 3 * 2 ** 24;
+   PCH_ADPA_CRT_HPD_ENABLE             : constant := 1 * 2 ** 23;
+
+   SFUSE_STRAP_CRT_DAC_CAP_DISABLE     : constant := 1 * 2 **  6;
+
+   HOTPLUG_CTL_DDI_A_HPD_INPUT_ENABLE  : constant := 1 * 2 **  4;
+   HOTPLUG_CTL_DDI_A_HPD_STATUS        : constant := 3 * 2 **  0;
+   HOTPLUG_CTL_DDI_A_HPD_LONG_DETECT   : constant := 1 * 2 **  1;
+
+   SHOTPLUG_CTL_DETECT_MASK            : constant := 16#0303_0303#;
+
+   type Digital_Port_Value is array (Digital_Port) of Word32;
+   DDI_PORT_DETECTED : constant Digital_Port_Value :=
+     (DIGI_B => 1 * 2 **  2,
+      DIGI_C => 1 * 2 **  1,
+      DIGI_D => 1 * 2 **  0,
+      DIGI_A => 1 * 2 **  0,
+      others => 0);
+   SHOTPLUG_CTL_HPD_INPUT_ENABLE : constant Digital_Port_Value :=
+     (DIGI_B => 1 * 2 **  4,
+      DIGI_C => 1 * 2 ** 12,
+      DIGI_D => 1 * 2 ** 20,
+      DIGI_A => 1 * 2 ** 28,
+      others => 0);
+   SHOTPLUG_CTL_HPD_STATUS : constant Digital_Port_Value :=
+     (DIGI_B => 3 * 2 **  0,
+      DIGI_C => 3 * 2 **  8,
+      DIGI_D => 3 * 2 ** 16,
+      DIGI_A => 3 * 2 ** 24,
+      others => 0);
+   SHOTPLUG_CTL_LONG_DETECT : constant Digital_Port_Value :=
+     (DIGI_B => 1 * 2 **  1,
+      DIGI_C => 1 * 2 **  9,
+      DIGI_D => 1 * 2 ** 17,
+      DIGI_A => 1 * 2 ** 25,
+      others => 0);
+
+   procedure Initialize
+   is
+      DAC_Disabled,
+      Internal_Detected,
+      DDI_Detected : Boolean;
+
+      Last_Digital_Port : constant Digital_Port :=
+        (if Config.Has_DDI_D then DIGI_D else DIGI_C);
+
+      subtype Ext_Digital_Port is
+         Digital_Port range DIGI_B .. DIGI_D;
+      type Digital_Port_To_GMA_Port is array (Ext_Digital_Port) of Port_Type;
+      To_HDMI_Port : constant Digital_Port_To_GMA_Port :=
+        (DIGI_B => Digital1,
+         DIGI_C => Digital2,
+         DIGI_D => Digital3);
+      To_DP_Port : constant Digital_Port_To_GMA_Port :=
+        (DIGI_B => DP1,
+         DIGI_C => DP2,
+         DIGI_D => DP3);
+   begin
+      if Config.Has_PCH_DAC then
+         -- PCH_DAC (_A)
+         Registers.Is_Set_Mask
+           (Register => Registers.SFUSE_STRAP,
+            Mask     => SFUSE_STRAP_CRT_DAC_CAP_DISABLE,
+            Result   => DAC_Disabled);
+         if not DAC_Disabled then
+            Registers.Set_Mask
+              (Register => Registers.PCH_ADPA,
+               Mask     => PCH_ADPA_CRT_HPD_CHANNEL_MASK or   -- clear status
+                           PCH_ADPA_CRT_HPD_ENABLE);
+         end if;
+         Config.Valid_Port (Analog) := not DAC_Disabled;
+      end if;
+
+      if Config.Internal_Is_EDP then
+         -- DDI_A
+         Registers.Is_Set_Mask
+           (Register => Registers.DDI_BUF_CTL_A,
+            Mask     => DDI_PORT_DETECTED (DIGI_A),
+            Result   => Internal_Detected);
+         if Internal_Detected then
+            if Config.Has_HOTPLUG_CTL then
+               Registers.Set_Mask
+                 (Register => Registers.HOTPLUG_CTL,
+                  Mask     => HOTPLUG_CTL_DDI_A_HPD_INPUT_ENABLE or
+                              HOTPLUG_CTL_DDI_A_HPD_STATUS);   -- clear status
+               if Config.Has_SHOTPLUG_CTL_A then
+                  -- Have to enable south hotplug too on SoCs.
+                  Registers.Unset_And_Set_Mask
+                    (Register    => Registers.SHOTPLUG_CTL,
+                     Mask_Unset  => SHOTPLUG_CTL_DETECT_MASK,
+                     Mask_Set    => SHOTPLUG_CTL_HPD_INPUT_ENABLE (DIGI_A));
+               end if;
+            else
+               Registers.Unset_And_Set_Mask
+                 (Register    => Registers.SHOTPLUG_CTL,
+                  Mask_Unset  => SHOTPLUG_CTL_DETECT_MASK,
+                  Mask_Set    => SHOTPLUG_CTL_HPD_INPUT_ENABLE (DIGI_A) or
+                                 SHOTPLUG_CTL_HPD_STATUS (DIGI_A));  -- clear
+            end if;
+         end if;
+      else
+         Internal_Detected := False;
+      end if;
+      Config.Valid_Port (Internal) := Internal_Detected;
+
+      -- DDI_[BCD]
+      for Port in Ext_Digital_Port range DIGI_B .. Last_Digital_Port loop
+         Registers.Is_Set_Mask
+           (Register => Registers.SFUSE_STRAP,
+            Mask     => DDI_PORT_DETECTED (Port),
+            Result   => DDI_Detected);
+         Config.Valid_Port (To_HDMI_Port (Port)) :=
+            Config.Valid_Port (To_HDMI_Port (Port)) and DDI_Detected;
+         Config.Valid_Port (To_DP_Port (Port)) :=
+            Config.Valid_Port (To_DP_Port (Port)) and DDI_Detected;
+
+         if DDI_Detected then
+            Registers.Unset_And_Set_Mask
+              (Register    => Registers.SHOTPLUG_CTL,
+               Mask_Unset  => SHOTPLUG_CTL_DETECT_MASK,
+               Mask_Set    => SHOTPLUG_CTL_HPD_INPUT_ENABLE (Port) or
+                              SHOTPLUG_CTL_HPD_STATUS (Port)); -- clear status
+         else
+            Registers.Unset_Mask
+              (Register => Registers.SHOTPLUG_CTL,
+               Mask     => SHOTPLUG_CTL_DETECT_MASK or
+                           SHOTPLUG_CTL_HPD_INPUT_ENABLE (Port));
+         end if;
+      end loop;
+   end Initialize;
+
+   procedure Hotplug_Detect (Port_Cfg : in Port_Config; Detected : out Boolean)
+   is
+      Ctl32 : Word32;
+   begin
+      if Port_Cfg.Display = VGA then
+         Registers.Read (Registers.PCH_ADPA, Ctl32, Verbose => False);
+         Ctl32 := Ctl32 and PCH_ADPA_CRT_HPD_CHANNEL_MASK;
+         Detected := Ctl32 = PCH_ADPA_CRT_HPD_CHANNEL_MASK;
+         if Ctl32 /= 0 then
+            Registers.Set_Mask
+              (Register => Registers.PCH_ADPA,
+               Mask     => PCH_ADPA_CRT_HPD_CHANNEL_MASK);
+         end if;
+      elsif Config.Has_HOTPLUG_CTL and then Port_Cfg.Port = DIGI_A then
+         Registers.Read (Registers.HOTPLUG_CTL, Ctl32, Verbose => False);
+         Detected := (Ctl32 and HOTPLUG_CTL_DDI_A_HPD_LONG_DETECT) /= 0;
+
+         if (Ctl32 and HOTPLUG_CTL_DDI_A_HPD_STATUS) /= 0 then
+            Registers.Set_Mask
+              (Register => Registers.HOTPLUG_CTL,
+               Mask     => HOTPLUG_CTL_DDI_A_HPD_STATUS);
+         end if;
+      elsif Port_Cfg.Port in DIGI_A .. DIGI_D then
+         Registers.Read (Registers.SHOTPLUG_CTL, Ctl32, Verbose => False);
+         Detected :=
+           (Ctl32 and SHOTPLUG_CTL_LONG_DETECT (Port_Cfg.Port)) /= 0;
+
+         if (Ctl32 and SHOTPLUG_CTL_HPD_STATUS (Port_Cfg.Port)) /= 0 then
+            Registers.Unset_And_Set_Mask
+              (Register    => Registers.SHOTPLUG_CTL,
+               Mask_Unset  => SHOTPLUG_CTL_DETECT_MASK,
+               Mask_Set    => SHOTPLUG_CTL_HPD_STATUS (Port_Cfg.Port));
+         end if;
+      else
+         Detected := False;
+      end if;
+   end Hotplug_Detect;
+
+end HW.GFX.GMA.Port_Detect;
diff --git a/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.adb b/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.adb
new file mode 100644
index 0000000..b46c29d
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.adb
@@ -0,0 +1,235 @@
+--
+-- Copyright (C) 2014-2016 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; version 2 of the License.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with GNAT.Source_Info;
+
+with HW.Time;
+with HW.Debug;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Power_And_Clocks_Haswell is
+
+   PWR_WELL_CTL_ENABLE_REQUEST   : constant := 1 * 2 ** 31;
+   PWR_WELL_CTL_DISABLE_REQUEST  : constant := 0 * 2 ** 31;
+   PWR_WELL_CTL_STATE_ENABLED    : constant := 1 * 2 ** 30;
+
+   ----------------------------------------------------------------------------
+
+   SRD_CTL_ENABLE          : constant := 1 * 2 ** 31;
+   SRD_STATUS_STATE_MASK   : constant := 7 * 2 ** 29;
+
+   type Pipe is (EDP, A, B, C);
+   type SRD_Regs is record
+      CTL     : Registers.Registers_Index;
+      STATUS  : Registers.Registers_Index;
+   end record;
+   type SRD_Per_Pipe_Regs is array (Pipe) of SRD_Regs;
+   SRD : constant SRD_Per_Pipe_Regs := SRD_Per_Pipe_Regs'
+     (A     => SRD_Regs'
+        (CTL      => Registers.SRD_CTL_A,
+         STATUS   => Registers.SRD_STATUS_A),
+      B     => SRD_Regs'
+        (CTL      => Registers.SRD_CTL_B,
+         STATUS   => Registers.SRD_STATUS_B),
+      C     => SRD_Regs'
+        (CTL      => Registers.SRD_CTL_C,
+         STATUS   => Registers.SRD_STATUS_C),
+      EDP   => SRD_Regs'
+        (CTL      => Registers.SRD_CTL_EDP,
+         STATUS   => Registers.SRD_STATUS_EDP));
+
+   ----------------------------------------------------------------------------
+
+   IPS_CTL_ENABLE          : constant := 1 * 2 ** 31;
+   DISPLAY_IPS_CONTROL     : constant := 16#19#;
+
+   GT_MAILBOX_READY        : constant := 1 * 2 ** 31;
+
+   ----------------------------------------------------------------------------
+
+   procedure PSR_Off
+   is
+      Enabled : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.Has_Per_Pipe_SRD then
+         for P in Pipe loop
+            Registers.Is_Set_Mask (SRD (P).CTL, SRD_CTL_ENABLE, Enabled);
+            if Enabled then
+               Registers.Unset_Mask (SRD (P).CTL, SRD_CTL_ENABLE);
+               Registers.Wait_Unset_Mask (SRD (P).STATUS, SRD_STATUS_STATE_MASK);
+
+               pragma Debug (Debug.Put_Line ("Disabled PSR."));
+            end if;
+         end loop;
+      else
+         Registers.Is_Set_Mask (Registers.SRD_CTL, SRD_CTL_ENABLE, Enabled);
+         if Enabled then
+            Registers.Unset_Mask (Registers.SRD_CTL, SRD_CTL_ENABLE);
+            Registers.Wait_Unset_Mask (Registers.SRD_STATUS, SRD_STATUS_STATE_MASK);
+
+            pragma Debug (Debug.Put_Line ("Disabled PSR."));
+         end if;
+      end if;
+   end PSR_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure GT_Mailbox_Write (MBox : Word32; Value : Word32) is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
+      Registers.Write (Registers.GT_MAILBOX_DATA, Value);
+      Registers.Write (Registers.GT_MAILBOX, GT_MAILBOX_READY or MBox);
+
+      Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
+      Registers.Write (Registers.GT_MAILBOX_DATA, 0);
+   end GT_Mailbox_Write;
+
+   procedure IPS_Off
+   is
+      Enabled : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.Has_IPS then
+         Registers.Is_Set_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE, Enabled);
+         if Enabled then
+            if Config.Has_IPS_CTL_Mailbox then
+               GT_Mailbox_Write (DISPLAY_IPS_CONTROL, 0);
+               -- May take up to 42ms.
+               Registers.Wait_Unset_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE);
+            else
+               Registers.Unset_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE);
+            end if;
+
+            pragma Debug (Debug.Put_Line ("Disabled IPS."));
+            -- We have to wait until the next vblank here.
+            -- 20ms should be enough.
+            Time.M_Delay (20);
+         end if;
+      end if;
+   end IPS_Off;
+
+   ----------------------------------------------------------------------------
+
+   procedure PDW_Off
+   is
+      Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+      Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+      Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+      Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); --  Result for debugging only
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); --  Result for debugging only
+
+      if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+          PWR_WELL_CTL_ENABLE_REQUEST) /= 0
+      then
+         Registers.Wait_Set_Mask
+           (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
+      end if;
+
+      if (Ctl1 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
+         Registers.Write (Registers.PWR_WELL_CTL_BIOS, PWR_WELL_CTL_DISABLE_REQUEST);
+      end if;
+
+      if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
+         Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_DISABLE_REQUEST);
+      end if;
+   end PDW_Off;
+
+   procedure PDW_On
+   is
+      Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+      Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+      Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+      Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); --  Result for debugging only
+      pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); --  Result for debugging only
+
+      if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+          PWR_WELL_CTL_ENABLE_REQUEST) = 0
+      then
+         Registers.Wait_Unset_Mask
+           (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
+      end if;
+
+      if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) = 0 then
+         Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_ENABLE_REQUEST);
+         Registers.Wait_Set_Mask
+           (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
+      end if;
+   end PDW_On;
+
+   function Need_PDW (Checked_Configs : Configs_Type) return Boolean is
+   begin
+      return (Checked_Configs (Primary).Port /= Disabled and
+              Checked_Configs (Primary).Port /= Internal) or
+             Checked_Configs (Secondary).Port /= Disabled or
+             Checked_Configs (Tertiary).Port /= Disabled;
+   end Need_PDW;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_All_Off is
+   begin
+      -- HSW: disable panel self refresh (PSR) on eDP if enabled
+         -- wait for PSR idling
+      PSR_Off;
+      IPS_Off;
+   end Pre_All_Off;
+
+   procedure Initialize is
+   begin
+      -- HSW: disable power down well
+      PDW_Off;
+   end Initialize;
+
+   procedure Power_Set_To (Configs : Configs_Type) is
+   begin
+      if Need_PDW (Configs) then
+         PDW_On;
+      else
+         PDW_Off;
+      end if;
+   end Power_Set_To;
+
+   procedure Power_Up (Old_Configs, New_Configs : Configs_Type) is
+   begin
+      if not Need_PDW (Old_Configs) and Need_PDW (New_Configs) then
+         PDW_On;
+      end if;
+   end Power_Up;
+
+   procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type)
+   is
+   begin
+      if (Need_PDW (Old_Configs) or Need_PDW (Tmp_Configs)) and
+         not Need_PDW (New_Configs)
+      then
+         PDW_Off;
+      end if;
+   end Power_Down;
+
+end HW.GFX.GMA.Power_And_Clocks_Haswell;
diff --git a/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.ads b/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.ads
new file mode 100644
index 0000000..84cf889
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.ads
@@ -0,0 +1,27 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; version 2 of the License.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+private package HW.GFX.GMA.Power_And_Clocks_Haswell is
+
+   procedure PSR_Off;
+
+   procedure Pre_All_Off;
+   procedure Post_All_Off is null;
+
+   procedure Initialize;
+
+   procedure Power_Set_To (Configs : Configs_Type);
+   procedure Power_Up (Old_Configs, New_Configs : Configs_Type);
+   procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type);
+
+end HW.GFX.GMA.Power_And_Clocks_Haswell;