gma tgl: Add support for allocating PLLs
This patch adds support for allocating both combo PHY PLLs and USB
Type-C PLLs for Tiger Lake.
Verified Combo PHY (HDMI, eDP) and Type-C (DP Alt mode) on
Google/delbin.
Squashed clean-ups:
gma tgl: Clean up the code for gfx plls
This patch improves code quality through various optimizations, such as
replacing indexed array access with direct value access and simplifying
variable declarations. It also simplifies function rewrites by using
constructs like case-when instead of multiple range mappings or if else
blocks. Additionally, the patch addresses minor typographical errors.
These changes enhance the code's readability and maintainability
without impacting functionality.
Signed-off-by: Dinesh Gehlot <digehlot@google.com>
gma tgl: Rename DKL PLL to Dekel_Phy
This patch changes the name of a PLL "DKL" to "Dekel_Phy" to maintain
consistency with the naming convention of other PHY PLLs
Signed-off-by: Dinesh Gehlot <digehlot@google.com>
Change-Id: I9dd7e0d0180f70d73eb50d7e58718261e5e74071
Signed-off-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Reviewed-on: https://review.sourcearcade.org/c/libgfxinit/+/465
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Tested-by: Nico Huber <nico.h@gmx.de>
diff --git a/common/tigerlake/hw-gfx-gma-plls-dekel_phy.adb b/common/tigerlake/hw-gfx-gma-plls-dekel_phy.adb
new file mode 100644
index 0000000..9172dad
--- /dev/null
+++ b/common/tigerlake/hw-gfx-gma-plls-dekel_phy.adb
@@ -0,0 +1,400 @@
+--
+-- 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.Config;
+with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.Power_And_Clocks;
+
+package body HW.GFX.GMA.PLLs.Dekel_Phy is
+
+ use type HW.Word64;
+
+ subtype Frequency_KHz is Pos64
+ range (Frequency_Type'First / 1_000) .. (Frequency_Type'Last / 1_000);
+ subtype DCO_Range_KHz is Pos64 range 7_992_000 .. 10_000_000;
+
+ DPLL_ENABLE_PLL_ENABLE : constant := 1 * 2 ** 31;
+ DPLL_ENABLE_PLL_LOCK : constant := 1 * 2 ** 30;
+ DPLL_ENABLE_POWER_ENABLE : constant := 1 * 2 ** 27;
+ DPLL_ENABLE_POWER_STATE : constant := 1 * 2 ** 26;
+
+ function HIP_INDEX_REG (P : DKL_DPLLs) return Registers.Registers_Index
+ is (if P <= TCPLL4
+ then Registers.HIP_INDEX_REG0
+ else Registers.HIP_INDEX_REG1);
+
+ function HIP_INDEX_VAL (P : DKL_DPLLs; Val : Word32) return Word32 is
+ (case P is
+ when TCPLL1 => Val * 2 ** 0,
+ when TCPLL2 => Val * 2 ** 8,
+ when TCPLL3 => Val * 2 ** 16,
+ when TCPLL4 => Val * 2 ** 24,
+ when TCPLL5 => Val * 2 ** 0,
+ when TCPLL6 => Val * 2 ** 8);
+
+ function DKL_PLL_ENABLE (P : DKL_DPLLs) return Registers.Registers_Index is
+ (if Config.Has_New_Type_C_PLL_Enable then
+ (case P is
+ when TCPLL1 => Registers.PORTTC1_PLL1_ENABLE,
+ when TCPLL2 => Registers.PORTTC2_PLL1_ENABLE,
+ when TCPLL3 => Registers.PORTTC3_PLL1_ENABLE,
+ when TCPLL4 => Registers.PORTTC4_PLL1_ENABLE,
+ when TCPLL5 => Registers.MGPLL5_ENABLE,
+ when TCPLL6 => Registers.MGPLL6_ENABLE)
+ else
+ (case P is
+ when TCPLL1 => Registers.MGPLL1_ENABLE,
+ when TCPLL2 => Registers.MGPLL2_ENABLE,
+ when TCPLL3 => Registers.MGPLL3_ENABLE,
+ when TCPLL4 => Registers.MGPLL4_ENABLE,
+ when TCPLL5 => Registers.MGPLL5_ENABLE,
+ when TCPLL6 => Registers.MGPLL6_ENABLE));
+
+ type PLL_Regs_Record is record
+ DKL_REFCLKIN_CTL : Registers.Registers_Index;
+ DKL_CLKTOP2_CORECLKCTL1 : Registers.Registers_Index;
+ DKL_CLKTOP2_HSCLKCTL : Registers.Registers_Index;
+ DKL_PLL_DIV0 : Registers.Registers_Index;
+ DKL_PLL_DIV1 : Registers.Registers_Index;
+ DKL_PLL_SSC : Registers.Registers_Index;
+ DKL_PLL_BIAS : Registers.Registers_Index;
+ DKL_PLL_COLDST_BIAS : Registers.Registers_Index;
+ end record;
+
+ type PLL_Regs_Array is array (DKL_DPLLs) of PLL_Regs_Record;
+ PLL_Regs : constant PLL_Regs_Array :=
+ PLL_Regs_Array'
+ (TCPLL1 =>
+ (Registers.DKL_REFCLKIN_CTL_1,
+ Registers.DKL_CLKTOP2_CCC1_1,
+ Registers.DKL_CLKTOP2_HSCC_1,
+ Registers.DKL_PLL_DIV0_1,
+ Registers.DKL_PLL_DIV1_1,
+ Registers.DKL_PLL_SSC_1,
+ Registers.DKL_PLL_BIAS_1,
+ Registers.DKL_PLL_COLDST_BIAS_1),
+ TCPLL2 =>
+ (Registers.DKL_REFCLKIN_CTL_2,
+ Registers.DKL_CLKTOP2_CCC1_2,
+ Registers.DKL_CLKTOP2_HSCC_2,
+ Registers.DKL_PLL_DIV0_2,
+ Registers.DKL_PLL_DIV1_2,
+ Registers.DKL_PLL_SSC_2,
+ Registers.DKL_PLL_BIAS_2,
+ Registers.DKL_PLL_COLDST_BIAS_2),
+ TCPLL3 =>
+ (Registers.DKL_REFCLKIN_CTL_3,
+ Registers.DKL_CLKTOP2_CCC1_3,
+ Registers.DKL_CLKTOP2_HSCC_3,
+ Registers.DKL_PLL_DIV0_3,
+ Registers.DKL_PLL_DIV1_3,
+ Registers.DKL_PLL_SSC_3,
+ Registers.DKL_PLL_BIAS_3,
+ Registers.DKL_PLL_COLDST_BIAS_3),
+ TCPLL4 =>
+ (Registers.DKL_REFCLKIN_CTL_4,
+ Registers.DKL_CLKTOP2_CCC1_4,
+ Registers.DKL_CLKTOP2_HSCC_4,
+ Registers.DKL_PLL_DIV0_4,
+ Registers.DKL_PLL_DIV1_4,
+ Registers.DKL_PLL_SSC_4,
+ Registers.DKL_PLL_BIAS_4,
+ Registers.DKL_PLL_COLDST_BIAS_4),
+ TCPLL5 =>
+ (Registers.DKL_REFCLKIN_CTL_5,
+ Registers.DKL_CLKTOP2_CCC1_5,
+ Registers.DKL_CLKTOP2_HSCC_5,
+ Registers.DKL_PLL_DIV0_5,
+ Registers.DKL_PLL_DIV1_5,
+ Registers.DKL_PLL_SSC_5,
+ Registers.DKL_PLL_BIAS_5,
+ Registers.DKL_PLL_COLDST_BIAS_5),
+ TCPLL6 =>
+ (Registers.DKL_REFCLKIN_CTL_6,
+ Registers.DKL_CLKTOP2_CCC1_6,
+ Registers.DKL_CLKTOP2_HSCC_6,
+ Registers.DKL_PLL_DIV0_6,
+ Registers.DKL_PLL_DIV1_6,
+ Registers.DKL_PLL_SSC_6,
+ Registers.DKL_PLL_BIAS_6,
+ Registers.DKL_PLL_COLDST_BIAS_6));
+
+ DKL_REFCLKIN_CTL_OD_2_MUX_MASK : constant := 7 * 2 ** 8;
+ DKL_CLKTOP2_CORECLKCTL1_A_DIVRATIO_MASK : constant := 16#ff# * 2 ** 8;
+ DKL_CLKTOP2_HSCLKCTL_MASK : constant := 16#1_FF00#;
+ DKL_PLL_DIV0_MASK : constant := 16#1F_FFFF#;
+ DKL_PLL_DIV1_MASK : constant := 16#1F_00FF#;
+ DKL_PLL_SSC_MASK : constant := 16#E0FF_3A00#;
+ DKL_PLL_BIAS_MASK : constant := 16#7FFF_FF00#;
+ DKL_PLL_COLD_BIAS_MASK : constant := 16#ffff#;
+ DKL_PLL_BIAS_FRAC_EN_H : constant := 1 * 2 ** 30;
+
+ procedure Calc_Dividers
+ (Clock : in Frequency_Type;
+ Display : in Display_Type;
+ DKL_Refclkin_Ctl : out Word32;
+ DKL_Clktop2_Coreclkctl1 : out Word32;
+ DKL_Clktop2_HSClkCtl : out Word32;
+ DCO_Khz : out DCO_Range_KHz;
+ Success : out Boolean)
+ is
+ DCO_Min_Freq, DCO_Max_Freq : Int64;
+ type Dividers_List is array (1 .. 4) of Int64;
+ Dividers : constant Dividers_List := (7, 5, 3, 2);
+ DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_2 : constant := 0 * 2 ** 12;
+ DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_3 : constant := 1 * 2 ** 12;
+ DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_5 : constant := 2 * 2 ** 12;
+ DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_7 : constant := 3 * 2 ** 12;
+ function DKL_REFCLKIN_CTL_OD_2_MUX (N : Word32) return Word32
+ is (Shift_Left (N, 8));
+ function DKL_CLKTOP2_CORECLKCTL1_A_DIVRATIO (N : Word32) return Word32
+ is (Shift_Left (N, 8));
+ function DKL_CLKTOP2_HSCLKCTL_TLINEDRV_CLKSEL (N : Word32) return Word32
+ is (Shift_Left (N, 14));
+ function DKL_CLKTOP2_HSCLKCTL_CORE_INPUTSEL (N : Word32) return Word32
+ is (Shift_Left (N, 16));
+ function DKL_CLKTOP2_HSCLKCTL_DSDIV_RATIO (N : Word32) return Word32
+ is (Shift_Left (N, 8));
+ Clock_Khz : constant Frequency_KHz := Frequency_KHz(Clock / 1_000);
+ begin
+ if Display = DP then
+ DCO_Min_Freq := 8_100_000;
+ DCO_Max_Freq := 8_100_000;
+ else
+ DCO_Min_Freq := DCO_Range_KHz'First;
+ DCO_Max_Freq := DCO_Range_KHz'Last;
+ end if;
+
+ DKL_Refclkin_Ctl := 0;
+ DKL_clktop2_coreclkctl1 := 0;
+ DKL_CLKTOP2_hsclkctl := 0;
+
+ DCO_KHz := DCO_Min_Freq;
+ Success := False;
+ for I in Dividers'Range loop
+ pragma Loop_Invariant (DCO_KHz >= DCO_Min_Freq and DCO_KHz <= DCO_Max_Freq);
+ for Div2 in reverse 1 .. 10 loop
+ declare
+ Tmp : constant Pos64 := Dividers (I) * Pos64 (Div2) * Pos64(Clock_Khz) * 5;
+ A_DivRatio, TLineDrv, Inputsel, Hsdiv : Word32;
+ begin
+ if Tmp >= DCO_Min_Freq and then Tmp <= DCO_Max_Freq then
+ DCO_KHz := Tmp;
+ if Div2 >= 2 then
+ A_DivRatio := (if Display = DP then 10 else 5);
+ TLineDrv := 1;
+ else
+ A_DivRatio := 5;
+ TLineDrv := 0;
+ end if;
+ Inputsel := (if Display = DP then 0 else 1);
+ Hsdiv := (case Dividers (I) is
+ when 2 => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_2,
+ when 3 => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_3,
+ when 5 => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_5,
+ when 7 => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_7,
+ when others => DKL_CLKTOP2_HSCLKCTL_HSDIV_RATIO_2);
+ DKL_Refclkin_Ctl := DKL_REFCLKIN_CTL_OD_2_MUX (1);
+ DKL_clktop2_coreclkctl1 :=
+ DKL_CLKTOP2_CORECLKCTL1_A_DIVRATIO (A_DivRatio);
+ DKL_CLKTOP2_hsclkctl :=
+ DKL_CLKTOP2_HSCLKCTL_TLINEDRV_CLKSEL (TLineDrv) or
+ DKL_CLKTOP2_HSCLKCTL_CORE_INPUTSEL (Inputsel) or
+ Hsdiv or
+ DKL_CLKTOP2_HSCLKCTL_DSDIV_RATIO (Word32 (Div2));
+ Success := True;
+ end if;
+ exit when Success;
+ end;
+ end loop;
+ exit when Success;
+ end loop;
+ end Calc_Dividers;
+
+ procedure On
+ (PLL : in DKL_DPLLs;
+ Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ is
+ DKL_Refclkin_Ctl : Word32;
+ DKL_Clktop2_Coreclkctl1 : Word32;
+ DKL_Clktop2_HSClkCtl : Word32;
+ DCO_Khz : DCO_Range_KHz;
+ Clock : Frequency_Type;
+ begin
+ if Port_Cfg.Display = HDMI then
+ Clock := Port_Cfg.Mode.Dotclock;
+ else
+ Clock := Frequency_Type (DP_Symbol_Rate (Port_Cfg.DP.Bandwidth));
+ end if;
+
+ Calc_Dividers
+ (Clock => Clock,
+ Display => Port_Cfg.Display,
+ DKL_Refclkin_Ctl => DKL_Refclkin_Ctl,
+ DKL_Clktop2_Coreclkctl1 => DKL_Clktop2_Coreclkctl1,
+ DKL_Clktop2_HSClkCtl => DKL_Clktop2_HSClkCtl,
+ DCO_Khz => DCO_Khz,
+ Success => Success);
+
+ if not Success then
+ Debug.Put_Line ("Could not find dividers for port!");
+ return;
+ end if;
+
+ Registers.Set_Mask
+ (Register => DKL_PLL_ENABLE (PLL),
+ Mask => DPLL_ENABLE_POWER_ENABLE);
+
+ Registers.Wait_Set_Mask
+ (Register => DKL_PLL_ENABLE (PLL),
+ Mask => DPLL_ENABLE_POWER_STATE);
+
+ declare
+ Tmp : Int64;
+ Refclk : Power_And_Clocks.Refclk_Range;
+ Refclk_Khz : Power_And_Clocks.Refclk_Range_KHz;
+ FeedFwGain : Word32;
+ M1Div : constant := 2;
+ M2Div_Int, M2Div_Rem: Int64;
+ M2Div_Frac : Word32;
+ TDC_Target : Word32;
+ Prop_Coeff, Int_Coeff : Word32;
+ IRef_Ndiv, Iref_Itrim : Word32;
+ begin
+ Power_And_Clocks.Get_Refclk (Refclk);
+ Refclk_Khz := Power_And_Clocks.Refclk_Range_KHz (Refclk / 1_000);
+ M2Div_Int := Int64(DCO_Khz / (Refclk_Khz * M1Div));
+ M2Div_Rem := Int64(DCO_Khz rem (Refclk_Khz * M1Div));
+ Tmp := M2Div_Rem * 2 ** 22;
+ M2Div_Frac := Word32 (Tmp / Int64(Refclk_Khz * M1Div));
+
+ case Refclk is
+ when 24_000_000 =>
+ Iref_Ndiv := 1;
+ Iref_Itrim := 25;
+ when 38_400_000 =>
+ Iref_Ndiv := 2;
+ Iref_Itrim := 28;
+ when others =>
+ Iref_Ndiv := 1;
+ Iref_Itrim := 28;
+ end case;
+
+ -- Real number math converted to fixed point
+ -- see note in i915 about these calculations
+ TDC_Target := Word32 (2 * 1_000 * 100_000 * 10 / (132 * Refclk_Khz) + 5) / 10;
+
+ if M2Div_Rem > 0 then
+ FeedFwGain := (M1Div * 1_000_000 * 100) / (Word32 (DCO_Khz) * 3 / 10);
+ else
+ FeedFwGain := 0;
+ end if;
+
+ if DCO_Khz >= 9_000_000 then
+ Prop_Coeff := 5;
+ Int_Coeff := 10;
+ else
+ Prop_Coeff := 4;
+ Int_Coeff := 8;
+ end if;
+
+ -- The following DKL registers are at MMIO index 2
+ Registers.Write (HIP_INDEX_REG (PLL), HIP_INDEX_VAL (PLL, 2));
+
+ Registers.Unset_And_Set_Mask
+ (Register => PLL_Regs (PLL).DKL_REFCLKIN_CTL,
+ Mask_Unset => DKL_REFCLKIN_CTL_OD_2_MUX_MASK,
+ Mask_Set => DKL_Refclkin_Ctl);
+
+ Registers.Unset_And_Set_Mask
+ (Register => PLL_Regs (PLL).DKL_CLKTOP2_CORECLKCTL1,
+ Mask_Unset => DKL_CLKTOP2_CORECLKCTL1_A_DIVRATIO_MASK,
+ Mask_Set => DKL_Clktop2_Coreclkctl1);
+
+ Registers.Unset_And_Set_Mask
+ (Register => PLL_Regs (PLL).DKL_CLKTOP2_HSCLKCTL,
+ Mask_Unset => DKL_CLKTOP2_HSCLKCTL_MASK,
+ Mask_Set => DKL_CLKTOP2_hsclkctl);
+
+ Registers.Unset_And_Set_mask
+ (Register => PLL_Regs (PLL).DKL_PLL_DIV0,
+ Mask_Unset => DKL_PLL_DIV0_MASK,
+ Mask_Set => Shift_Left (Int_Coeff, 16) or
+ Shift_Left (Prop_Coeff, 12) or
+ Shift_Left (M1Div, 8) or
+ Word32(M2Div_Int));
+
+ Registers.Unset_And_Set_Mask
+ (Register => PLL_Regs (PLL).DKL_PLL_DIV1,
+ Mask_Unset => DKL_PLL_DIV1_MASK,
+ Mask_Set => Shift_Left (Iref_Itrim, 16) or
+ TDC_Target);
+
+ Registers.Unset_And_Set_Mask
+ (Register => PLL_Regs (PLL).DKL_PLL_SSC,
+ Mask_Unset => DKL_PLL_SSC_MASK,
+ Mask_Set => Shift_Left (Iref_Ndiv, 29) or
+ Shift_Left (4, 11)); -- SSC_STEP_NUM (always 4)
+
+ Registers.Unset_And_Set_Mask
+ (Register => PLL_Regs (PLL).DKL_PLL_BIAS,
+ Mask_Unset => DKL_PLL_BIAS_MASK,
+ Mask_Set => Shift_Left (M2Div_Frac, 8) or
+ (if M2Div_Frac > 0 then DKL_PLL_BIAS_FRAC_EN_H else 0));
+
+ Registers.Unset_And_Set_Mask
+ (Register => PLL_Regs (PLL).DKL_PLL_COLDST_BIAS,
+ Mask_Unset => DKL_PLL_COLD_BIAS_MASK,
+ Mask_Set => FeedFwGain);
+
+ -- Read back the last programmed PHY PLL register
+ Registers.Posting_Read (PLL_Regs (PLL).DKL_PLL_COLDST_BIAS);
+
+ Registers.Set_Mask
+ (Register => DKL_PLL_ENABLE (PLL),
+ Mask => DPLL_ENABLE_PLL_ENABLE);
+
+ Registers.Wait_Set_Mask
+ (Register => DKL_PLL_ENABLE (PLL),
+ Mask => DPLL_ENABLE_PLL_LOCK);
+ end;
+ end On;
+
+ procedure Free (PLL : DKL_DPLLs)
+ is
+ begin
+ Registers.Unset_Mask
+ (Register => DKL_PLL_ENABLE (PLL),
+ Mask => DPLL_ENABLE_PLL_ENABLE);
+ Registers.Wait_Unset_Mask
+ (Register => DKL_PLL_ENABLE (PLL),
+ Mask => DPLL_ENABLE_PLL_LOCK);
+
+ Registers.Unset_Mask
+ (Register => DKL_PLL_ENABLE (PLL),
+ Mask => DPLL_ENABLE_POWER_ENABLE);
+ Registers.Wait_Unset_Mask
+ (Register => DKL_PLL_ENABLE (PLL),
+ Mask => DPLL_ENABLE_POWER_STATE);
+ end Free;
+
+ procedure All_Off is
+ begin
+ for PLL in DKL_DPLLs loop
+ Free (PLL);
+ end loop;
+ end All_Off;
+
+end HW.GFX.GMA.PLLs.Dekel_Phy;