| -- |
| -- Copyright (C) 2022 Google, LLC |
| -- |
| -- 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.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 |
| |
| 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; |
| |
| 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 |
| 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"); |
| |
| --------------------------------------------------------------------- |
| |
| procedure Pre_On |
| (Pipe : in Pipe_Index; |
| Port_Cfg : in Port_Config; |
| PLL_Hint : in Word32; |
| 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)); |
| |
| 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 |
| Port : GPU_Port renames Port_Cfg.Port; |
| begin |
| 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 |
| 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 |
| if Port_Cfg.Port in Combo_Port then |
| Off (Pipe, Port_Cfg.Port); |
| end if; |
| end Post_Off; |
| |
| procedure Pre_All_Off is |
| begin |
| for P in Valid_Panels loop |
| Panel.Backlight_Off (P); |
| Panel.Off (P); |
| end loop; |
| end Pre_All_Off; |
| |
| procedure Post_All_Off is |
| begin |
| 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; |