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/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;