gma tgl: Add connector programming

This patch adds support for enabling displays on both combo PHY ports
and Type-C ports over DP-Alt mode.

Verified eDP, HDMI (not Type-C),  and DP Alt mode on google/delbin.

Change-Id: I908e8bef8451d21eecde9ce6defddc2b3df7f738
Signed-off-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: https://review.sourcearcade.org/c/libgfxinit/+/469
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Tested-by: Nico Huber <nico.h@gmx.de>
diff --git a/common/tigerlake/hw-gfx-gma-connectors-combo_phy.adb b/common/tigerlake/hw-gfx-gma-connectors-combo_phy.adb
new file mode 100644
index 0000000..057f221
--- /dev/null
+++ b/common/tigerlake/hw-gfx-gma-connectors-combo_phy.adb
@@ -0,0 +1,569 @@
+--
+-- Copyright (C) 2022 Google, LLC
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.GFX.DP_Training;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.DP_Aux_Ch;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors.Combo_Phy is
+
+   DDI_BUF_CTL_BUFFER_ENABLE        : constant :=     1 * 2 ** 31;
+   DDI_BUF_CTL_TRANS_SELECT_MASK    : constant := 16#f# * 2 ** 24;
+   DDI_BUF_CTL_PORT_REVERSAL        : constant :=     1 * 2 ** 16;
+   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_IDLE_STATUS          : constant :=     1 * 2 **  7;
+
+   DDI_BUF_CTL : constant array (Combo_Port) of Registers.Registers_Index :=
+     (DIGI_A  => Registers.DDI_BUF_CTL_A,
+      DIGI_B  => Registers.DDI_BUF_CTL_B,
+      DIGI_C  => Registers.DDI_BUF_CTL_C);
+
+   DDI_BUF_CTL_PORT_WIDTH : constant array (DP_Lane_Count) of Word32 :=
+     (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);
+
+   type Port_Regs_Record is record
+      PORT_TX_DW5_LN0 : Registers.Registers_Index;
+      PORT_TX_DW5_GRP : Registers.Registers_Index;
+      PORT_TX_DW7_LN0 : Registers.Registers_Index;
+      PORT_TX_DW7_GRP : Registers.Registers_Index;
+      PORT_TX_DW2_LN0 : Registers.Registers_Index;
+      PORT_TX_DW2_GRP : Registers.Registers_Index;
+      PORT_TX_DW4_LN0 : Registers.Registers_Index;
+      PORT_TX_DW4_LN1 : Registers.Registers_Index;
+      PORT_TX_DW4_LN2 : Registers.Registers_Index;
+      PORT_TX_DW4_LN3 : Registers.Registers_Index;
+      PORT_TX_DW4_GRP : Registers.Registers_Index;
+      PORT_PCS_DW1_LN0 : Registers.Registers_Index;
+      PORT_PCS_DW1_GRP : Registers.Registers_Index;
+      PORT_CL_DW5 : Registers.Registers_Index;
+      PORT_CL_DW10 : Registers.Registers_Index;
+      DDI_BUF_CTL : Registers.Registers_Index;
+   end record;
+
+   Port_Regs : constant array (Combo_Port) of Port_Regs_Record :=
+     (DIGI_A =>
+       (Registers.PORT_TX_DW5_LN0_A, Registers.PORT_TX_DW5_GRP_A,
+        Registers.PORT_TX_DW7_LN0_A, Registers.PORT_TX_DW7_GRP_A,
+        Registers.PORT_TX_DW2_LN0_A, Registers.PORT_TX_DW2_GRP_A,
+        Registers.PORT_TX_DW4_LN0_A, Registers.PORT_TX_DW4_LN1_A,
+           Registers.PORT_TX_DW4_LN2_A, Registers.PORT_TX_DW4_LN3_A,
+           Registers.PORT_TX_DW4_GRP_A,
+        Registers.PORT_PCS_DW1_LN0_A, Registers.PORT_PCS_DW1_GRP_A,
+        Registers.PORT_CL_DW5_A,
+        Registers.PORT_CL_DW10_A,
+        Registers.DDI_BUF_CTL_A),
+      DIGI_B =>
+        (Registers.PORT_TX_DW5_LN0_B, Registers.PORT_TX_DW5_GRP_B,
+         Registers.PORT_TX_DW7_LN0_B, Registers.PORT_TX_DW7_GRP_B,
+         Registers.PORT_TX_DW2_LN0_B, Registers.PORT_TX_DW2_GRP_B,
+         Registers.PORT_TX_DW4_LN0_B, Registers.PORT_TX_DW4_LN1_B,
+            Registers.PORT_TX_DW4_LN2_B, Registers.PORT_TX_DW4_LN3_B,
+            Registers.PORT_TX_DW4_GRP_B,
+         Registers.PORT_PCS_DW1_LN0_B, Registers.PORT_PCS_DW1_GRP_B,
+         Registers.PORT_CL_DW5_B,
+         Registers.PORT_CL_DW10_B,
+         Registers.DDI_BUF_CTL_B),
+      DIGI_C =>
+        (Registers.PORT_TX_DW5_LN0_C, Registers.PORT_TX_DW5_GRP_C,
+         Registers.PORT_TX_DW7_LN0_C, Registers.PORT_TX_DW7_GRP_C,
+         Registers.PORT_TX_DW2_LN0_C, Registers.PORT_TX_DW2_GRP_C,
+         Registers.PORT_TX_DW4_LN0_C, Registers.PORT_TX_DW4_LN1_C,
+            Registers.PORT_TX_DW4_LN2_C, Registers.PORT_TX_DW4_LN3_C,
+            Registers.PORT_TX_DW4_GRP_C,
+         Registers.PORT_PCS_DW1_LN0_C, Registers.PORT_PCS_DW1_GRP_C,
+         Registers.PORT_CL_DW5_C,
+         Registers.PORT_CL_DW10_C,
+         Registers.DDI_BUF_CTL_C));
+
+   type Lanes is (LN0, LN1, LN2, LN3);
+   function PORT_TX_DW4 (Lane : Lanes; Port : Combo_Port)
+      return Registers.Registers_Index
+   is (case Lane is
+       when LN0 => Port_Regs (Port).PORT_TX_DW4_LN0,
+       when LN1 => Port_Regs (Port).PORT_TX_DW4_LN1,
+       when LN2 => Port_Regs (Port).PORT_TX_DW4_LN2,
+       when LN3 => Port_Regs (Port).PORT_TX_DW4_LN3);
+
+   PORT_PCS_DW1_LN0_COMMON_KEEPER : constant := 1 * 2 ** 26;
+
+   type Post_Cursor is new Natural range 0 .. 63;
+   function PORT_TX_DW4_POST_CURSOR1 (P : Post_Cursor) return Word32 is
+      (Shift_Left (Word32 (P), 12));
+
+   type Cursor_Coeff is new Natural range 0 .. 63;
+   function PORT_TX_DW4_CURSOR_COEFF (C : Cursor_Coeff) return Word32 is
+      (Word32 (C));
+
+   type N_Scalar is new Natural range 0 .. 127;
+   function PORT_TX_DW7_N_SCALAR (N : N_Scalar) return Word32 is
+      (Shift_Left (Word32 (N), 24));
+
+   type Swing_Select is new Natural range 0 .. 15;
+   function PORT_TX_SWING_SEL_UPPER (S : Swing_Select) return Word32 is
+      (Shift_Left (Shift_Right (Word32(S), 3), 15));
+   function PORT_TX_SWING_SEL_LOWER (S : Swing_Select) return Word32 is
+      (Shift_Left (Word32(S), 11));
+
+   type Buffer_Trans is record
+     DW2_SWING_SEL    : Swing_Select;
+     DW7_N_SCALAR     : N_Scalar;
+     DW4_CURSOR_COEFF : Cursor_Coeff;
+     DW4_POST_CURSOR1 : Post_Cursor;
+   end record;
+
+   type Buffer_Trans_HDMI_Range is new Natural range 0 .. 6;
+   Buffer_Trans_HDMI : constant array (Buffer_Trans_HDMI_Range) of Buffer_Trans :=
+     ((16#A#, 16#60#, 16#3F#, 16#00#),
+      (16#B#, 16#73#, 16#36#, 16#09#),
+      (16#6#, 16#7F#, 16#31#, 16#0E#),
+      (16#B#, 16#73#, 16#3F#, 16#00#),
+      (16#6#, 16#7F#, 16#37#, 16#08#),
+      (16#6#, 16#7F#, 16#3F#, 16#00#),
+      (16#6#, 16#7F#, 16#35#, 16#0A#));
+
+   type Buffer_Trans_DP_Range is new Natural range 0 .. 9;
+   type Buffer_Trans_DP_Array is array (Buffer_Trans_DP_Range) of Buffer_Trans;
+
+   TGL_Buffer_Trans_DP_HBR : constant Buffer_Trans_DP_Array :=
+     ((16#a#, 16#32#, 16#3f#, 16#00#),
+      (16#a#, 16#4f#, 16#37#, 16#08#),
+      (16#c#, 16#71#, 16#2f#, 16#10#),
+      (16#6#, 16#7d#, 16#2b#, 16#14#),
+      (16#a#, 16#4c#, 16#3f#, 16#00#),
+      (16#c#, 16#73#, 16#34#, 16#0b#),
+      (16#6#, 16#7f#, 16#2f#, 16#10#),
+      (16#c#, 16#6c#, 16#3c#, 16#03#),
+      (16#6#, 16#7f#, 16#35#, 16#0a#),
+      (16#6#, 16#7f#, 16#3f#, 16#00#));
+
+   TGL_Buffer_Trans_DP_HBR2 : constant Buffer_Trans_DP_Array :=
+     ((16#a#, 16#35#, 16#3f#, 16#00#),
+      (16#a#, 16#4f#, 16#37#, 16#08#),
+      (16#c#, 16#63#, 16#2f#, 16#10#),
+      (16#6#, 16#7f#, 16#2b#, 16#14#),
+      (16#a#, 16#47#, 16#3f#, 16#00#),
+      (16#c#, 16#63#, 16#34#, 16#0b#),
+      (16#6#, 16#7f#, 16#2f#, 16#10#),
+      (16#c#, 16#61#, 16#3c#, 16#03#),
+      (16#6#, 16#7b#, 16#35#, 16#0a#),
+      (16#6#, 16#7f#, 16#3f#, 16#00#));
+
+   TGL_Buffer_Trans_DP_HBR2_U_Y : constant Buffer_Trans_DP_Array :=
+     ((16#a#, 16#35#, 16#3f#, 16#00#),
+      (16#a#, 16#4f#, 16#36#, 16#09#),
+      (16#c#, 16#60#, 16#32#, 16#0d#),
+      (16#c#, 16#7f#, 16#2d#, 16#12#),
+      (16#c#, 16#47#, 16#3f#, 16#00#),
+      (16#c#, 16#6f#, 16#36#, 16#09#),
+      (16#6#, 16#7d#, 16#32#, 16#0d#),
+      (16#6#, 16#60#, 16#3c#, 16#03#),
+      (16#6#, 16#7f#, 16#34#, 16#0b#),
+      (16#6#, 16#7f#, 16#3f#, 16#00#));
+
+   TGL_Buffer_Trans_DP_HBR2_EDP_HBR3 : constant Buffer_Trans_DP_Array :=
+     ((16#a#, 16#35#, 16#3f#, 16#00#),
+      (16#a#, 16#4f#, 16#37#, 16#08#),
+      (16#c#, 16#71#, 16#2f#, 16#10#),
+      (16#6#, 16#7f#, 16#2b#, 16#14#),
+      (16#a#, 16#4c#, 16#3f#, 16#00#),
+      (16#c#, 16#73#, 16#34#, 16#0b#),
+      (16#6#, 16#7f#, 16#2f#, 16#10#),
+      (16#c#, 16#6c#, 16#3c#, 16#03#),
+      (16#6#, 16#7f#, 16#35#, 16#0a#),
+      (16#6#, 16#7f#, 16#3f#, 16#00#));
+
+   TGL_Buffer_Trans_EDP_HBR2 : constant Buffer_Trans_DP_Array :=
+     ((16#0#, 16#7F#, 16#3F#, 16#00#),
+      (16#8#, 16#7F#, 16#38#, 16#07#),
+      (16#1#, 16#7F#, 16#33#, 16#0C#),
+      (16#9#, 16#7F#, 16#31#, 16#0E#),
+      (16#8#, 16#7F#, 16#3F#, 16#00#),
+      (16#1#, 16#7F#, 16#38#, 16#07#),
+      (16#9#, 16#7F#, 16#35#, 16#0A#),
+      (16#1#, 16#7F#, 16#3F#, 16#00#),
+      (16#9#, 16#7F#, 16#38#, 16#07#),
+      (16#9#, 16#7F#, 16#3F#, 16#00#));
+
+   ADL_Buffer_Trans_EDP_HBR3 : constant Buffer_Trans_DP_Array :=
+     ((16#a#, 16#35#, 16#3f#, 16#00#),
+      (16#a#, 16#4f#, 16#37#, 16#08#),
+      (16#c#, 16#71#, 16#30#, 16#0f#),
+      (16#6#, 16#7f#, 16#2b#, 16#14#),
+      (16#a#, 16#4c#, 16#3f#, 16#00#),
+      (16#c#, 16#73#, 16#34#, 16#0b#),
+      (16#6#, 16#7f#, 16#30#, 16#0f#),
+      (16#c#, 16#63#, 16#3f#, 16#00#),
+      (16#6#, 16#7f#, 16#38#, 16#07#),
+      (16#6#, 16#7f#, 16#3f#, 16#00#));
+
+   ADL_Buffer_Trans_EDP_HBR2 : constant Buffer_Trans_DP_Array :=
+     ((16#4#, 16#50#, 16#38#, 16#07#),
+      (16#4#, 16#58#, 16#35#, 16#0a#),
+      (16#4#, 16#60#, 16#34#, 16#0b#),
+      (16#4#, 16#6a#, 16#32#, 16#0d#),
+      (16#4#, 16#5e#, 16#38#, 16#07#),
+      (16#4#, 16#61#, 16#36#, 16#09#),
+      (16#4#, 16#6b#, 16#34#, 16#0b#),
+      (16#4#, 16#69#, 16#39#, 16#06#),
+      (16#4#, 16#73#, 16#37#, 16#08#),
+      (16#4#, 16#7a#, 16#38#, 16#07#));
+
+   ADL_Buffer_Trans_DP_HBR3 : constant Buffer_Trans_DP_Array :=
+     ((16#a#, 16#35#, 16#3f#, 16#00#),
+      (16#a#, 16#4f#, 16#37#, 16#08#),
+      (16#c#, 16#71#, 16#30#, 16#0f#),
+      (16#6#, 16#7f#, 16#2b#, 16#14#),
+      (16#a#, 16#4c#, 16#3f#, 16#00#),
+      (16#c#, 16#73#, 16#34#, 16#0b#),
+      (16#6#, 16#7f#, 16#30#, 16#0f#),
+      (16#c#, 16#63#, 16#3f#, 16#00#),
+      (16#6#, 16#7f#, 16#38#, 16#07#),
+      (16#6#, 16#7f#, 16#3f#, 16#00#));
+
+   ADL_Buffer_Trans_DP_HBR : constant Buffer_Trans_DP_Array :=
+     ((16#a#, 16#35#, 16#3f#, 16#00#),
+      (16#a#, 16#4f#, 16#37#, 16#08#),
+      (16#c#, 16#71#, 16#31#, 16#0e#),
+      (16#6#, 16#7f#, 16#2c#, 16#13#),
+      (16#a#, 16#4c#, 16#3f#, 16#00#),
+      (16#c#, 16#73#, 16#34#, 16#0b#),
+      (16#6#, 16#7f#, 16#2f#, 16#10#),
+      (16#c#, 16#7c#, 16#3c#, 16#03#),
+      (16#6#, 16#7f#, 16#35#, 16#0a#),
+      (16#6#, 16#7f#, 16#3f#, 16#00#));
+
+   PORT_CL_DW10_PWR_DOWN_LN_MASK  : constant := 16#f# * 2 ** 4;
+   PORT_CL_DW10_PWR_UP_ALL        : constant :=     0 * 2 ** 4;
+   PORT_CL_DW10_PWR_DOWN_LN_3_2   : constant := 16#c# * 2 ** 4;
+   PORT_CL_DW10_PWR_DOWN_LN_3_2_1 : constant := 16#e# * 2 ** 4;
+   PORT_CL_DW10_PWR_DOWN_LN_1_0   : constant := 16#3# * 2 ** 4;
+   PORT_CL_DW10_PWR_DOWN_LN_2_1_0 : constant := 16#7# * 2 ** 4;
+
+   EDP4K2K_MODE_OVRD_EN        : constant := 1 * 2 **  2;
+   EDP4K2K_MODE_OVRD_OPTIMIZED : constant := 1 * 2 **  3;
+
+   ---------------------------------------------------------------------
+
+   procedure Set_Vswing_And_Deemphasis
+     (Port        : Combo_Port;
+      Buf_Trans   : Buffer_Trans;
+      Display     : Display_Type;
+      Lane_Count  : DP_Lane_Count)
+   is
+      type Training_Values is (Training_Enable, Training_Disable);
+
+      PORT_TX_DW5_TX_TRAINING_EN        : constant := 1 * 2 ** 31;
+      PORT_TX_DW5_SCALING_MODE_SEL_MASK : constant := 7 * 2 ** 18;
+      PORT_TX_DW5_RTERM_SELECT_MASK     : constant := 7 * 2 ** 3;
+      PORT_TX_DW5_TAP2_DISABLE          : constant := 1 * 2 ** 30;
+      PORT_TX_DW5_TAP3_DISABLE          : constant := 1 * 2 ** 29;
+      PORT_TX_DW5_CURSOR_PROGRAM        : constant := 1 * 2 ** 26;
+      PORT_TX_DW5_COEFF_POLARITY        : constant := 1 * 2 ** 25;
+
+      PORT_TX_DW2_RCOMP_SCALAR_MASK     : constant := 16#ff# * 2 ** 0;
+      PORT_TX_DW2_SWING_SEL_LOWER_MASK  : constant :=      7 * 2 ** 11;
+      PORT_TX_DW2_SWING_SEL_UPPER       : constant :=      1 * 2 ** 15;
+
+      PORT_TX_DW4_LOADGEN_SELECT        : constant :=      1 * 2 ** 31;
+      PORT_TX_DW4_POST_CURSOR2_MASK     : constant := 16#3f# * 2 ** 6;
+      PORT_TX_DW4_POST_CURSOR1_MASK     : constant := 16#3f# * 2 ** 12;
+      PORT_TX_DW4_CURSOR_COEFF_MASK     : constant := 16#3f# * 2 ** 0;
+
+      PORT_TX_DW7_N_SCALAR_MASK         : constant := 16#7f# * 2 ** 24;
+
+      type Scaling_Mode is new Natural range 0 .. 7;
+      function PORT_TX_DW5_SCALING_MODE_SEL (S : Scaling_Mode) return Word32 is
+         (Shift_Left (Word32 (S), 18));
+
+      type Rterm_Select is new Natural range 0 .. 7;
+      function PORT_TX_DW5_RTERM_SELECT (R : Rterm_Select) return Word32 is
+         (Shift_Left (Word32 (R), 3));
+
+      type Rcomp_Scalar is new Natural range 0 .. 255;
+      function PORT_TX_DW2_RCOMP_SCALAR (R : Rcomp_Scalar) return Word32 is
+         (Word32 (R));
+
+      procedure Set_Tx_Training (Port : Combo_Port; Training : Training_Values) is
+         DW5 : Word32;
+      begin
+        Registers.Read (Port_Regs (Port).PORT_TX_DW5_LN0, DW5);
+        Registers.Write
+          (Register => Port_Regs (Port).PORT_TX_DW5_GRP,
+           Value    => (if Training = Training_Enable
+                        then DW5 or PORT_TX_DW5_TX_TRAINING_EN
+                        else DW5 and not PORT_TX_DW5_TX_TRAINING_EN));
+      end Set_Tx_Training;
+
+      Tmp : Word32;
+      PORT_CL_DW5_SUS_CLOCK_CONFIG : constant := 3 * 2 ** 0;
+   begin
+      -- Enable common keeper for DP/eDP only
+      Registers.Read (Port_Regs (Port).PORT_PCS_DW1_LN0, Tmp);
+      Registers.Write
+        (Port_Regs (Port).PORT_PCS_DW1_GRP,
+         (if Display = DP
+          then Tmp or PORT_PCS_DW1_LN0_COMMON_KEEPER
+          else Tmp and not PORT_PCS_DW1_LN0_COMMON_KEEPER));
+
+      -- Program loadgen select (group access is not allowed since each lane may
+      -- have a unique value.
+      -- <= 6GHz, 4 lanes   (0, 1, 1, 1)
+      -- <= 6GHz, 1,2 lanes (0, 1, 1, 0)
+      -- > 6GHz,  any lanes (0, 0, 0, 0)
+      for Lane in Lanes loop
+         if (Lane_Count = DP_Lane_Count_4 and Lane /= LN0) or
+            (Lane_Count /= DP_Lane_Count_4 and (Lane = LN1 or Lane = LN2))
+         then
+            Registers.Set_Mask (PORT_TX_DW4 (Lane, Port),
+                                PORT_TX_DW4_LOADGEN_SELECT);
+         else
+            Registers.Unset_Mask (PORT_TX_DW4 (Lane, Port),
+                                  PORT_TX_DW4_LOADGEN_SELECT);
+         end if;
+      end loop;
+
+      Registers.Set_Mask
+        (Register => Port_Regs (Port).PORT_CL_DW5,
+         Mask     => PORT_CL_DW5_SUS_CLOCK_CONFIG);
+
+      -- In order to change swing values, training must be disabled
+      Set_Tx_Training (Port, Training_Disable);
+
+      if Display = DP then
+         Registers.Unset_Mask
+          (Register => Port_Regs (Port).PORT_CL_DW10,
+           Mask     => EDP4K2K_MODE_OVRD_EN or EDP4K2K_MODE_OVRD_OPTIMIZED);
+      end if;
+
+      Registers.Read (Port_Regs (Port).PORT_TX_DW5_LN0, Tmp);
+      Tmp := Tmp and not
+         (PORT_TX_DW5_SCALING_MODE_SEL_MASK or
+          PORT_TX_DW5_RTERM_SELECT_MASK or
+          PORT_TX_DW5_TAP2_DISABLE or
+          PORT_TX_DW5_TAP3_DISABLE or
+          PORT_TX_DW5_CURSOR_PROGRAM or
+          PORT_TX_DW5_COEFF_POLARITY);
+      Tmp := Tmp or
+         PORT_TX_DW5_SCALING_MODE_SEL (2) or
+         PORT_TX_DW5_RTERM_SELECT (6) or
+         PORT_TX_DW5_TAP3_DISABLE;
+      Registers.Write (Port_Regs (Port).PORT_TX_DW5_GRP, Tmp);
+
+      Registers.Read (Port_Regs (Port).PORT_TX_DW2_LN0, Tmp);
+      Tmp := Tmp and not
+        (PORT_TX_DW2_RCOMP_SCALAR_MASK or
+         PORT_TX_DW2_SWING_SEL_LOWER_MASK or
+         PORT_TX_DW2_SWING_SEL_UPPER);
+      Tmp := Tmp or PORT_TX_SWING_SEL_UPPER (Buf_Trans.DW2_SWING_SEL);
+      Tmp := Tmp or PORT_TX_SWING_SEL_LOWER (Buf_Trans.DW2_SWING_SEL);
+      Tmp := Tmp or PORT_TX_DW2_RCOMP_SCALAR (16#98#);
+      Registers.Write (Port_Regs (Port).PORT_TX_DW2_GRP, Tmp);
+
+      -- Cannot write to GRP, because it would overwrite individual loadgen bits
+      for Lane in Lanes loop
+         Registers.Read (PORT_TX_DW4 (Lane, Port), Tmp);
+         Tmp := Tmp and not
+            (PORT_TX_DW4_POST_CURSOR2_MASK or
+             PORT_TX_DW4_POST_CURSOR1_MASK or
+             PORT_TX_DW4_CURSOR_COEFF_MASK);
+         Tmp := Tmp or PORT_TX_DW4_POST_CURSOR1 (Buf_Trans.DW4_POST_CURSOR1);
+         Tmp := Tmp or PORT_TX_DW4_CURSOR_COEFF (Buf_Trans.DW4_CURSOR_COEFF);
+         Registers.Write (PORT_TX_DW4 (Lane, Port), Tmp);
+      end loop;
+
+      Registers.Read (Port_Regs (Port).PORT_TX_DW7_LN0, Tmp);
+      Tmp := Tmp and not PORT_TX_DW7_N_SCALAR_MASK;
+      Tmp := Tmp or PORT_TX_DW7_N_SCALAR (Buf_Trans.DW7_N_SCALAR);
+      Registers.Write (Port_Regs (Port).PORT_TX_DW7_GRP, Tmp);
+
+      -- To trigger an update of swing values, set training enable
+      Set_Tx_Training (Port, Training_Enable);
+   end Set_Vswing_And_Deemphasis;
+
+   ---------------------------------------------------------------------
+
+   procedure Power_Up_Lanes
+     (Port  : Combo_Port;
+      Lane_Count : DP_Lane_Count)
+   is
+      Lane_Mask : constant Word32 :=
+        (case Lane_Count is
+            -- if Lane_Reversal then PWR_DOWN_LN_2_1_0
+            -- else PORT_CL_DW10_PWR_DOWN_LN_3_2_1
+            when DP_Lane_Count_1 => PORT_CL_DW10_PWR_DOWN_LN_3_2_1,
+            -- if Lane_Reversal then PWR_DOWN_1_0
+            -- else PORT_CL_DW10_PWR_DOWN_LN_3_2
+            when DP_Lane_Count_2 => PORT_CL_DW10_PWR_DOWN_LN_3_2,
+            when others          => PORT_CL_DW10_PWR_UP_ALL);
+   begin
+      Registers.Unset_And_Set_Mask
+        (Register   => Port_Regs (Port).PORT_CL_DW10,
+         Mask_Unset => PORT_CL_DW10_PWR_DOWN_LN_MASK,
+         Mask_Set   => Lane_Mask);
+   end Power_Up_Lanes;
+
+   ---------------------------------------------------------------------
+
+   procedure Set_Signal_Levels
+     (Port        : Combo_Port;
+      eDP         : Boolean;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set)
+   is
+      function Get_Buf_Trans_Table
+        (eDP : Boolean) return Buffer_Trans_DP_Array is
+      begin
+         if Config.Has_TGL_Buffer_Translations then
+            if eDP then
+               if Link.Bandwidth > DP_Bandwidth_5_4 then
+                  return TGL_Buffer_Trans_DP_HBR2_EDP_HBR3;
+               else
+                  return TGL_Buffer_Trans_DP_HBR;
+               end if;
+            else
+               if Link.Bandwidth > DP_Bandwidth_2_7 then
+                  if Config.Is_LP then
+                     return TGL_Buffer_Trans_DP_HBR2_U_Y;
+                 else
+                     return TGL_Buffer_Trans_DP_HBR2;
+                  end if;
+               else
+                  return TGL_Buffer_Trans_DP_HBR;
+               end if;
+            end if;
+         else
+            if eDP then
+               if Link.Bandwidth > DP_Bandwidth_5_4 then
+                  return ADL_Buffer_Trans_EDP_HBR3;
+               else
+                  return ADL_Buffer_Trans_DP_HBR; -- EDP_HBR2 ?
+               end if;
+            else
+               if Link.Bandwidth > DP_Bandwidth_2_7 then
+                  return ADL_Buffer_Trans_DP_HBR3;
+               else
+                  return ADL_Buffer_Trans_DP_HBR;
+               end if;
+            end if;
+         end if;
+      end Get_Buf_Trans_Table;
+
+      function To_Buf_Trans_Index
+         (Set : DP_Info.Train_Set) return Buffer_Trans_DP_Range
+      is
+      begin
+         case Set.Voltage_Swing is
+         when DP_Info.VS_Level_0 =>
+            case Set.Pre_Emph is
+            when DP_Info.Emph_Level_0 => return 0;
+            when DP_Info.Emph_Level_1 => return 1;
+            when DP_Info.Emph_Level_2 => return 2;
+            when DP_Info.Emph_Level_3 => return 3;
+            end case;
+         when DP_Info.VS_Level_1 =>
+            case Set.Pre_Emph is
+            when DP_Info.Emph_Level_0 => return 4;
+            when DP_Info.Emph_Level_1 => return 5;
+            when DP_Info.Emph_Level_2 => return 6;
+            when others =>               return 0;
+            end case;
+         when DP_Info.VS_Level_2 =>
+            case Set.Pre_Emph is
+            when DP_Info.Emph_Level_0 => return 7;
+            when DP_Info.Emph_Level_1 => return 8;
+            when others =>               return 0;
+            end case;
+         when DP_Info.VS_Level_3 =>
+            case Set.Pre_Emph is
+            when DP_Info.Emph_Level_0 => return 9;
+            when others =>               return 0;
+            end case;
+         end case;
+      end To_Buf_Trans_Index;
+
+      Was_Enabled : Boolean;
+      Buf_Trans : Buffer_Trans;
+      Entry_Index : constant Buffer_Trans_DP_Range :=
+         To_Buf_Trans_Index (Train_Set);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Is_Set_Mask
+        (Register => Port_Regs (Port).DDI_BUF_CTL,
+         Mask     => DDI_BUF_CTL_BUFFER_ENABLE,
+         Result   => Was_Enabled);
+
+      Buf_Trans := Get_Buf_Trans_Table (eDP) (Entry_Index);
+
+      Set_Vswing_And_Deemphasis
+        (Port        => Port,
+         Buf_Trans   => Buf_Trans,
+         Display     => DP,
+         Lane_Count  => Link.Lane_Count);
+
+      if not Was_Enabled then
+         Power_Up_Lanes (Port, Link.Lane_Count);
+      end if;
+
+      Registers.Unset_And_Set_Mask
+        (Register    => Port_Regs (Port).DDI_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_PORT_WIDTH (Link.Lane_Count));
+      Registers.Posting_Read (Port_Regs (Port).DDI_BUF_CTL);
+
+      if not Was_Enabled then
+         Registers.Wait_Unset_Mask (Port_Regs (Port).DDI_BUF_CTL, DDI_BUF_CTL_IDLE_STATUS);
+      end if;
+   end Set_Signal_Levels;
+
+   ---------------------------------------------------------------------
+
+   procedure Enable_HDMI (Port : Combo_Port)
+   is
+      HDMI_Lane_Count : constant DP_Lane_Count := DP_Lane_Count_4;
+   begin
+      Set_Vswing_And_Deemphasis
+        (Port        => Port,
+         Buf_Trans   => Buffer_Trans_HDMI (Buffer_Trans_HDMI'First),
+         Display     => HDMI,
+         Lane_Count  => HDMI_Lane_Count);
+
+      Power_Up_Lanes (Port, HDMI_Lane_Count);
+
+      Registers.Unset_And_Set_Mask
+           (Register    => DDI_BUF_CTL (Port),
+            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);
+
+      Registers.Wait_Unset_Mask
+        (Register => DDI_BUF_CTL (Port),
+         Mask     => DDI_BUF_CTL_IDLE_STATUS,
+         TOut_MS  => 1);
+   end Enable_HDMI;
+
+end HW.GFX.GMA.Connectors.Combo_Phy;