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/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;