Initial upstream commit

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

Change-Id: I9f5775208f9df6fb29074bf3bc498f68cb17b3a0
Signed-off-by: Nico Huber <nico.huber@secunet.com>
diff --git a/common/ironlake/hw-gfx-gma-connectors-fdi.adb b/common/ironlake/hw-gfx-gma-connectors-fdi.adb
new file mode 100644
index 0000000..2d295f7
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-connectors-fdi.adb
@@ -0,0 +1,342 @@
+--
+-- Copyright (C) 2016 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; version 2 of the License.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.Time;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.PCH.FDI;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors.FDI
+is
+
+   PCH_FDI_CHICKEN_B_AND_C             : constant :=      1 * 2 ** 12;
+
+   type TX_CTL_Regs is array (GPU_FDI_Port) of Registers.Registers_Index;
+   TX_CTL : constant TX_CTL_Regs :=
+     (DIGI_B => Registers.FDI_TX_CTL_A,
+      DIGI_C => Registers.FDI_TX_CTL_B,
+      DIGI_D => Registers.FDI_TX_CTL_C);
+
+   FDI_TX_CTL_FDI_TX_ENABLE            : constant :=      1 * 2 ** 31;
+   FDI_TX_CTL_VP_MASK                  : constant := 16#3f# * 2 ** 22;
+   FDI_TX_CTL_PORT_WIDTH_SEL_SHIFT     : constant :=               19;
+   FDI_TX_CTL_ENHANCED_FRAMING_ENABLE  : constant :=      1 * 2 ** 18;
+   FDI_TX_CTL_FDI_PLL_ENABLE           : constant :=      1 * 2 ** 14;
+   FDI_TX_CTL_COMPOSITE_SYNC_SELECT    : constant :=      1 * 2 ** 11;
+   FDI_TX_CTL_AUTO_TRAIN_ENABLE        : constant :=      1 * 2 ** 10;
+   FDI_TX_CTL_AUTO_TRAIN_DONE          : constant :=      1 * 2 **  1;
+
+   TP_SHIFT : constant := (if Config.CPU <= Sandybridge then 28 else 8);
+   FDI_TX_CTL_TRAINING_PATTERN_MASK    : constant := 3 * 2 ** TP_SHIFT;
+   FDI_TX_CTL_TRAINING_PATTERN_1       : constant := 0 * 2 ** TP_SHIFT;
+   FDI_TX_CTL_TRAINING_PATTERN_2       : constant := 1 * 2 ** TP_SHIFT;
+   FDI_TX_CTL_TRAINING_PATTERN_IDLE    : constant := 2 * 2 ** TP_SHIFT;
+   FDI_TX_CTL_TRAINING_PATTERN_NORMAL  : constant := 3 * 2 ** TP_SHIFT;
+
+   subtype FDI_TX_CTL_VP_T is Natural range 0 .. 3;
+   type Vswing_Preemph_Values is array (FDI_TX_CTL_VP_T) of Word32;
+   FDI_TX_CTL_VSWING_PREEMPH : constant Vswing_Preemph_Values :=
+     (0 => 16#00# * 2 ** 22,
+      1 => 16#3a# * 2 ** 22,
+      2 => 16#39# * 2 ** 22,
+      3 => 16#38# * 2 ** 22);
+
+   function FDI_TX_CTL_PORT_WIDTH_SEL (Lane_Count : DP_Lane_Count) return Word32
+   is
+   begin
+      return Shift_Left
+        (Word32 (Lane_Count_As_Integer (Lane_Count)) - 1,
+         FDI_TX_CTL_PORT_WIDTH_SEL_SHIFT);
+   end FDI_TX_CTL_PORT_WIDTH_SEL;
+
+   ----------------------------------------------------------------------------
+
+   --
+   -- This is usually used with Ivy Bridge.
+   --
+   procedure Auto_Training
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   with
+      Pre => Port_Cfg.Port in GPU_FDI_Port
+   is
+      PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      -- try each preemph/voltage pair twice
+      for VP2 in Natural range 0 .. FDI_TX_CTL_VP_T'Last * 2 + 1
+      loop
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_VP_MASK or
+                           FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_FDI_TX_ENABLE or
+                           FDI_TX_CTL_VSWING_PREEMPH (VP2 / 2) or
+                           FDI_TX_CTL_AUTO_TRAIN_ENABLE or
+                           FDI_TX_CTL_TRAINING_PATTERN_1);
+         Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+         PCH.FDI.Auto_Train (PCH_FDI_Port);
+
+         -- read at least twice
+         for I in 0 .. 3 loop
+            Registers.Is_Set_Mask
+              (Register => TX_CTL (Port_Cfg.Port),
+               Mask     => FDI_TX_CTL_AUTO_TRAIN_DONE,
+               Result   => Success);
+            exit when Success or I = 3;
+            Time.U_Delay (1);
+         end loop;
+         exit when Success;
+
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_FDI_TX_ENABLE or
+                           FDI_TX_CTL_AUTO_TRAIN_ENABLE or
+                           FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_1);
+
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
+      end loop;
+
+      if Success then
+         PCH.FDI.Enable_EC (PCH_FDI_Port);
+      else
+         Registers.Unset_Mask
+           (Register => TX_CTL (Port_Cfg.Port),
+            Mask     => FDI_TX_CTL_FDI_PLL_ENABLE);
+
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
+      end if;
+   end Auto_Training;
+
+   ----------------------------------------------------------------------------
+
+   --
+   -- Used with Sandy Bridge (should work with Ivy Bridge too)
+   --
+   procedure Full_Training
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   with
+      Pre => Port_Cfg.Port in GPU_FDI_Port
+   is
+      PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      -- try each preemph/voltage pair twice
+      for VP2 in Natural range 0 .. FDI_TX_CTL_VP_T'Last * 2 + 1
+      loop
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_VP_MASK or
+                           FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_FDI_TX_ENABLE or
+                           FDI_TX_CTL_VSWING_PREEMPH (VP2 / 2) or
+                           FDI_TX_CTL_TRAINING_PATTERN_1);
+         Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+         PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_1, Success);
+
+         if Success then
+            Registers.Unset_And_Set_Mask
+              (Register    => TX_CTL (Port_Cfg.Port),
+               Mask_Unset  => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+               Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_2);
+            Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+            PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_2, Success);
+         end if;
+         exit when Success;
+
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_FDI_TX_ENABLE or
+                           FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_1);
+
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
+      end loop;
+
+      if Success then
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_NORMAL);
+         Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+         PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_None, Success);
+      else
+         Registers.Unset_Mask
+           (Register => TX_CTL (Port_Cfg.Port),
+            Mask     => FDI_TX_CTL_FDI_PLL_ENABLE);
+
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
+      end if;
+   end Full_Training;
+
+   ----------------------------------------------------------------------------
+
+   --
+   -- Used with original Ironlake (Nehalem CPU)
+   --
+   -- This is close to what Linux' i915 does. A comment in i915_reg.h
+   -- states that it uses only the lowest voltage / pre-emphasis levels
+   -- which is why we leave them at zero here and don't try different
+   -- values.
+   --
+   -- It's actually not clear from i915's code if the values really are
+   -- at zero or if it's just reusing what the Video BIOS set. Some code
+   -- in coreboot sets them to zero explicitly.
+   --
+   procedure Simple_Training
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   with
+      Pre => Port_Cfg.Port in GPU_FDI_Port
+   is
+      PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+        (Register    => TX_CTL (Port_Cfg.Port),
+         Mask_Unset  => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+         Mask_Set    => FDI_TX_CTL_FDI_TX_ENABLE or
+                        FDI_TX_CTL_TRAINING_PATTERN_1);
+      Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+      PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_1, Success);
+
+      if Success then
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_2);
+         Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+         PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_2, Success);
+      end if;
+
+      if Success then
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_NORMAL);
+         Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+         PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_None, Success);
+      else
+         Registers.Unset_And_Set_Mask
+           (Register    => TX_CTL (Port_Cfg.Port),
+            Mask_Unset  => FDI_TX_CTL_FDI_TX_ENABLE or
+                           FDI_TX_CTL_TRAINING_PATTERN_MASK,
+            Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_1);
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
+
+         Registers.Unset_Mask
+           (Register => TX_CTL (Port_Cfg.Port),
+            Mask     => FDI_TX_CTL_FDI_PLL_ENABLE);
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
+      end if;
+   end Simple_Training;
+
+   ----------------------------------------------------------------------------
+
+   procedure Pre_On (Port_Cfg : Port_Config)
+   is
+      Composite_Sel : constant :=
+        (if Config.Has_FDI_Composite_Sel then
+            FDI_TX_CTL_COMPOSITE_SYNC_SELECT else 0);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      -- The PCH_FDI_CHICKEN_B_AND_C bit may not be changed when any of
+      -- both ports is active. Bandwidth calculations before calling us
+      -- should ensure this.
+      if Config.Has_FDI_C then
+         if Port_Cfg.Port = DIGI_D or
+            (Port_Cfg.Port = DIGI_C and
+             Port_Cfg.FDI.Lane_Count <= DP_Lane_Count_2)
+         then
+            Registers.Set_Mask
+              (Register => Registers.PCH_FDI_CHICKEN_B_C,
+               Mask     => PCH_FDI_CHICKEN_B_AND_C);
+         elsif Port_Cfg.Port = DIGI_C then
+            Registers.Unset_Mask
+              (Register => Registers.PCH_FDI_CHICKEN_B_C,
+               Mask     => PCH_FDI_CHICKEN_B_AND_C);
+         end if;
+      end if;
+
+      PCH.FDI.Pre_Train (PCH_FDIs (Port_Cfg.Port), Port_Cfg);
+
+      Registers.Write
+        (Register => TX_CTL (Port_Cfg.Port),
+         Value    => FDI_TX_CTL_PORT_WIDTH_SEL (Port_Cfg.FDI.Lane_Count) or
+                     FDI_TX_CTL_ENHANCED_FRAMING_ENABLE or
+                     FDI_TX_CTL_FDI_PLL_ENABLE or
+                     Composite_Sel or
+                     FDI_TX_CTL_TRAINING_PATTERN_1);
+      Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+      Time.U_Delay (100);
+   end Pre_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Post_On
+     (Port_Cfg : in     Port_Config;
+      Success  :    out Boolean)
+   is
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      case Config.FDI_Training is
+         when GMA.Simple_Training   => Simple_Training (Port_Cfg, Success);
+         when GMA.Full_Training     => Full_Training (Port_Cfg, Success);
+         when GMA.Auto_Training     => Auto_Training (Port_Cfg, Success);
+      end case;
+   end Post_On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Off (Port : GPU_FDI_Port; OT : Off_Type)
+   is
+      PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port);
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      Registers.Unset_And_Set_Mask
+        (Register    => TX_CTL (Port),
+         Mask_Unset  => FDI_TX_CTL_FDI_TX_ENABLE or
+                        FDI_TX_CTL_AUTO_TRAIN_ENABLE or
+                        FDI_TX_CTL_TRAINING_PATTERN_MASK,
+         Mask_Set    => FDI_TX_CTL_TRAINING_PATTERN_1);
+
+      PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
+
+      if OT >= Clock_Off then
+         Registers.Unset_Mask
+           (Register => TX_CTL (Port),
+            Mask     => FDI_TX_CTL_FDI_PLL_ENABLE);
+
+         PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
+      end if;
+   end Off;
+
+end HW.GFX.GMA.Connectors.FDI;