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;