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/Makefile.inc b/common/tigerlake/Makefile.inc
index 0082e25..c5fb4f2 100644
--- a/common/tigerlake/Makefile.inc
+++ b/common/tigerlake/Makefile.inc
@@ -3,6 +3,10 @@
 gfxinit-y += hw-gfx-gma-power_and_clocks.adb
 gfxinit-y += hw-gfx-gma-power_and_clocks.ads
 gfxinit-y += hw-gfx-gma-connectors.adb
+gfxinit-y += hw-gfx-gma-connectors-tc.adb
+gfxinit-y += hw-gfx-gma-connectors-tc.ads
+gfxinit-y += hw-gfx-gma-connectors-combo_phy.adb
+gfxinit-y += hw-gfx-gma-connectors-combo_phy.ads
 gfxinit-y += hw-gfx-gma-port_detect.adb
 gfxinit-y += hw-gfx-gma-plls.adb
 gfxinit-y += hw-gfx-gma-plls.ads
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;
diff --git a/common/tigerlake/hw-gfx-gma-connectors-combo_phy.ads b/common/tigerlake/hw-gfx-gma-connectors-combo_phy.ads
new file mode 100644
index 0000000..b5347a2
--- /dev/null
+++ b/common/tigerlake/hw-gfx-gma-connectors-combo_phy.ads
@@ -0,0 +1,27 @@
+--
+-- 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.GMA.DP_Info;
+
+private package HW.GFX.GMA.Connectors.Combo_Phy is
+
+   procedure Set_Signal_Levels
+     (Port        : Combo_Port;
+      eDP         : Boolean;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set);
+
+   procedure Enable_HDMI (Port : Combo_Port);
+
+end HW.GFX.GMA.Connectors.Combo_Phy;
diff --git a/common/tigerlake/hw-gfx-gma-connectors-tc.adb b/common/tigerlake/hw-gfx-gma-connectors-tc.adb
new file mode 100644
index 0000000..4b84f3c
--- /dev/null
+++ b/common/tigerlake/hw-gfx-gma-connectors-tc.adb
@@ -0,0 +1,610 @@
+--
+-- 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.GMA.Config;
+with HW.GFX.GMA.Registers;
+with HW.GFX.DP_Info;
+with HW.GFX.GMA.PCode;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+use type HW.Word64;
+
+package body HW.GFX.GMA.Connectors.TC is
+
+   function HIP_INDEX_REG (P : USBC_Port) return Registers.Registers_Index
+   is
+     (if P <= DDI_TC4
+      then Registers.HIP_INDEX_REG0
+      else Registers.HIP_INDEX_REG1);
+
+   function HIP_INDEX_VAL (P : USBC_Port; Val : Word32) return Word32 is
+     (Val * 2 ** (8 * ((GPU_Port'Pos (P) - GPU_Port'Pos (DDI_TC1)) mod 4)));
+
+   type Port_Regs_Array is array (USBC_Port) of Registers.Registers_Index;
+   DKL_DP_MODE : constant Port_Regs_Array :=
+     (DDI_TC1 => Registers.DKL_DP_MODE_1,
+      DDI_TC2 => Registers.DKL_DP_MODE_2,
+      DDI_TC3 => Registers.DKL_DP_MODE_3,
+      DDI_TC4 => Registers.DKL_DP_MODE_4,
+      DDI_TC5 => Registers.DKL_DP_MODE_5,
+      DDI_TC6 => Registers.DKL_DP_MODE_6);
+
+   function DP_PIN_ASSIGNMENT_SHIFT (P : USBC_Port) return natural is
+     (case P is
+      when DDI_TC1 => 0,
+      when DDI_TC2 => 4,
+      when DDI_TC3 => 8,
+      when DDI_TC4 => 12,
+      when DDI_TC5 => 16,
+      when DDI_TC6 => 20);
+
+   DP_PIN_ASSIGNMENT_MASK : constant := 16#f#;
+
+   TGL_PCODE_TCCOLD         : constant := 16#26#;
+   TCCOLD_BLOCK_REQ         : constant := 16#00#;
+   TCCOLD_UNBLOCK_REQ       : constant := 16#01#;
+   TCCOLD_BLOCK_RESULT_FAIL : constant := 16#01#;
+
+   type Fia_Regs_Record is record
+      PORT_TX_DFLEXDPMLE1 : Registers.Registers_Index;
+      PORT_TX_DFLEXDPSP   : Registers.Registers_Index;
+      PORT_TX_DFLEXDPPMS  : Registers.Registers_Index;
+      PORT_TX_DFLEXDPCSSS : Registers.Registers_Index;
+      PORT_TX_DFLEXPA1    : Registers.Registers_Index;
+   end record;
+
+   Fia_Regs : constant array (USBC_Port) of Fia_Regs_Record :=
+     (DDI_TC1 =>
+        (Registers.PORT_TX_DFLEXDPMLE1_FIA1,
+         Registers.PORT_TX_DFLEXDPSP_FIA1,
+         Registers.PORT_TX_DFLEXDPPMS_FIA1,
+         Registers.PORT_TX_DFLEXDPCSSS_FIA1,
+         Registers.PORT_TX_DFLEXPA1_FIA1),
+      DDI_TC2 =>
+        (Registers.PORT_TX_DFLEXDPMLE1_FIA1,
+         Registers.PORT_TX_DFLEXDPSP_FIA1,
+         Registers.PORT_TX_DFLEXDPPMS_FIA1,
+         Registers.PORT_TX_DFLEXDPCSSS_FIA1,
+         Registers.PORT_TX_DFLEXPA1_FIA1),
+      DDI_TC3 =>
+        (Registers.PORT_TX_DFLEXDPMLE1_FIA2,
+         Registers.PORT_TX_DFLEXDPSP_FIA2,
+         Registers.PORT_TX_DFLEXDPPMS_FIA2,
+         Registers.PORT_TX_DFLEXDPCSSS_FIA2,
+         Registers.PORT_TX_DFLEXPA1_FIA2),
+      DDI_TC4 =>
+        (Registers.PORT_TX_DFLEXDPMLE1_FIA2,
+         Registers.PORT_TX_DFLEXDPSP_FIA2,
+         Registers.PORT_TX_DFLEXDPPMS_FIA2,
+         Registers.PORT_TX_DFLEXDPCSSS_FIA2,
+         Registers.PORT_TX_DFLEXPA1_FIA2),
+      DDI_TC5 =>
+        (Registers.PORT_TX_DFLEXDPMLE1_FIA3,
+         Registers.PORT_TX_DFLEXDPSP_FIA3,
+         Registers.PORT_TX_DFLEXDPPMS_FIA3,
+         Registers.PORT_TX_DFLEXDPCSSS_FIA3,
+         Registers.PORT_TX_DFLEXPA1_FIA3),
+      DDI_TC6 =>
+        (Registers.PORT_TX_DFLEXDPMLE1_FIA3,
+         Registers.PORT_TX_DFLEXDPSP_FIA3,
+         Registers.PORT_TX_DFLEXDPPMS_FIA3,
+         Registers.PORT_TX_DFLEXDPCSSS_FIA3,
+         Registers.PORT_TX_DFLEXPA1_FIA3));
+
+   function Fia_Index (Port : USBC_Port) return natural
+   is (case Port is
+       when DDI_TC1 | DDI_TC3 | DDI_TC5 => 0,
+       when DDI_TC2 | DDI_TC4 | DDI_TC6 => 1);
+
+   function DFLEXDPMLE1_DPMLETC_MASK (Port : USBC_Port) return Word32 is
+      (Shift_Left (15, 4 * Fia_Index (Port)));
+   function DFLEXDPMLE1_DPMLETC_ML0 (Port : USBC_Port) return Word32 is
+      (Shift_Left (1, 4 * Fia_Index (Port)));
+   function DFLEXDPMLE1_DPMLETC_ML1_0 (Port : USBC_Port) return Word32 is
+      (Shift_Left (3, 4 * Fia_Index (Port)));
+   function DFLEXDPMLE1_DPMLETC_ML3 (Port : USBC_Port) return Word32 is
+      (Shift_Left (8, 4 * Fia_Index (Port)));
+   function DFLEXDPMLE1_DPMLETC_ML3_2 (Port : USBC_Port) return Word32 is
+      (Shift_Left (12, 4 * Fia_Index (Port)));
+   function DFLEXDPMLE1_DPMLETC_ML3_0 (Port : USBC_Port) return Word32 is
+      (Shift_Left (15, 4 * Fia_Index (Port)));
+   function DP_PHY_MODE_STATUS_COMPLETE (Port : USBC_Port) return Word32 is
+      (Shift_Left (1, Fia_Index (Port)));
+   function DP_PHY_MODE_STATUS_NOT_SAFE (Port : USBC_Port) return Word32 is
+      (Shift_Left (1, Fia_Index (Port)));
+   function TC_LIVE_STATE_TC (Port : USBC_Port) return Word32 is
+      (Shift_Left (1, Fia_Index (Port) * 8 + 5));
+   function DP_LANE_ASSIGNMENT_MASK (Port : USBC_Port) return Word32 is
+      (Shift_Left (16#f#, Fia_Index (Port) * 8));
+   function DP_LANE_ASSIGNMENT_SHIFT (Port : USBC_Port) return natural is
+      (Fia_Index (Port) * 8);
+
+   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_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);
+   DDI_BUF_CTL : constant Port_Regs_Array :=
+     (DDI_TC1  => Registers.DDI_BUF_CTL_USBC1,
+      DDI_TC2  => Registers.DDI_BUF_CTL_USBC2,
+      DDI_TC3  => Registers.DDI_BUF_CTL_USBC3,
+      DDI_TC4  => Registers.DDI_BUF_CTL_USBC4,
+      DDI_TC5  => Registers.DDI_BUF_CTL_USBC5,
+      DDI_TC6  => Registers.DDI_BUF_CTL_USBC6);
+
+   type Buffer_Trans is record
+      Vswing_Control     : Word32;
+      Preshoot_Control   : Word32;
+      Deemphasis_Control : Word32;
+   end record;
+
+   type Buffer_Trans_Range is new natural range 0 .. 9;
+   type Buffer_Trans_Array is array (Buffer_Trans_Range) of Buffer_Trans;
+   TGL_Buffer_Trans_DP_HBR2 : constant Buffer_Trans_Array :=
+     ((16#7#, 16#0#, 16#00#),
+      (16#5#, 16#0#, 16#05#),
+      (16#2#, 16#0#, 16#0B#),
+      (16#0#, 16#0#, 16#19#),
+      (16#5#, 16#0#, 16#00#),
+      (16#2#, 16#0#, 16#08#),
+      (16#0#, 16#0#, 16#14#),
+      (16#2#, 16#0#, 16#00#),
+      (16#0#, 16#0#, 16#0B#),
+      (16#0#, 16#0#, 16#00#));
+   TGL_Buffer_Trans_DP_HBR : constant Buffer_Trans_Array :=
+     ((16#7#, 16#0#, 16#00#),
+      (16#5#, 16#0#, 16#05#),
+      (16#2#, 16#0#, 16#0B#),
+      (16#0#, 16#0#, 16#18#),
+      (16#5#, 16#0#, 16#00#),
+      (16#2#, 16#0#, 16#08#),
+      (16#0#, 16#0#, 16#14#),
+      (16#2#, 16#0#, 16#00#),
+      (16#0#, 16#0#, 16#0B#),
+      (16#0#, 16#0#, 16#00#));
+   Buffer_Trans_HDMI : constant Buffer_Trans_Array :=
+     ((16#7#, 16#0#, 16#0#),
+      (16#6#, 16#0#, 16#0#),
+      (16#4#, 16#0#, 16#0#),
+      (16#2#, 16#0#, 16#0#),
+      (16#0#, 16#0#, 16#0#),
+      (16#0#, 16#0#, 16#5#),
+      (16#0#, 16#0#, 16#6#),
+      (16#0#, 16#0#, 16#7#),
+      (16#0#, 16#0#, 16#8#),
+      (16#0#, 16#0#, 16#A#));
+   ADL_Buffer_Trans_DP_HBR2 : constant Buffer_Trans_Array :=
+     ((16#7#, 16#0#, 16#00#),
+      (16#5#, 16#0#, 16#04#),
+      (16#2#, 16#0#, 16#0a#),
+      (16#0#, 16#0#, 16#18#),
+      (16#5#, 16#0#, 16#00#),
+      (16#2#, 16#0#, 16#06#),
+      (16#0#, 16#0#, 16#14#),
+      (16#2#, 16#0#, 16#00#),
+      (16#0#, 16#0#, 16#09#),
+      (16#0#, 16#0#, 16#00#));
+   ADL_Buffer_Trans_DP_HBR : constant Buffer_Trans_Array :=
+     ((16#7#, 16#0#, 16#01#),
+      (16#5#, 16#0#, 16#06#),
+      (16#2#, 16#0#, 16#0b#),
+      (16#0#, 16#0#, 16#17#),
+      (16#5#, 16#0#, 16#00#),
+      (16#2#, 16#0#, 16#08#),
+      (16#0#, 16#0#, 16#14#),
+      (16#2#, 16#0#, 16#00#),
+      (16#0#, 16#0#, 16#0b#),
+      (16#0#, 16#0#, 16#00#));
+
+   type Vswing_Regs_Record is record
+      DKL_TX_PMD_LANE_SUS  : Registers.Registers_Index;
+      DKL_TX_DPCNTL0       : Registers.Registers_Index;
+      DKL_TX_DPCNTL1       : Registers.Registers_Index;
+      DKL_TX_DPCNTL2       : Registers.Registers_Index;
+   end record;
+
+   Vswing_Regs : constant array (USBC_Port) of Vswing_Regs_Record :=
+     (DDI_TC1 =>
+        (Registers.DKL_TX_PMD_LANE_SUS_1,
+         Registers.DKL_TX_DPCNTL0_1,
+         Registers.DKL_TX_DPCNTL1_1,
+         Registers.DKL_TX_DPCNTL2_1),
+      DDI_TC2 =>
+        (Registers.DKL_TX_PMD_LANE_SUS_2,
+         Registers.DKL_TX_DPCNTL0_2,
+         Registers.DKL_TX_DPCNTL1_2,
+         Registers.DKL_TX_DPCNTL2_2),
+      DDI_TC3 =>
+        (Registers.DKL_TX_PMD_LANE_SUS_3,
+         Registers.DKL_TX_DPCNTL0_3,
+         Registers.DKL_TX_DPCNTL1_3,
+         Registers.DKL_TX_DPCNTL2_3),
+      DDI_TC4 =>
+        (Registers.DKL_TX_PMD_LANE_SUS_4,
+         Registers.DKL_TX_DPCNTL0_4,
+         Registers.DKL_TX_DPCNTL1_4,
+         Registers.DKL_TX_DPCNTL2_4),
+      DDI_TC5 =>
+        (Registers.DKL_TX_PMD_LANE_SUS_5,
+         Registers.DKL_TX_DPCNTL0_5,
+         Registers.DKL_TX_DPCNTL1_5,
+         Registers.DKL_TX_DPCNTL2_5),
+      DDI_TC6 =>
+        (Registers.DKL_TX_PMD_LANE_SUS_6,
+         Registers.DKL_TX_DPCNTL0_6,
+         Registers.DKL_TX_DPCNTL1_6,
+         Registers.DKL_TX_DPCNTL2_6));
+
+   procedure Set_HIP_For_Port (P : USBC_Port; N : Natural) is
+   begin
+      Registers.Write (HIP_INDEX_REG (P), HIP_INDEX_VAL (P, Word32 (N)));
+   end Set_HIP_For_Port;
+
+   subtype Pin_Assignment_Type is natural range 0 .. 6;
+   procedure Get_Pin_Assignment
+     (P          : in    USBC_Port;
+      Assignment : out   Pin_Assignment_Type)
+   is
+      Tmp : Word32;
+      A : Word32;
+   begin
+      Registers.Read (Fia_Regs (P).PORT_TX_DFLEXPA1, Tmp);
+      A := Shift_Right (Tmp, DP_PIN_ASSIGNMENT_SHIFT (P));
+      A := A and DP_PIN_ASSIGNMENT_MASK;
+
+      if natural (A) in Pin_Assignment_Type then
+         Assignment := Pin_Assignment_Type (A);
+      else
+         Assignment := Pin_Assignment_Type'First;
+      end if;
+   end Get_Pin_Assignment;
+
+   ---------------------------------------------------------------------
+
+   procedure Program_DP_Mode (P : USBC_Port; Lane_Count : Natural)
+   is
+      MG_DP_MODE_CFG_DP_X1_MODE : constant := 1 * 2 ** 6;
+      MG_DP_MODE_CFG_DP_X2_MODE : constant := 1 * 2 ** 7;
+      DP_X_MODE_MASK : constant Word32 :=
+         MG_DP_MODE_CFG_DP_X1_MODE or MG_DP_MODE_CFG_DP_X2_MODE;
+      Assignment : Pin_Assignment_Type;
+      Ln0, Ln1 : Word32;
+   begin
+      Set_HIP_For_Port (P, 0);
+      Registers.Read (DKL_DP_MODE (P), Ln0);
+      Set_HIP_For_Port (P, 1);
+      Registers.Read (DKL_DP_MODE (P), Ln1);
+
+      Ln0 := Ln0 and not DP_X_MODE_MASK;
+      Ln1 := Ln1 and not DP_X_MODE_MASK;
+
+      Get_Pin_Assignment (P, Assignment);
+      case Assignment is
+      when 0 =>
+         if Lane_Count = 1 then
+            Ln1 := Ln1 or MG_DP_MODE_CFG_DP_X1_MODE;
+         else
+            Ln0 := Ln0 or MG_DP_MODE_CFG_DP_X2_MODE;
+            Ln1 := Ln1 or MG_DP_MODE_CFG_DP_X2_MODE;
+         end if;
+      when 1 =>
+         if Lane_Count = 4 then
+            Ln0 := Ln0 or MG_DP_MODE_CFG_DP_X2_MODE;
+            Ln1 := Ln1 or MG_DP_MODE_CFG_DP_X2_MODE;
+         end if;
+      when 2 =>
+         if Lane_Count = 2 then
+            Ln0 := Ln0 or MG_DP_MODE_CFG_DP_X2_MODE;
+            Ln1 := Ln1 or MG_DP_MODE_CFG_DP_X2_MODE;
+         end if;
+      when 3 | 4 | 5 | 6 =>
+         if Lane_Count = 1 then
+            Ln0 := Ln0 or MG_DP_MODE_CFG_DP_X1_MODE;
+            Ln1 := Ln1 or MG_DP_MODE_CFG_DP_X1_MODE;
+         else
+            Ln0 := Ln0 or MG_DP_MODE_CFG_DP_X2_MODE;
+            Ln1 := Ln1 or MG_DP_MODE_CFG_DP_X2_MODE;
+         end if;
+      end case;
+
+      Set_HIP_For_Port (P, 0);
+      Registers.Unset_And_Set_Mask (DKL_DP_MODE (P), DP_X_MODE_MASK, Ln0);
+      Set_HIP_For_Port (P, 1);
+      Registers.Unset_And_Set_Mask (DKL_DP_MODE (P), DP_X_MODE_MASK, Ln1);
+   end Program_DP_Mode;
+
+   ---------------------------------------------------------------------
+
+   procedure TC_Cold_Request
+     (Request : in     TC_Cold_Request_Type;
+      Success :    out Boolean)
+   is
+      Result : Word64;
+   begin
+      for Try in 1 .. 3 loop
+         PCode.Mailbox_Read
+           (MBox       => TGL_PCODE_TCCOLD,
+            Command    => (if Request = Block
+                           then TCCOLD_BLOCK_REQ
+                           else TCCOLD_UNBLOCK_REQ),
+            Wait_Ready => True,
+            Reply      => Result,
+            Success    => Success);
+
+         if Success then
+            Success := (Result and TCCOLD_BLOCK_RESULT_FAIL) = 0;
+         end if;
+
+         exit when Success;
+
+         -- Wait 1 millisecond and try again
+         Time.U_Delay (1_000);
+      end loop;
+   end TC_Cold_Request;
+
+   ---------------------------------------------------------------------
+
+   procedure Claim
+     (Port     : in     USBC_Port;
+      DP_Alt   : in     Boolean;
+      Success  :    out Boolean)
+   is
+   begin
+      -- For legacy ports, this is supposed to be
+      -- initialized once during boot, hence wait.
+      Registers.Wait_Set_Mask
+        (Register => Fia_Regs (Port).PORT_TX_DFLEXDPPMS,
+         Mask     => DP_PHY_MODE_STATUS_COMPLETE (Port),
+         TOut_MS  => (if DP_Alt then 0 else 100),
+         Success  => Success);
+      if not Success then
+         pragma Debug (Debug.Put_Line ("DP PHY mode status not complete"));
+         return;
+      end if;
+
+      Registers.Set_Mask
+        (Register => Fia_Regs (Port).PORT_TX_DFLEXDPCSSS,
+         Mask     => DP_PHY_MODE_STATUS_NOT_SAFE (Port));
+   end Claim;
+
+   procedure Claimed (Port : USBC_Port; Is_Claimed : out Boolean) is
+   begin
+      if Port not in DDI_TC1 .. Config.Last_TC_Port then
+         Is_Claimed := False;
+         return;
+      end if;
+
+      Registers.Is_Set_Mask
+        (Register => Fia_Regs (Port).PORT_TX_DFLEXDPCSSS,
+         Mask     => DP_PHY_MODE_STATUS_NOT_SAFE (Port),
+         Result   => Is_Claimed);
+   end Claimed;
+
+   ---------------------------------------------------------------------
+
+   procedure Connect
+     (Port     : in     USBC_Port;
+      DP_Alt   : in     Boolean;
+      Lanes    : in     DP_Lane_Count;
+      Success  :    out Boolean)
+   is
+      procedure Get_Lane_Assignment_Count (Lanes : out DP_Lane_Count)
+      is
+          Lane_Mask : Word32;
+          Tmp : Word32;
+      begin
+         Registers.Read (Fia_Regs (Port).PORT_TX_DFLEXDPSP, Tmp);
+         Lane_Mask := Shift_Right (Tmp and DP_LANE_ASSIGNMENT_MASK (Port),
+                                   DP_LANE_ASSIGNMENT_SHIFT (Port));
+         Lanes :=
+           (case Lane_Mask is
+               when 16#1# | 16#2# | 16#4# | 16#8#  => DP_Lane_Count_1,
+               when 16#3# | 16#c#                  => DP_Lane_Count_2,
+               when 16#f#                          => DP_Lane_Count_4,
+               when others                         => DP_Lane_Count_1);
+      end Get_Lane_Assignment_Count;
+
+      procedure Set_Lane_Count (Lanes : DP_Lane_Count) is
+      begin
+         Registers.Unset_And_Set_Mask
+           (Register   => Fia_Regs (Port).PORT_TX_DFLEXDPMLE1,
+            Mask_Unset => DFLEXDPMLE1_DPMLETC_MASK (Port),
+            Mask_Set   =>
+              (case Lanes is
+                  -- ML0 is not lane-reversed, ML3 is reverse
+                  when DP_Lane_Count_1 => DFLEXDPMLE1_DPMLETC_ML0 (Port),
+                  -- ML1_0 is not reversed, ML3_2 is reverse
+                  when DP_Lane_Count_2 => DFLEXDPMLE1_DPMLETC_ML1_0 (Port),
+                  -- symmetric
+                  when DP_Lane_Count_4 => DFLEXDPMLE1_DPMLETC_ML3_0 (Port)));
+      end Set_Lane_Count;
+
+      Assigned_Lanes : DP_Lane_Count;
+   begin
+      Claimed (Port, Success);
+      if not Success then
+         pragma Debug (Debug.Put_Line ("Tried to connect to unclaimed port."));
+         return;
+      end if;
+
+      if DP_Alt then
+         Registers.Is_Set_Mask
+           (Register => Fia_Regs (Port).PORT_TX_DFLEXDPSP,
+            Mask     => TC_LIVE_STATE_TC (Port),
+            Result   => Success);
+         if not Success then
+            pragma Debug (Debug.Put_Line ("DP-Alt is not connected."));
+            return;
+         end if;
+
+         Get_Lane_Assignment_Count (Assigned_Lanes);
+         Set_Lane_Count (Assigned_Lanes);
+      else
+         Set_Lane_Count (Lanes);
+      end if;
+   end Connect;
+
+   ---------------------------------------------------------------------
+
+   procedure Disconnect (Port : USBC_Port) is
+   begin
+      if Port in DDI_TC1 .. Config.Last_TC_Port then
+         Registers.Unset_Mask
+           (Register => Fia_Regs (Port).PORT_TX_DFLEXDPCSSS,
+            Mask     => DP_PHY_MODE_STATUS_NOT_SAFE (Port));
+      end if;
+   end Disconnect;
+
+   ---------------------------------------------------------------------
+
+   procedure Set_Vswing_And_Deemphasis
+      (Port      : USBC_Port;
+       Buf_Trans : Buffer_Trans)
+   is
+      -- Preshoot Coeff, Deemphasis Coeff, VSwing Control,
+      DPcnt_Mask : constant Word32 := 16#3_ff07#;
+      DPcnt_Val : constant Word32 :=
+         Buf_Trans.Vswing_Control or
+         Shift_Left (Buf_Trans.Deemphasis_Control, 8) or
+         Shift_Left (Buf_Trans.Preshoot_Control, 13);
+      DKL_TX_DP20BITMODE : constant := 1 * 2 ** 2;
+   begin
+      for Lane in 0 .. 1 loop
+         Set_HIP_For_Port (Port, Lane);
+         Registers.Write (Vswing_Regs (Port).DKL_TX_PMD_LANE_SUS, 0);
+         Registers.Unset_And_Set_Mask
+            (Vswing_Regs (Port).DKL_TX_DPCNTL0, DPcnt_Mask, DPcnt_Val);
+         Registers.Unset_And_Set_Mask
+            (Vswing_Regs (Port).DKL_TX_DPCNTL1, DPcnt_Mask, DPcnt_Val);
+         Registers.Unset_Mask
+            (Vswing_Regs (Port).DKL_TX_DPCNTL2, DKL_TX_DP20BITMODE);
+      end loop;
+
+   end Set_Vswing_And_Deemphasis;
+
+   ---------------------------------------------------------------------
+
+   procedure Set_Signal_Levels
+     (Port        : USBC_Port;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set)
+   is
+      function To_Buf_Trans_Index
+         (Set : DP_Info.Train_Set) return Buffer_Trans_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_Range :=
+         To_Buf_Trans_Index (Train_Set);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Is_Set_Mask
+        (Register => DDI_BUF_CTL (Port),
+         Mask     => DDI_BUF_CTL_BUFFER_ENABLE,
+         Result   => Was_Enabled);
+
+      if Config.Has_TGL_Buffer_Translations then
+         if Link.Bandwidth > DP_Bandwidth_2_7 then
+            Buf_Trans := TGL_Buffer_Trans_DP_HBR2 (Entry_Index);
+         else
+            Buf_Trans := TGL_Buffer_Trans_DP_HBR (Entry_Index);
+         end if;
+      else
+         if Link.Bandwidth > DP_Bandwidth_2_7 then
+            Buf_Trans := ADL_Buffer_Trans_DP_HBR2 (Entry_Index);
+         else
+            Buf_Trans := ADL_Buffer_Trans_DP_HBR (Entry_Index);
+         end if;
+      end if;
+
+      Set_Vswing_And_Deemphasis (Port, Buf_Trans);
+
+      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 or
+                        DDI_BUF_CTL_PORT_WIDTH (Link.Lane_Count));
+      Registers.Posting_Read (DDI_BUF_CTL (Port));
+
+      if not Was_Enabled then
+         Registers.Wait_Unset_Mask (DDI_BUF_CTL (Port), DDI_BUF_CTL_IDLE_STATUS);
+      end if;
+   end Set_Signal_Levels;
+
+   procedure Enable_HDMI (Port : USBC_Port)
+   is
+      HDMI_Lane_Count : constant := 4;
+      Buf_Trans : constant Buffer_Trans :=
+         Buffer_Trans_HDMI (Buffer_Trans_HDMI'Last);
+   begin
+      Program_DP_Mode (Port, HDMI_Lane_Count);
+      Set_Vswing_And_Deemphasis (Port, Buf_Trans);
+
+      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.TC;
diff --git a/common/tigerlake/hw-gfx-gma-connectors-tc.ads b/common/tigerlake/hw-gfx-gma-connectors-tc.ads
new file mode 100644
index 0000000..d9caf49
--- /dev/null
+++ b/common/tigerlake/hw-gfx-gma-connectors-tc.ads
@@ -0,0 +1,44 @@
+--
+-- 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.GMA.DP_Info;
+
+package HW.GFX.GMA.Connectors.TC is
+
+   procedure Claim
+     (Port     : in     USBC_Port;
+      DP_Alt   : in     Boolean;
+      Success  :    out Boolean);
+   procedure Claimed (Port : USBC_Port; Is_Claimed : out Boolean);
+   procedure Connect
+     (Port     : in     USBC_Port;
+      DP_Alt   : in     Boolean;
+      Lanes    : in     DP_Lane_Count;
+      Success  :    out Boolean);
+   procedure Disconnect (Port : USBC_Port);
+
+   procedure Program_DP_Mode (P : USBC_Port; Lane_Count : Natural);
+   procedure Enable_HDMI (Port : USBC_Port);
+
+   procedure Set_Signal_Levels
+     (Port        : USBC_Port;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set);
+
+   type TC_Cold_Request_Type is (Block, Unblock);
+   procedure TC_Cold_Request
+     (Request : in     TC_Cold_Request_Type;
+      Success :    out Boolean);
+
+end HW.GFX.GMA.Connectors.TC;
diff --git a/common/tigerlake/hw-gfx-gma-connectors.adb b/common/tigerlake/hw-gfx-gma-connectors.adb
index 0870288..02a208b 100644
--- a/common/tigerlake/hw-gfx-gma-connectors.adb
+++ b/common/tigerlake/hw-gfx-gma-connectors.adb
@@ -12,34 +12,296 @@
 -- GNU General Public License for more details.
 --
 
+with HW.GFX.GMA.DP_Aux_Ch;
+with HW.GFX.DP_Training;
+with HW.GFX.GMA.Combo_Phy;
 with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Config_Helpers;
+with HW.GFX.GMA.Connectors.TC;
+with HW.GFX.GMA.Connectors.Combo_Phy;
+with HW.GFX.GMA.DP_Aux_Request;
+with HW.GFX.GMA.DP_Info;
 with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.Power_And_Clocks;
+with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.Transcoder;
 
 with HW.Debug;
 with GNAT.Source_Info;
 
 package body HW.GFX.GMA.Connectors is
 
-   procedure Post_Reset_Off is
-   begin
-      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
-   end Post_Reset_Off;
+   type Pipe_Regs is array (Pipe_Index) of Registers.Registers_Index;
+   DP_TP_CTL : constant Pipe_Regs :=
+     (Primary   => Registers.TGL_DP_TP_CTL_A,
+      Secondary => Registers.TGL_DP_TP_CTL_B,
+      Tertiary  => Registers.TGL_DP_TP_CTL_C);
+   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_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_IDLE        : constant := 2 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_NORMAL      : constant := 3 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_PAT3        : constant := 4 * 2 **  8;
+   DP_TP_CTL_LINK_TRAIN_PAT4        : constant := 5 * 2 **  8;
 
-   procedure Initialize is
-   begin
-      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
-   end Initialize;
+   DP_TP_STATUS : constant Pipe_Regs :=
+     (Primary   => Registers.TGL_DP_TP_STATUS_A,
+      Secondary => Registers.TGL_DP_TP_STATUS_B,
+      Tertiary  => Registers.TGL_DP_TP_STATUS_C);
+   DP_TP_STATUS_MIN_IDLES_SENT      : constant := 1 * 2 ** 25;
+
+   DDI_BUF_CTL_BUFFER_ENABLE        : constant :=      1 * 2 ** 31;
+   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_PORT_REVERSAL        : constant :=      1 * 2 ** 16;
+   DDI_BUF_CTL_IDLE_STATUS          : constant :=      1 * 2 **  7;
+   DDI_BUF_CTL_TRANS_SELECT_MASK    : constant := 16#f#  * 2 ** 24;
+
+   DDI_CLK_SEL_SHIFT : constant array (Combo_Port) of Natural :=
+     (DIGI_A => 0,
+      DIGI_B => 2,
+      DIGI_C => 4);
+
+   function DDI_CLK_OFF (Port : TGL_Digital_Port) return Word32
+   is
+     (case Port is
+         when DIGI_A  => 1 * 2 ** 10,
+         when DIGI_B  => 1 * 2 ** 11,
+         when DIGI_C  => 1 * 2 ** 24,
+         when DDI_TC1 => 1 * 2 ** 12,
+         when DDI_TC2 => 1 * 2 ** 13,
+         when DDI_TC3 => 1 * 2 ** 14,
+         when DDI_TC4 => 1 * 2 ** 21,
+         when DDI_TC5 => 1 * 2 ** 22,
+         when DDI_TC6 => 1 * 2 ** 23,
+         when others  => 0);
+
+   function DDI_BUF_CTL (Port : TGL_Digital_Port) return Registers.Registers_Index
+   is
+     (case Port is
+         when DIGI_A  => Registers.DDI_BUF_CTL_A,
+         when DIGI_B  => Registers.DDI_BUF_CTL_B,
+         when DIGI_C  => Registers.DDI_BUF_CTL_C,
+         when DDI_TC1 => Registers.DDI_BUF_CTL_USBC1,
+         when DDI_TC2 => Registers.DDI_BUF_CTL_USBC2,
+         when DDI_TC3 => Registers.DDI_BUF_CTL_USBC3,
+         when DDI_TC4 => Registers.DDI_BUF_CTL_USBC4,
+         when DDI_TC5 => Registers.DDI_BUF_CTL_USBC5,
+         when DDI_TC6 => Registers.DDI_BUF_CTL_USBC6,
+         when others  => Registers.DDI_BUF_CTL_A);
+
+   DDI_CLK_SEL : constant array (USBC_Port) of Registers.Registers_Index :=
+     (DDI_TC1 => Registers.DDI_CLK_SEL_USBC1,
+      DDI_TC2 => Registers.DDI_CLK_SEL_USBC2,
+      DDI_TC3 => Registers.DDI_CLK_SEL_USBC3,
+      DDI_TC4 => Registers.DDI_CLK_SEL_USBC4,
+      DDI_TC5 => Registers.DDI_CLK_SEL_USBC5,
+      DDI_TC6 => Registers.DDI_CLK_SEL_USBC6);
+
+   type Training_Port_Info is record
+      Port  : TGL_Digital_Port;
+      Pipe  : Pipe_Index;
+      eDP   : Boolean;
+   end record;
+
+   function To_DP (Port_Info : Training_Port_Info) return DP_Port is
+     (case Port_Info.Port is
+      when DIGI_A  => DP_A,
+      when DIGI_B  => DP_B,
+      when DIGI_C  => DP_C,
+      when DDI_TC1 => DP_D,
+      when DDI_TC2 => DP_E,
+      when DDI_TC3 => DP_F,
+      when DDI_TC4 => DP_G,
+      when DDI_TC5 => DP_H,
+      when DDI_TC6 => DP_I,
+      when others  => DP_A);
 
    ---------------------------------------------------------------------
 
+   procedure Off (Pipe : Pipe_Index; Port : TGL_Digital_Port)
+   is
+      Enabled : Boolean;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+      Registers.Is_Set_Mask
+        (Register => DDI_BUF_CTL (Port),
+         Mask     => DDI_BUF_CTL_BUFFER_ENABLE,
+         Result   => Enabled);
+
+      if Enabled then
+         Registers.Unset_Mask
+           (Register => DDI_BUF_CTL (Port),
+            Mask     => DDI_BUF_CTL_BUFFER_ENABLE);
+      end if;
+
+      Registers.Unset_Mask
+        (Register   => DP_TP_CTL (Pipe),
+         Mask       => DP_TP_CTL_TRANSPORT_ENABLE);
+
+      if Enabled then
+         Registers.Wait_Set_Mask
+           (Register => DDI_BUF_CTL (Port),
+            Mask     => DDI_BUF_CTL_IDLE_STATUS);
+      end if;
+
+     Registers.Set_Mask
+       (Register => Registers.DPCLKA_CFGCR0,
+        Mask     => DDI_CLK_OFF (Port));
+   end Off;
+
+   procedure Off (Port_Info : Training_Port_Info) is
+   begin
+      Off (Port_Info.Pipe, Port_Info.Port);
+   end Off;
+
+   ---------------------------------------------------------------------
+
+   procedure Set_TP_CTL
+     (Pipe : Pipe_Index;
+      Link : DP_Link;
+      Pattern : DP_Info.Training_Pattern)
+   is
+      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,
+         DP_Info.TP_2    => DP_TP_CTL_LINK_TRAIN_PAT2,
+         DP_Info.TP_3    => DP_TP_CTL_LINK_TRAIN_PAT3,
+         DP_Info.TP_Idle => DP_TP_CTL_LINK_TRAIN_IDLE,
+         DP_Info.TP_None => DP_TP_CTL_LINK_TRAIN_NORMAL);
+
+      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 => DP_TP_CTL (Pipe),
+         Value    => DP_TP_CTL_TRANSPORT_ENABLE or
+                     DP_TP_CTL_Enhanced_Frame or
+                     DP_TP_CTL_MODE_SST or
+                     DP_TP_CTL_LINK_TRAIN (Pattern));
+      Registers.Posting_Read (DP_TP_CTL (Pipe));
+   end Set_TP_CTL;
+
+   procedure Set_Training_Pattern
+     (Port_Info   : Training_Port_Info;
+      Link        : DP_Link;
+      Pattern     : DP_Info.Training_Pattern)
+   is
+      use type DP_Info.Training_Pattern;
+      Pipe : Pipe_Index renames Port_Info.Pipe;
+   begin
+      if Pattern < DP_Info.TP_Idle then
+         Set_TP_CTL (Pipe, Link, Pattern);
+      else
+         Set_TP_CTL (Pipe, Link, DP_Info.TP_Idle);
+         Registers.Wait_Set_Mask
+           (Register => DP_TP_STATUS (Pipe),
+            Mask => DP_TP_STATUS_MIN_IDLES_SENT);
+         Set_TP_CTL (Pipe, Link, DP_Info.TP_None);
+      end if;
+   end Set_Training_Pattern;
+
+   ---------------------------------------------------------------------
+
+   pragma Warnings (GNATprove, Off, "unused variable ""Port_Info""",
+                    Reason => "Needed for a common interface");
+   function Max_V_Swing
+     (Port_Info : Training_Port_Info) return DP_Info.DP_Voltage_Swing
+   is
+     (DP_Info.VS_Level_3);
+
+   function Max_Pre_Emph
+     (Port_Info   : Training_Port_Info;
+      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_Info""");
+
+   ---------------------------------------------------------------------
+
+   procedure Set_Signal_Levels
+     (Port_Info   : Training_Port_Info;
+      Link        : DP_Link;
+      Train_Set   : DP_Info.Train_Set)
+   is
+      Port : TGL_Digital_Port renames Port_Info.Port;
+   begin
+      if Port in Combo_Port then
+         Combo_Phy.Set_Signal_Levels (Port, Port_Info.eDP, Link, Train_Set);
+      elsif Port in USBC_Port then
+         TC.Set_Signal_Levels (Port, Link, Train_Set);
+      end if;
+   end Set_Signal_Levels;
+
+   ---------------------------------------------------------------------
+
+   procedure Map_PLL_To_Port
+     (Port     : TGL_Digital_Port;
+      PLL_Hint : Word32)
+   is
+      CLOCK_SELECT_MASK  : constant := 16#f000_0000#;
+      CLOCK_SELECT_TYPEC : constant := 16#8000_0000#;
+      DDI_CLK_SEL_MASK : constant := 3;
+      Clk_Sel_Shift : Natural;
+   begin
+      if Port in USBC_Port then
+         Registers.Unset_And_Set_Mask
+           (Register   => DDI_CLK_SEL (Port),
+            Mask_Unset => CLOCK_SELECT_MASK,
+            Mask_Set   => CLOCK_SELECT_TYPEC);
+      end if;
+
+      if Port in Combo_Port then
+         Clk_Sel_Shift := DDI_CLK_SEL_SHIFT (Port);
+         Registers.Unset_And_Set_Mask
+           (Register   => Registers.DPCLKA_CFGCR0,
+            Mask_Unset => Shift_Left (DDI_CLK_SEL_MASK, Clk_Sel_Shift),
+            Mask_Set   => Shift_Left (PLL_Hint, Clk_Sel_Shift));
+         Registers.Posting_Read (Registers.DPCLKA_CFGCR0);
+      end if;
+   end Map_PLL_To_Port;
+
+   ---------------------------------------------------------------------
+
+   pragma Warnings (GNAT, Off, """Port_Cfg"" is not modified",
+                    Reason => "Needed for a common interface");
    procedure Prepare
      (Port     : in     Active_Port_Type;
       Port_Cfg : in out Port_Config;
       Success  :    out Boolean)
    is
    begin
-      Success := False;
-   end;
+      if Port_Cfg.Port in USBC_Port then
+         TC.Connect
+           (Port     => Port_Cfg.Port,
+            DP_Alt   => Port in Physical_USBC_Ports,
+            Lanes    => Port_Cfg.DP.Lane_Count,
+            Success  => Success);
+      else
+         Success := True;
+      end if;
+   end Prepare;
+   pragma Warnings (GNAT, On, """Port_Cfg"" is not modified");
 
    ---------------------------------------------------------------------
 
@@ -47,38 +309,96 @@
      (Pipe        : in     Pipe_Index;
       Port_Cfg    : in     Port_Config;
       PLL_Hint    : in     Word32;
-      Success     :    out Boolean) is
+      Success     :    out Boolean)
+   is
+      package Training is new DP_Training
+        (TPS3_Supported    => True,
+         T                 => Training_Port_Info,
+         Aux_T             => DP_Port,
+         Aux_Ch            => HW.GFX.GMA.DP_Aux_Ch,
+         DP_Info           => DP_Info,
+         To_Aux            => To_DP,
+         Max_V_Swing       => Max_V_Swing,
+         Max_Pre_Emph      => Max_Pre_Emph,
+         Set_Pattern       => Set_Training_Pattern,
+         Set_Signal_Levels => Set_Signal_Levels,
+         Off               => Off);
+      Port : TGL_Digital_Port;
    begin
       pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
-      Success := True;
+
+      if Port_Cfg.Port not in TGL_Digital_Port then
+         Success := False;
+         return;
+      end if;
+
+      Port := Port_Cfg.Port;
+      Map_PLL_To_Port (Port, PLL_Hint);
+
+      Registers.Unset_Mask
+        (Register => Registers.DPCLKA_CFGCR0,
+         Mask     => DDI_CLK_OFF (Port));
+
+      if Port_Cfg.Display = DP then
+         if Port in USBC_Port then
+            TC.Program_DP_Mode
+               (Port, Natural (Lane_Count_As_Integer (Port_Cfg.DP.Lane_Count)));
+         end if;
+         Transcoder.Enable_Pipe_Clock (Pipe, Port_Cfg);
+         Transcoder.Configure (Pipe, Port_Cfg, Scale => False);
+
+         Training.Train_DP
+           (Port    => (Port, Pipe, eDP => Port_Cfg.Is_eDP),
+            Link    => Port_Cfg.DP,
+            Success => Success);
+      elsif Port_Cfg.Display = HDMI then
+         Transcoder.Enable_Pipe_Clock (Pipe, Port_Cfg);
+         Transcoder.Configure (Pipe, Port_Cfg, Scale => False);
+
+         Success := True;
+      else
+         Success := False;
+      end if;
    end Pre_On;
 
    procedure Post_On
      (Pipe     : in     Pipe_Index;
       Port_Cfg : in     Port_Config;
       PLL_Hint : in     Word32;
-      Success  :    out Boolean) is
+      Success  :    out Boolean)
+   is
+      Port : GPU_Port renames Port_Cfg.Port;
    begin
-      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
-      Panel.Backlight_On (Port_Cfg.Panel);
       Success := True;
+
+      if Port_Cfg.Display = HDMI then
+         if Port in Combo_Port then
+            Combo_Phy.Enable_HDMI (Port);
+         elsif Port in USBC_Port then
+            TC.Enable_HDMI (Port);
+         end if;
+      end if;
+
+      Panel.Backlight_On (Port_Cfg.Panel);
    end Post_On;
 
+   ---------------------------------------------------------------------
+
    procedure Pre_Off (Pipe : Pipe_Index; Port_Cfg : Port_Config) is
    begin
-      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
       Panel.Backlight_Off (Port_Cfg.Panel);
       Panel.Off (Port_Cfg.Panel);
    end Pre_Off;
 
    procedure Post_Off (Pipe : Pipe_Index; Port_Cfg : Port_Config) is
    begin
-      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+      if Port_Cfg.Port in Combo_Port then
+         Off (Pipe, Port_Cfg.Port);
+      end if;
    end Post_Off;
 
    procedure Pre_All_Off  is
    begin
-      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
       for P in Valid_Panels loop
          Panel.Backlight_Off (P);
          Panel.Off (P);
@@ -87,7 +407,23 @@
 
    procedure Post_All_Off is
    begin
-      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+      for Port in Combo_Port loop
+         Off (Pipe_Index'First, Port); -- pipe index is arbitrary
+      end loop;
    end Post_All_Off;
 
+   procedure Post_Reset_Off is
+   begin
+      for Port in Combo_Port loop
+         Off (Pipe_Index'First, Port);
+      end loop;
+   end Post_Reset_Off;
+
+   procedure Initialize is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      GMA.Combo_Phy.Initialize;
+   end Initialize;
+
 end HW.GFX.GMA.Connectors;
diff --git a/common/tigerlake/hw-gfx-gma-power_and_clocks.adb b/common/tigerlake/hw-gfx-gma-power_and_clocks.adb
index 4e74a82..3425053 100644
--- a/common/tigerlake/hw-gfx-gma-power_and_clocks.adb
+++ b/common/tigerlake/hw-gfx-gma-power_and_clocks.adb
@@ -19,6 +19,7 @@
 with HW.GFX.GMA.PCode;
 with HW.GFX.GMA.Registers;
 with HW.GFX.GMA.Transcoder;
+with HW.GFX.GMA.Connectors.TC;
 
 use type HW.Word64;
 
@@ -38,6 +39,7 @@
    subtype Dynamic_Well    is Power_Domain range PW2 .. PW_Domain'Last;
    subtype Port_Domain     is Power_Domain range DDI_A .. AUX_USBC6;
    subtype DDI_Domain      is Power_Domain range DDI_A .. DDI_USBC6;
+   subtype DDI_USBC_Domain is Power_Domain range DDI_USBC1 .. DDI_USBC6;
    subtype AUX_Domain      is Power_Domain range AUX_A .. AUX_USBC6;
    subtype AUX_USBC_Domain is Power_Domain range AUX_USBC1 .. AUX_USBC6;
 
@@ -214,7 +216,7 @@
          when DDI_USBC5 | AUX_USBC5 => DDI_TC5,
          when DDI_USBC6 | AUX_USBC6 => DDI_TC6);
 
-   procedure Pre_PD_On (PD : Power_Domain)
+   procedure Pre_PD_On (PD : in Power_Domain; Success : out Boolean)
    is
       DP_AUX_CH_CTL_TBT_IO : constant := 1 * 2 ** 11;
    begin
@@ -223,10 +225,14 @@
          Registers.Unset_Mask
            (Register => AUX_CTL_Regs (PD),
             Mask     => DP_AUX_CH_CTL_TBT_IO);
+         Connectors.TC.Claimed (To_GPU_Port (PD), Success);
       elsif PD = PW1 then
          Registers.Wait_Set_Mask
            (Register => Registers.FUSE_STATUS,
-            Mask     => FUSE_STATUS_PG0_DIST_STATUS);
+            Mask     => FUSE_STATUS_PG0_DIST_STATUS,
+            Success  => Success);
+      else
+         Success := True;
       end if;
    end Pre_PD_On;
 
@@ -248,6 +254,17 @@
       end if;
    end Post_PD_On;
 
+   procedure Pre_PD_Off (PD : Power_Domain) is
+   begin
+      if PD in DDI_USBC_Domain then
+         -- Could be moved to a higher level, but right now it's
+         -- convenient to do it here: When requested to turn the
+         -- power off, we know exactly that we don't want to use
+         -- the port (anymore).
+         Connectors.TC.Disconnect (To_GPU_Port (PD));
+      end if;
+   end Pre_PD_Off;
+
    procedure PD_On (PD : Power_Domain)
    is
       Ctl1, Ctl2 : Word32;
@@ -265,7 +282,11 @@
       end if;
 
       if (Ctl2 and Power_Request_Mask (PD)) = 0 then
-         Pre_PD_On (PD);
+         Pre_PD_On (PD, Success);
+         if not Success then
+            pragma Debug (Debug.Put_Line ("Connection flow failed!"));
+            return;
+         end if;
 
          Registers.Set_Mask (PWR_CTL_DRIVER (PD_Type), Power_Request_Mask (PD));
 
@@ -298,6 +319,8 @@
             Mask     => Power_State_Mask (PD),
             TOut_MS  => 1);
 
+         Pre_PD_Off (PD);
+
          Registers.Unset_Mask (PWR_CTL_DRIVER (PD_Type), Power_Request_Mask (PD));
          Registers.Unset_Mask (PWR_CTL_BIOS (PD_Type), Power_Request_Mask (PD));
       end if;
@@ -745,11 +768,20 @@
             PD_On (PW2);
             PD_On (PW3);
          end if;
-
          PD_On (DDI);
+
+         if GPU_Port in USBC_Port then
+            Connectors.TC.Claim
+              (Port     => GPU_Port,
+               DP_Alt   => Port in Physical_USBC_Ports,
+               Success  => Success);
+            if not Success then
+               return;
+            end if;
+         end if;
          PD_On (Aux);
 
-         Success := True;  -- FIXME: Handle port connection flow "claim" errors
+         Success := True;
       end On;
    begin
       pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));