487 lines
18 KiB
Ada
487 lines
18 KiB
Ada
|
--
|
||
|
-- Copyright (C) 2017 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.Debug;
|
||
|
with GNAT.Source_Info;
|
||
|
|
||
|
with HW.GFX.GMA.Config;
|
||
|
with HW.GFX.GMA.Registers;
|
||
|
with HW.GFX.GMA.DDI_Phy;
|
||
|
|
||
|
use HW.GFX.GMA.Registers;
|
||
|
|
||
|
package body HW.GFX.GMA.PLLs
|
||
|
with
|
||
|
Refined_State => (State => null)
|
||
|
is
|
||
|
|
||
|
-- DPLL clock equation:
|
||
|
-- 5 * Target_Dotclock = Ref_Clk * M1 * (M2 / 2^22) / N / (P1 * P2)
|
||
|
--
|
||
|
-- Where
|
||
|
-- M1 = 2,
|
||
|
-- Ref_Clk = 100MHz,
|
||
|
-- VCO = Ref_Clk * 2 * (M2 / 2^22),
|
||
|
-- N = 1 and
|
||
|
-- P = P1 * P2.
|
||
|
|
||
|
Ref_Clk : constant := 100_000_000;
|
||
|
M1 : constant := 2;
|
||
|
N : constant := 1;
|
||
|
|
||
|
-- i915 has a fixme for the M2 range. But the VCO range is very
|
||
|
-- limited, giving us a narrow range for M2: 24 .. 33.5
|
||
|
subtype VCO_Range is Pos64 range 4_800_000_000 .. 6_700_000_000;
|
||
|
subtype M2_Range is Pos64 range
|
||
|
N * VCO_Range'First * 2 ** 22 / Ref_Clk / M1 ..
|
||
|
N * VCO_Range'Last * 2 ** 22 / Ref_Clk / M1;
|
||
|
|
||
|
subtype N_Range is Pos64 range 1 .. 1;
|
||
|
|
||
|
subtype P1_Range is Pos64 range 2 .. 4;
|
||
|
subtype P2_Range is Pos64 range 1 .. 20;
|
||
|
|
||
|
subtype Clock_Range is Frequency_Type range
|
||
|
Frequency_Type'First .. 540_000_000;
|
||
|
subtype HDMI_Clock_Range is Clock_Range range
|
||
|
25_000_000 .. Config.HDMI_Max_Clock_24bpp;
|
||
|
subtype Clock_Gap is Clock_Range range 223_333_333 + 1 .. 240_000_000 - 1;
|
||
|
|
||
|
type Clock_Type is record
|
||
|
M2 : M2_Range;
|
||
|
P1 : P1_Range;
|
||
|
P2 : P2_Range;
|
||
|
VCO : VCO_Range;
|
||
|
Dotclock : Clock_Range;
|
||
|
end record;
|
||
|
|
||
|
Invalid_Clock : constant Clock_Type :=
|
||
|
(M2 => M2_Range'Last,
|
||
|
P1 => P1_Range'Last,
|
||
|
P2 => P2_Range'Last,
|
||
|
VCO => VCO_Range'Last,
|
||
|
Dotclock => Clock_Range'Last);
|
||
|
|
||
|
procedure Calculate_Clock_Parameters
|
||
|
(Target_Dotclock : in HDMI_Clock_Range;
|
||
|
Best_Clock : out Clock_Type;
|
||
|
Valid : out Boolean)
|
||
|
with
|
||
|
Pre => True
|
||
|
is
|
||
|
Target_Clock : constant Pos64 := 5 * Target_Dotclock;
|
||
|
|
||
|
M2, VCO, Current_Clock : Pos64;
|
||
|
P2 : P2_Range;
|
||
|
|
||
|
Valid_Clk : Boolean;
|
||
|
begin
|
||
|
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
|
||
|
|
||
|
Valid := False;
|
||
|
Best_Clock := Invalid_Clock;
|
||
|
|
||
|
-- reverse loops as hardware prefers higher values
|
||
|
for P1 in reverse P1_Range loop
|
||
|
-- find the highest P2 that results in valid clock
|
||
|
P2 := P2_Range'Last;
|
||
|
loop
|
||
|
M2 := Div_Round_Closest
|
||
|
(Target_Clock * P2 * P1 * N * 2 ** 22, Ref_Clk * M1);
|
||
|
VCO := Div_Round_Closest (Ref_Clk * M1 * M2, 2 ** 22 * N);
|
||
|
Current_Clock := Div_Round_Closest (VCO, P1 * P2);
|
||
|
|
||
|
Valid_Clk := M2 in M2_Range and then
|
||
|
Div_Round_Closest (Current_Clock, 5) in Clock_Range;
|
||
|
if Valid_Clk then
|
||
|
-- the error is always below 2^-22, higher P takes precedence
|
||
|
if not Valid or P1 * P2 > Best_Clock.P1 * Best_Clock.P2 then
|
||
|
Best_Clock := Clock_Type'
|
||
|
(M2 => M2,
|
||
|
P1 => P1,
|
||
|
P2 => P2,
|
||
|
VCO => VCO,
|
||
|
Dotclock => Div_Round_Closest (Current_Clock, 5));
|
||
|
Valid := True;
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
-- Prefer higher P2 over marginal lower error. This is
|
||
|
-- just an optimization, since lower P2 values would get
|
||
|
-- filtered above anyway.
|
||
|
exit when Valid_Clk;
|
||
|
|
||
|
-- If M2 got too low, it won't get any better. Another
|
||
|
-- optimization.
|
||
|
exit when M2 < M2_Range'First;
|
||
|
|
||
|
exit when P2 = P2_Range'First;
|
||
|
|
||
|
if P2 > 10 then
|
||
|
P2 := P2 - 2;
|
||
|
else
|
||
|
P2 := P2 - 1;
|
||
|
end if;
|
||
|
end loop;
|
||
|
end loop;
|
||
|
|
||
|
pragma Debug (Valid, Debug.Put_Line ("Valid clock found."));
|
||
|
pragma Debug (Valid, Debug.Put ("M2 / P1 / P2: "));
|
||
|
pragma Debug (Valid, Debug.Put_Word32 (Word32 (Best_Clock.M2)));
|
||
|
pragma Debug (Valid, Debug.Put (" / "));
|
||
|
pragma Debug (Valid, Debug.Put_Int8 (Pos8 (Best_Clock.P1)));
|
||
|
pragma Debug (Valid, Debug.Put (" / "));
|
||
|
pragma Debug (Valid, Debug.Put_Int8 (Pos8 (Best_Clock.P2)));
|
||
|
pragma Debug (Valid, Debug.New_Line);
|
||
|
pragma Debug (Valid, Debug.Put ("Best / Target: "));
|
||
|
pragma Debug (Valid, Debug.Put_Int64 (Best_Clock.Dotclock));
|
||
|
pragma Debug (Valid, Debug.Put (" / "));
|
||
|
pragma Debug (Valid, Debug.Put_Int64 (Target_Dotclock));
|
||
|
pragma Debug (Valid, Debug.New_Line);
|
||
|
pragma Debug (not Valid, Debug.Put_Line ("No valid clock found."));
|
||
|
end Calculate_Clock_Parameters;
|
||
|
|
||
|
----------------------------------------------------------------------------
|
||
|
|
||
|
subtype Valid_PLLs is T range DPLL_A .. DPLL_C;
|
||
|
|
||
|
type Port_PLL_Regs is record
|
||
|
PLL_ENABLE : Registers_Index;
|
||
|
PLL_EBB_0 : Registers_Index;
|
||
|
PLL_EBB_4 : Registers_Index;
|
||
|
PLL_0 : Registers_Index;
|
||
|
PLL_1 : Registers_Index;
|
||
|
PLL_2 : Registers_Index;
|
||
|
PLL_3 : Registers_Index;
|
||
|
PLL_6 : Registers_Index;
|
||
|
PLL_8 : Registers_Index;
|
||
|
PLL_9 : Registers_Index;
|
||
|
PLL_10 : Registers_Index;
|
||
|
PCS_DW12_LN01 : Registers_Index;
|
||
|
PCS_DW12_GRP : Registers_Index;
|
||
|
end record;
|
||
|
type Port_PLL_Array is array (Valid_PLLs) of Port_PLL_Regs;
|
||
|
|
||
|
PORT : constant Port_PLL_Array :=
|
||
|
(DPLL_A =>
|
||
|
(PLL_ENABLE => BXT_PORT_PLL_ENABLE_A,
|
||
|
PLL_EBB_0 => BXT_PORT_PLL_EBB_0_A,
|
||
|
PLL_EBB_4 => BXT_PORT_PLL_EBB_4_A,
|
||
|
PLL_0 => BXT_PORT_PLL_0_A,
|
||
|
PLL_1 => BXT_PORT_PLL_1_A,
|
||
|
PLL_2 => BXT_PORT_PLL_2_A,
|
||
|
PLL_3 => BXT_PORT_PLL_3_A,
|
||
|
PLL_6 => BXT_PORT_PLL_6_A,
|
||
|
PLL_8 => BXT_PORT_PLL_8_A,
|
||
|
PLL_9 => BXT_PORT_PLL_9_A,
|
||
|
PLL_10 => BXT_PORT_PLL_10_A,
|
||
|
PCS_DW12_LN01 => BXT_PORT_PCS_DW12_01_A,
|
||
|
PCS_DW12_GRP => BXT_PORT_PCS_DW12_GRP_A),
|
||
|
DPLL_B =>
|
||
|
(PLL_ENABLE => BXT_PORT_PLL_ENABLE_B,
|
||
|
PLL_EBB_0 => BXT_PORT_PLL_EBB_0_B,
|
||
|
PLL_EBB_4 => BXT_PORT_PLL_EBB_4_B,
|
||
|
PLL_0 => BXT_PORT_PLL_0_B,
|
||
|
PLL_1 => BXT_PORT_PLL_1_B,
|
||
|
PLL_2 => BXT_PORT_PLL_2_B,
|
||
|
PLL_3 => BXT_PORT_PLL_3_B,
|
||
|
PLL_6 => BXT_PORT_PLL_6_B,
|
||
|
PLL_8 => BXT_PORT_PLL_8_B,
|
||
|
PLL_9 => BXT_PORT_PLL_9_B,
|
||
|
PLL_10 => BXT_PORT_PLL_10_B,
|
||
|
PCS_DW12_LN01 => BXT_PORT_PCS_DW12_01_B,
|
||
|
PCS_DW12_GRP => BXT_PORT_PCS_DW12_GRP_B),
|
||
|
DPLL_C =>
|
||
|
(PLL_ENABLE => BXT_PORT_PLL_ENABLE_C,
|
||
|
PLL_EBB_0 => BXT_PORT_PLL_EBB_0_C,
|
||
|
PLL_EBB_4 => BXT_PORT_PLL_EBB_4_C,
|
||
|
PLL_0 => BXT_PORT_PLL_0_C,
|
||
|
PLL_1 => BXT_PORT_PLL_1_C,
|
||
|
PLL_2 => BXT_PORT_PLL_2_C,
|
||
|
PLL_3 => BXT_PORT_PLL_3_C,
|
||
|
PLL_6 => BXT_PORT_PLL_6_C,
|
||
|
PLL_8 => BXT_PORT_PLL_8_C,
|
||
|
PLL_9 => BXT_PORT_PLL_9_C,
|
||
|
PLL_10 => BXT_PORT_PLL_10_C,
|
||
|
PCS_DW12_LN01 => BXT_PORT_PCS_DW12_01_C,
|
||
|
PCS_DW12_GRP => BXT_PORT_PCS_DW12_GRP_C));
|
||
|
|
||
|
PORT_PLL_ENABLE : constant := 1 * 2 ** 31;
|
||
|
PORT_PLL_ENABLE_LOCK : constant := 1 * 2 ** 30;
|
||
|
PORT_PLL_ENABLE_REF_SEL : constant := 1 * 2 ** 27;
|
||
|
|
||
|
PORT_PLL_EBB0_P1_SHIFT : constant := 13;
|
||
|
PORT_PLL_EBB0_P1_MASK : constant := 16#07# * 2 ** 13;
|
||
|
PORT_PLL_EBB0_P2_SHIFT : constant := 8;
|
||
|
PORT_PLL_EBB0_P2_MASK : constant := 16#1f# * 2 ** 8;
|
||
|
function PORT_PLL_EBB0_P1 (P1 : P1_Range) return Word32 is
|
||
|
begin
|
||
|
return Shift_Left (Word32 (P1), PORT_PLL_EBB0_P1_SHIFT);
|
||
|
end PORT_PLL_EBB0_P1;
|
||
|
function PORT_PLL_EBB0_P2 (P2 : P2_Range) return Word32 is
|
||
|
begin
|
||
|
return Shift_Left (Word32 (P2), PORT_PLL_EBB0_P2_SHIFT);
|
||
|
end PORT_PLL_EBB0_P2;
|
||
|
|
||
|
PORT_PLL_EBB4_RECALIBRATE : constant := 1 * 2 ** 14;
|
||
|
PORT_PLL_EBB4_10BIT_CLK_ENABLE : constant := 1 * 2 ** 13;
|
||
|
|
||
|
PORT_PLL_0_M2_INT_MASK : constant := 16#ff# * 2 ** 0;
|
||
|
function PORT_PLL_0_M2_INT (M2 : M2_Range) return Word32 is
|
||
|
begin
|
||
|
return Shift_Right (Word32 (M2), 22);
|
||
|
end PORT_PLL_0_M2_INT;
|
||
|
|
||
|
PORT_PLL_1_N_SHIFT : constant := 8;
|
||
|
PORT_PLL_1_N_MASK : constant := 16#0f# * 2 ** 8;
|
||
|
function PORT_PLL_1_N (N : N_Range) return Word32 is
|
||
|
begin
|
||
|
return Shift_Left (Word32 (N), PORT_PLL_1_N_SHIFT);
|
||
|
end PORT_PLL_1_N;
|
||
|
|
||
|
PORT_PLL_2_M2_FRAC_MASK : constant := 16#3f_ffff#;
|
||
|
function PORT_PLL_2_M2_FRAC (M2 : M2_Range) return Word32 is
|
||
|
begin
|
||
|
return Word32 (M2) and PORT_PLL_2_M2_FRAC_MASK;
|
||
|
end PORT_PLL_2_M2_FRAC;
|
||
|
|
||
|
PORT_PLL_3_M2_FRAC_EN_MASK : constant := 1 * 2 ** 16;
|
||
|
function PORT_PLL_3_M2_FRAC_EN (M2 : M2_Range) return Word32 is
|
||
|
begin
|
||
|
return
|
||
|
(if (Word32 (M2) and PORT_PLL_2_M2_FRAC_MASK) /= 0 then
|
||
|
PORT_PLL_3_M2_FRAC_EN_MASK else 0);
|
||
|
end PORT_PLL_3_M2_FRAC_EN;
|
||
|
|
||
|
PORT_PLL_6_GAIN_CTL_SHIFT : constant := 16;
|
||
|
PORT_PLL_6_GAIN_CTL_MASK : constant := 16#07# * 2 ** 16;
|
||
|
PORT_PLL_6_INT_COEFF_SHIFT : constant := 8;
|
||
|
PORT_PLL_6_INT_COEFF_MASK : constant := 16#1f# * 2 ** 8;
|
||
|
PORT_PLL_6_PROP_COEFF_MASK : constant := 16#0f# * 2 ** 0;
|
||
|
function PORT_PLL_6_GAIN_COEFF (VCO : VCO_Range) return Word32 is
|
||
|
begin
|
||
|
return
|
||
|
(if VCO >= 6_200_000_000 then
|
||
|
Shift_Left (Word32'(3), PORT_PLL_6_GAIN_CTL_SHIFT) or
|
||
|
Shift_Left (Word32'(9), PORT_PLL_6_INT_COEFF_SHIFT) or
|
||
|
Word32'(4)
|
||
|
elsif VCO /= 5_400_000_000 then
|
||
|
Shift_Left (Word32'(3), PORT_PLL_6_GAIN_CTL_SHIFT) or
|
||
|
Shift_Left (Word32'(11), PORT_PLL_6_INT_COEFF_SHIFT) or
|
||
|
Word32'(5)
|
||
|
else
|
||
|
Shift_Left (Word32'(1), PORT_PLL_6_GAIN_CTL_SHIFT) or
|
||
|
Shift_Left (Word32'(8), PORT_PLL_6_INT_COEFF_SHIFT) or
|
||
|
Word32'(3));
|
||
|
end PORT_PLL_6_GAIN_COEFF;
|
||
|
|
||
|
PORT_PLL_8_TARGET_CNT_MASK : constant := 16#3ff#;
|
||
|
function PORT_PLL_8_TARGET_CNT (VCO : VCO_Range) return Word32 is
|
||
|
begin
|
||
|
return (if VCO >= 6_200_000_000 then 8 else 9);
|
||
|
end PORT_PLL_8_TARGET_CNT;
|
||
|
|
||
|
PORT_PLL_9_LOCK_THRESHOLD_SHIFT : constant := 1;
|
||
|
PORT_PLL_9_LOCK_THRESHOLD_MASK : constant := 16#07# * 2 ** 1;
|
||
|
function PORT_PLL_9_LOCK_THRESHOLD (Threshold : Natural) return Word32 is
|
||
|
begin
|
||
|
return
|
||
|
Shift_Left (Word32 (Threshold), PORT_PLL_9_LOCK_THRESHOLD_SHIFT) and
|
||
|
PORT_PLL_9_LOCK_THRESHOLD_MASK;
|
||
|
end PORT_PLL_9_LOCK_THRESHOLD;
|
||
|
|
||
|
PORT_PLL_10_DCO_AMP_OVR_EN_H : constant := 2 ** 27;
|
||
|
PORT_PLL_10_DCO_AMP_SHIFT : constant := 10;
|
||
|
PORT_PLL_10_DCO_AMP_MASK : constant := 16#0f# * 2 ** 10;
|
||
|
function PORT_PLL_10_DCO_AMP (Amp : Natural) return Word32 is
|
||
|
begin
|
||
|
return
|
||
|
Shift_Left (Word32 (Amp), PORT_PLL_10_DCO_AMP_SHIFT) and
|
||
|
PORT_PLL_10_DCO_AMP_MASK;
|
||
|
end PORT_PLL_10_DCO_AMP;
|
||
|
|
||
|
PORT_PCS_LANE_STAGGER_STRAP_OVRD : constant := 2 ** 6;
|
||
|
PORT_PCS_LANE_STAGGER_MASK : constant := 16#1f# * 2 ** 0;
|
||
|
function PORT_PCS_LANE_STAGGER (Dotclock : Clock_Range) return Word32 is
|
||
|
begin
|
||
|
return Word32'(PORT_PCS_LANE_STAGGER_STRAP_OVRD) or
|
||
|
(if Dotclock > 270_000_000 then
|
||
|
16#18#
|
||
|
elsif Dotclock > 135_000_000 then
|
||
|
16#0d#
|
||
|
elsif Dotclock > 67_000_000 then
|
||
|
16#07#
|
||
|
elsif Dotclock > 33_000_000 then
|
||
|
16#04#
|
||
|
else
|
||
|
16#02#);
|
||
|
end PORT_PCS_LANE_STAGGER;
|
||
|
|
||
|
----------------------------------------------------------------------------
|
||
|
|
||
|
procedure Program_DPLL (P : Valid_PLLs; Clock : Clock_Type)
|
||
|
is
|
||
|
PCS : Word32;
|
||
|
begin
|
||
|
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
|
||
|
|
||
|
Set_Mask (PORT (P).PLL_ENABLE, PORT_PLL_ENABLE_REF_SEL); -- non-SSC ref
|
||
|
Unset_Mask (PORT (P).PLL_EBB_4, PORT_PLL_EBB4_10BIT_CLK_ENABLE);
|
||
|
|
||
|
Unset_And_Set_Mask
|
||
|
(Register => PORT (P).PLL_EBB_0,
|
||
|
Mask_Unset => PORT_PLL_EBB0_P1_MASK or
|
||
|
PORT_PLL_EBB0_P2_MASK,
|
||
|
Mask_Set => PORT_PLL_EBB0_P1 (Clock.P1) or
|
||
|
PORT_PLL_EBB0_P2 (Clock.P2));
|
||
|
Unset_And_Set_Mask
|
||
|
(Register => PORT (P).PLL_0,
|
||
|
Mask_Unset => PORT_PLL_0_M2_INT_MASK,
|
||
|
Mask_Set => PORT_PLL_0_M2_INT (Clock.M2));
|
||
|
Unset_And_Set_Mask
|
||
|
(Register => PORT (P).PLL_1,
|
||
|
Mask_Unset => PORT_PLL_1_N_MASK,
|
||
|
Mask_Set => PORT_PLL_1_N (N));
|
||
|
Unset_And_Set_Mask
|
||
|
(Register => PORT (P).PLL_2,
|
||
|
Mask_Unset => PORT_PLL_2_M2_FRAC_MASK,
|
||
|
Mask_Set => PORT_PLL_2_M2_FRAC (Clock.M2));
|
||
|
Unset_And_Set_Mask
|
||
|
(Register => PORT (P).PLL_3,
|
||
|
Mask_Unset => PORT_PLL_3_M2_FRAC_EN_MASK,
|
||
|
Mask_Set => PORT_PLL_3_M2_FRAC_EN (Clock.M2));
|
||
|
Unset_And_Set_Mask
|
||
|
(Register => PORT (P).PLL_6,
|
||
|
Mask_Unset => PORT_PLL_6_GAIN_CTL_MASK or
|
||
|
PORT_PLL_6_INT_COEFF_MASK or
|
||
|
PORT_PLL_6_PROP_COEFF_MASK,
|
||
|
Mask_Set => PORT_PLL_6_GAIN_COEFF (Clock.VCO));
|
||
|
Unset_And_Set_Mask
|
||
|
(Register => PORT (P).PLL_8,
|
||
|
Mask_Unset => PORT_PLL_8_TARGET_CNT_MASK,
|
||
|
Mask_Set => PORT_PLL_8_TARGET_CNT (Clock.VCO));
|
||
|
Unset_And_Set_Mask
|
||
|
(Register => PORT (P).PLL_9,
|
||
|
Mask_Unset => PORT_PLL_9_LOCK_THRESHOLD_MASK,
|
||
|
Mask_Set => PORT_PLL_9_LOCK_THRESHOLD (5));
|
||
|
Unset_And_Set_Mask
|
||
|
(Register => PORT (P).PLL_10,
|
||
|
Mask_Unset => PORT_PLL_10_DCO_AMP_MASK,
|
||
|
Mask_Set => PORT_PLL_10_DCO_AMP_OVR_EN_H or
|
||
|
PORT_PLL_10_DCO_AMP (15));
|
||
|
|
||
|
Set_Mask (PORT (P).PLL_EBB_4, PORT_PLL_EBB4_RECALIBRATE);
|
||
|
Set_Mask (PORT (P).PLL_EBB_4, PORT_PLL_EBB4_10BIT_CLK_ENABLE);
|
||
|
|
||
|
Set_Mask (PORT (P).PLL_ENABLE, PORT_PLL_ENABLE);
|
||
|
Wait_Set_Mask
|
||
|
(Register => PORT (P).PLL_ENABLE,
|
||
|
Mask => PORT_PLL_ENABLE_LOCK,
|
||
|
TOut_MS => 1); -- 100us
|
||
|
|
||
|
Read (PORT (P).PCS_DW12_LN01, PCS);
|
||
|
PCS := PCS and not PORT_PCS_LANE_STAGGER_MASK;
|
||
|
PCS := PCS or PORT_PCS_LANE_STAGGER (Clock.Dotclock);
|
||
|
Write (PORT (P).PCS_DW12_GRP, PCS);
|
||
|
end Program_DPLL;
|
||
|
|
||
|
----------------------------------------------------------------------------
|
||
|
|
||
|
procedure Alloc
|
||
|
(Port_Cfg : in Port_Config;
|
||
|
PLL : out T;
|
||
|
Success : out Boolean)
|
||
|
is
|
||
|
Clock : Clock_Type := Invalid_Clock;
|
||
|
begin
|
||
|
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
|
||
|
|
||
|
case Port_Cfg.Port is
|
||
|
when DIGI_A => PLL := DPLL_A;
|
||
|
when DIGI_B => PLL := DPLL_B;
|
||
|
when DIGI_C => PLL := DPLL_C;
|
||
|
when others => PLL := Invalid_PLL;
|
||
|
end case;
|
||
|
|
||
|
Success := PLL /= Invalid_PLL;
|
||
|
|
||
|
if Success then
|
||
|
case Port_Cfg.Display is
|
||
|
when DP =>
|
||
|
Success := True;
|
||
|
-- we use static values for DP
|
||
|
case Port_Cfg.DP.Bandwidth is
|
||
|
when DP_Bandwidth_1_62 =>
|
||
|
Clock.M2 := 32 * 2 ** 22 + 1677722;
|
||
|
Clock.P1 := 4;
|
||
|
Clock.P2 := 2;
|
||
|
Clock.VCO := 6_480_000_019;
|
||
|
Clock.Dotclock := 162_000_000;
|
||
|
when DP_Bandwidth_2_7 =>
|
||
|
Clock.M2 := 27 * 2 ** 22;
|
||
|
Clock.P1 := 4;
|
||
|
Clock.P2 := 1;
|
||
|
Clock.VCO := 5_400_000_000;
|
||
|
Clock.Dotclock := 270_000_000;
|
||
|
when DP_Bandwidth_5_4 =>
|
||
|
Clock.M2 := 27 * 2 ** 22;
|
||
|
Clock.P1 := 2;
|
||
|
Clock.P2 := 1;
|
||
|
Clock.VCO := 5_400_000_000;
|
||
|
Clock.Dotclock := 540_000_000;
|
||
|
end case;
|
||
|
when HDMI =>
|
||
|
if Port_Cfg.Mode.Dotclock in HDMI_Clock_Range and
|
||
|
(Port_Cfg.Mode.Dotclock * 99 / 100 < Clock_Gap'First or
|
||
|
Port_Cfg.Mode.Dotclock * 101 / 100 > Clock_Gap'Last)
|
||
|
then
|
||
|
Calculate_Clock_Parameters
|
||
|
(Target_Dotclock => Port_Cfg.Mode.Dotclock,
|
||
|
Best_Clock => Clock,
|
||
|
Valid => Success);
|
||
|
else
|
||
|
Success := False;
|
||
|
pragma Debug (Debug.Put_Line
|
||
|
("Mode's dotclock is out of range."));
|
||
|
end if;
|
||
|
when others =>
|
||
|
Success := False;
|
||
|
pragma Debug (Debug.Put_Line ("Invalid display type!"));
|
||
|
end case;
|
||
|
end if;
|
||
|
|
||
|
if Success then
|
||
|
DDI_Phy.Pre_PLL (Port_Cfg);
|
||
|
Program_DPLL (PLL, Clock);
|
||
|
end if;
|
||
|
end Alloc;
|
||
|
|
||
|
procedure Free (PLL : T) is
|
||
|
begin
|
||
|
if PLL in Valid_PLLs then
|
||
|
Unset_Mask (PORT (PLL).PLL_ENABLE, PORT_PLL_ENABLE);
|
||
|
end if;
|
||
|
end Free;
|
||
|
|
||
|
procedure All_Off is
|
||
|
begin
|
||
|
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
|
||
|
|
||
|
for PLL in Valid_PLLs loop
|
||
|
Free (PLL);
|
||
|
end loop;
|
||
|
end All_Off;
|
||
|
|
||
|
end HW.GFX.GMA.PLLs;
|