gma ilk: Handle Ibex Peak DP correctly

We missed some bits specific to Ibex Peak DPs. Namely, the training
pattern selection moved and we have to select the transcoder in the
`DP_CTL` register as there is no separate DP transcoder register.

The latter is quirky, however. If a port (DP or HDMI) is disabled
with the second transcoder selected. This selection sticks and the
sibling port (e.g. DP2 to HDMI2) can't be enabled with the first
transcoder. As a workaround, we are supposed to enable a port that
was using the second transcoder with the first transcoder selected
again.

Change-Id: I0c1fafc2ba113aeeaf4ace750b333cc270bca37c
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/c/libgfxinit/+/35740
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
diff --git a/common/ironlake/hw-gfx-gma-connectors.adb b/common/ironlake/hw-gfx-gma-connectors.adb
index 9bb0b68..21eca97 100644
--- a/common/ironlake/hw-gfx-gma-connectors.adb
+++ b/common/ironlake/hw-gfx-gma-connectors.adb
@@ -88,7 +88,7 @@
                elsif Port_Cfg.PCH_Port in PCH_HDMI_Port then
                   PCH.HDMI.On (Port_Cfg, FDI_Port);
                elsif Port_Cfg.PCH_Port in PCH_DP_Port then
-                  PCH.DP.On (Port_Cfg, Success);
+                  PCH.DP.On (Port_Cfg, FDI_Port, Success);
                end if;
             end if;
          end;
diff --git a/common/ironlake/hw-gfx-gma-pch-dp.adb b/common/ironlake/hw-gfx-gma-pch-dp.adb
index 15d3720..72244d5 100644
--- a/common/ironlake/hw-gfx-gma-pch-dp.adb
+++ b/common/ironlake/hw-gfx-gma-pch-dp.adb
@@ -34,15 +34,25 @@
    DP_CTL_PREEMPH_LEVEL_SET_SHIFT      : constant :=          22;
    DP_CTL_PREEMPH_LEVEL_SET_MASK       : constant := 7 * 2 ** 22;
    DP_CTL_PORT_WIDTH_SHIFT             : constant :=          19;
+   DP_CTL_ENHANCED_FRAMING_ENABLE      : constant := 1 * 2 ** 18;
    DP_CTL_PORT_REVERSAL                : constant := 1 * 2 ** 15;
-   DP_CTL_LINK_TRAIN_MASK              : constant := 7 * 2 **  8;
-   DP_CTL_LINK_TRAIN_PAT1              : constant := 0 * 2 **  8;
-   DP_CTL_LINK_TRAIN_PAT2              : constant := 1 * 2 **  8;
-   DP_CTL_LINK_TRAIN_IDLE              : constant := 2 * 2 **  8;
-   DP_CTL_LINK_TRAIN_NORMAL            : constant := 3 * 2 **  8;
    DP_CTL_AUDIO_OUTPUT_ENABLE          : constant := 1 * 2 **  6;
    DP_CTL_PORT_DETECT                  : constant := 1 * 2 **  2;
 
+   function DP_CTL_LINK_TRAIN_SHIFT return Natural is
+     (if Config.Has_Trans_DP_Ctl then 8 else 28);
+
+   function DP_CTL_LINK_TRAIN_MASK return Word32 is
+     (if Config.Has_Trans_DP_Ctl then 7 * 2 ** 8 else 3 * 2 ** 28);
+
+   function DP_CTL_LINK_TRAIN (Pat : DP_Info.Training_Pattern) return Word32 is
+     (case Pat is
+         when DP_Info.TP_1      => Shift_Left (0, DP_CTL_LINK_TRAIN_SHIFT),
+         when DP_Info.TP_2      => Shift_Left (1, DP_CTL_LINK_TRAIN_SHIFT),
+         when DP_Info.TP_3      => Shift_Left (1, DP_CTL_LINK_TRAIN_SHIFT),
+         when DP_Info.TP_Idle   => Shift_Left (2, DP_CTL_LINK_TRAIN_SHIFT),
+         when DP_Info.TP_None   => Shift_Left (3, DP_CTL_LINK_TRAIN_SHIFT));
+
    function DP_CTL_VSWING_LEVEL_SET
      (VS : DP_Info.DP_Voltage_Swing)
       return Word32
