353 lines
13 KiB
Ada
353 lines
13 KiB
Ada
--
|
|
-- 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; 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.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
|
|
|
|
use all type PCH.FDI.Training_Pattern;
|
|
|
|
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;
|
|
|
|
function TP_SHIFT return Natural is
|
|
(if Config.Has_New_FDI_Source then 8 else 28);
|
|
|
|
function FDI_TX_CTL_TRAINING_PATTERN_MASK
|
|
return Word32 is (Shift_Left (3, TP_SHIFT));
|
|
|
|
function FDI_TX_CTL_TRAINING_PATTERN (TP : PCH.FDI.Training_Pattern)
|
|
return Word32 is
|
|
(case TP is
|
|
when TP_1 => Shift_Left (0, TP_SHIFT),
|
|
when TP_2 => Shift_Left (1, TP_SHIFT),
|
|
when TP_Idle => Shift_Left (2, TP_SHIFT),
|
|
when TP_None => Shift_Left (3, 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 (TP_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 (TP_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 (TP_1));
|
|
Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
|
|
|
|
PCH.FDI.Train (PCH_FDI_Port, 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 (TP_2));
|
|
Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
|
|
|
|
PCH.FDI.Train (PCH_FDI_Port, 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 (TP_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 (TP_None));
|
|
Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
|
|
|
|
PCH.FDI.Train (PCH_FDI_Port, 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 (TP_1));
|
|
Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
|
|
|
|
PCH.FDI.Train (PCH_FDI_Port, 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 (TP_2));
|
|
Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
|
|
|
|
PCH.FDI.Train (PCH_FDI_Port, 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 (TP_None));
|
|
Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
|
|
|
|
PCH.FDI.Train (PCH_FDI_Port, 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 (TP_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 Word32 :=
|
|
(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 (TP_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 (TP_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;
|