@@ -68,14 +78,6 @@
          DP_CTL_PORT_WIDTH_SHIFT);
    end DP_CTL_PORT_WIDTH;
 
-   type DP_CTL_LINK_TRAIN_Array is array (DP_Info.Training_Pattern) of Word32;
-   DP_CTL_LINK_TRAIN : constant DP_CTL_LINK_TRAIN_Array :=
-     (DP_Info.TP_1      => DP_CTL_LINK_TRAIN_PAT1,
-      DP_Info.TP_2      => DP_CTL_LINK_TRAIN_PAT2,
-      DP_Info.TP_3      => DP_CTL_LINK_TRAIN_PAT2,
-      DP_Info.TP_Idle   => DP_CTL_LINK_TRAIN_IDLE,
-      DP_Info.TP_None   => DP_CTL_LINK_TRAIN_NORMAL);
-
    ----------------------------------------------------------------------------
 
    pragma Warnings (GNATprove, Off, "unused variable ""Port""",
@@ -134,17 +136,43 @@
 
    procedure Off (Port : PCH_DP_Port)
    is
+      With_Transcoder_B_Enabled : Boolean := False;
    begin
       pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
 
+      if not Config.Has_Trans_DP_Ctl then
+         -- Ensure transcoder select isn't set to B,
+         -- disabled DP may block HDMI otherwise.
+         Registers.Is_Set_Mask
+           (Register => DP_CTL (Port),
+            Mask     => DP_CTL_DISPLAY_PORT_ENABLE or
+                        PCH_TRANSCODER_SELECT (FDI_B),
+            Result   => With_Transcoder_B_Enabled);
+      end if;
+
       Registers.Unset_And_Set_Mask
         (Register    => DP_CTL (Port),
          Mask_Unset  => DP_CTL_LINK_TRAIN_MASK,
-         Mask_Set    => DP_CTL_LINK_TRAIN_IDLE);
+         Mask_Set    => DP_CTL_LINK_TRAIN (DP_Info.TP_Idle));
       Registers.Posting_Read (DP_CTL (Port));
 
-      Registers.Write (DP_CTL (Port), 0);
+      Registers.Unset_Mask (DP_CTL (Port), DP_CTL_DISPLAY_PORT_ENABLE);
       Registers.Posting_Read (DP_CTL (Port));
+
+      if not Config.Has_Trans_DP_Ctl and then With_Transcoder_B_Enabled then
+         -- Reenable with transcoder A selected to switch.
+         Registers.Unset_And_Set_Mask
+           (Register    => DP_CTL (Port),
+            Mask_Unset  => PCH_TRANSCODER_SELECT_MASK or
+                           DP_CTL_LINK_TRAIN_MASK,
+            Mask_Set    => DP_CTL_DISPLAY_PORT_ENABLE or
+                           PCH_TRANSCODER_SELECT (FDI_A) or
+                           DP_CTL_LINK_TRAIN (DP_Info.TP_1));
+         Registers.Posting_Read (DP_CTL (Port));
+         Registers.Unset_Mask (DP_CTL (Port), DP_CTL_DISPLAY_PORT_ENABLE);
+         Registers.Posting_Read (DP_CTL (Port));
+      end if;
+
    end Off;
    pragma Warnings (GNATprove, On, "unused variable ""Port""");
    pragma Warnings (GNATprove, On, "unused variable ""Link""");
@@ -153,6 +181,7 @@
 
    procedure On
      (Port_Cfg : in     Port_Config;
+      FDI_Port : in     FDI_Port_Type;
       Success  :    out Boolean)
    is
       function To_DP (Port : PCH_DP_Port) return DP_Port
@@ -176,14 +205,23 @@
          Set_Pattern       => Set_Training_Pattern,
          Set_Signal_Levels => Set_Signal_Levels,
          Off               => Off);
+
+      DP_CTL_Transcoder_Select : constant Word32 :=
+        (if Config.Has_Trans_DP_Ctl
+         then 0 else PCH_TRANSCODER_SELECT (FDI_Port));
+      DP_CTL_Enhanced_Framing : constant Word32 :=
+        (if Config.Has_Trans_DP_Ctl
+         then 0 else DP_CTL_ENHANCED_FRAMING_ENABLE);
    begin
       pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
 
       Registers.Write
         (Register => DP_CTL (Port_Cfg.PCH_Port),
          Value    => DP_CTL_DISPLAY_PORT_ENABLE or
+                     DP_CTL_Transcoder_Select or
                      DP_CTL_PORT_WIDTH (Port_Cfg.DP.Lane_Count) or
-                     DP_CTL_LINK_TRAIN_PAT1);
+                     DP_CTL_Enhanced_Framing or
+                     DP_CTL_LINK_TRAIN (DP_Info.TP_1));
 
       Training.Train_DP
         (Port     => Port_Cfg.PCH_Port,
diff --git a/common/ironlake/hw-gfx-gma-pch-dp.ads b/common/ironlake/hw-gfx-gma-pch-dp.ads
index c2ab299..83c546d 100644
--- a/common/ironlake/hw-gfx-gma-pch-dp.ads
+++ b/common/ironlake/hw-gfx-gma-pch-dp.ads
@@ -17,6 +17,7 @@
 
    procedure On
      (Port_Cfg : in     Port_Config;
+      FDI_Port : in     FDI_Port_Type;
       Success  :    out Boolean)
    with
       Pre => Port_Cfg.PCH_Port in PCH_DP_Port;
diff --git a/common/ironlake/hw-gfx-gma-pch-hdmi.adb b/common/ironlake/hw-gfx-gma-pch-hdmi.adb
index aaf7951..9426a86 100644
--- a/common/ironlake/hw-gfx-gma-pch-hdmi.adb
+++ b/common/ironlake/hw-gfx-gma-pch-hdmi.adb
@@ -72,14 +72,35 @@
 
    procedure Off (Port : PCH_HDMI_Port)
    is
+      With_Transcoder_B_Enabled : Boolean := False;
    begin
       pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
 
+      if not Config.Has_Trans_DP_Ctl then
+         -- Ensure transcoder select isn't set to B,
+         -- disabled HDMI may block DP otherwise.
+         Registers.Is_Set_Mask
+           (Register => PCH_HDMI (Port),
+            Mask     => PCH_HDMI_ENABLE or
+                        PCH_TRANSCODER_SELECT (FDI_B),
+            Result   => With_Transcoder_B_Enabled);
+      end if;
+
       Registers.Unset_And_Set_Mask
          (Register   => PCH_HDMI (Port),
           Mask_Unset => PCH_HDMI_MASK,
           Mask_Set   => PCH_HDMI_HSYNC_ACTIVE_HIGH or
                         PCH_HDMI_VSYNC_ACTIVE_HIGH);
+      Registers.Posting_Read (PCH_HDMI (Port));
+
+      if not Config.Has_Trans_DP_Ctl and then With_Transcoder_B_Enabled then
+         -- Reenable with transcoder A selected to switch.
+         Registers.Set_Mask (PCH_HDMI (Port), PCH_HDMI_ENABLE);
+         Registers.Posting_Read (PCH_HDMI (Port));
+         Registers.Unset_Mask (PCH_HDMI (Port), PCH_HDMI_ENABLE);
+         Registers.Posting_Read (PCH_HDMI (Port));
+      end if;
+
    end Off;
 
    procedure All_Off