gma g45: Add support for Intel GM965 (Crestline)
Add GM965 as a new CPU type under the G45 generation. GM965 shares
the GMCH display architecture with G45/GM45 (no PCH, same connector
registers) but differs in PLL limits, VCO/CDClk tables, and register
field encoding.
Key differences from G45/GM45:
- PLL limits: Uses i9xx limits (VCO 1.4-2.8 GHz) instead of g4x
limits. Add I9XX_LVDS_Limits; refactor Calculate_Clock_Parameters
to take Limits as a parameter, with a new Select_Limits function.
- VCO: Uses Crestline (CL) VCO tables with different frequencies
and divisors than GM45's Cantiga (CTG) tables.
- CDClk: GCFGC register decoding uses bits 12:8 minus 1 (3 possible
divisor selections), unlike GM45's single bit 12.
- No native DisplayPort (SDVO B/C only).
- No HD Audio (G4X_AUD_VID_DID reads as 0).
- Has integrated LVDS transmitter (mobile platform).
- PCI IDs: 0x2a02 (I965_GM), 0x2a12 (I965_GME).
All implementation details cross-referenced against the Linux kernel
i915 driver (intel_dpll.c, intel_cdclk.c, intel_display_device.c).
Change-Id: I0d5d698cc1c2aa84778f0fc6c2752cb5ce4f1cb2
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-on: https://review.sourcearcade.org/c/libgfxinit/+/499
Reviewed-by: Nico Huber <nico.h@gmx.de>
Tested-by: Nico Huber <nico.h@gmx.de>
diff --git a/common/g45/hw-gfx-gma-plls.adb b/common/g45/hw-gfx-gma-plls.adb
index 5db76d6..fcc08ad 100644
--- a/common/g45/hw-gfx-gma-plls.adb
+++ b/common/g45/hw-gfx-gma-plls.adb
@@ -140,6 +140,18 @@
P2_Fast => 5, P2_Slow => 10,
P2_Threshold => 165_000_000,
VCO_Lower => 1_750_000_000, VCO_Upper => 3_500_000_000);
+ -- I9XX limits are used by GM965 (Crestline) and older non-G4x chips.
+ I9XX_LVDS_Limits : constant Limits_Type := Limits_Type'
+ (N_Lower => 3, N_Upper => 8,
+ M_Lower => 70, M_Upper => 120,
+ M1_Lower => 10, M1_Upper => 20,
+ M2_Lower => 5, M2_Upper => 9,
+ P_Lower => 7, P_Upper => 98,
+ P1_Lower => 1, P1_Upper => 8,
+ P2_Fast => 7, P2_Slow => 14,
+ P2_Threshold => 112_000_000,
+ VCO_Lower => 1_400_000_000, VCO_Upper => 2_800_000_000);
+ -- The All_Other/I9XX_SDVO limits match Linux' intel_limits_i9xx_sdvo.
All_Other_Limits : constant Limits_Type := Limits_Type'
(N_Lower => 3, N_Upper => 8,
M_Lower => 70, M_Upper => 120,
@@ -279,9 +291,9 @@
end Verify_Parameters;
procedure Calculate_Clock_Parameters
- (Display : in Display_Type;
- Target_Dotclock : in Clock_Range;
+ (Target_Dotclock : in Clock_Range;
Reference_Clock : in Clock_Range;
+ Limits : in Limits_Type;
Best_Clock : out Clock_Type;
Valid : out Boolean)
with
@@ -289,17 +301,6 @@
Pre => True,
Post => True
is
- Limits : constant Limits_Type :=
- (case Display is
- when LVDS =>
- (if Target_Dotclock >= Config.LVDS_Dual_Threshold then
- LVDS_Dual_Limits
- else
- LVDS_Single_Limits),
- when HDMI => HDMI_Analog_Limits,
- when VGA => HDMI_Analog_Limits,
- when others => All_Other_Limits); --TODO add SDVO type, needs other limits
-
P2 : P2_Range;
Best_Delta : Int64 := Int64'Last;
Current_Delta : Int64;
@@ -420,6 +421,34 @@
Shift_Left (Encoded_P1, DPLL_P1_DIVIDER_SHIFT));
end Program_DPLL;
+ function Select_Limits
+ (Display : Display_Type;
+ Target_Dotclock : Clock_Range)
+ return Limits_Type
+ is
+ begin
+ if Config.GMCH_GM965 then
+ -- i9xx PLL limits for GM965 (Crestline)
+ case Display is
+ when LVDS => return I9XX_LVDS_Limits;
+ when others => return All_Other_Limits;
+ end case;
+ else
+ -- G4x PLL limits for G45/GM45
+ case Display is
+ when LVDS =>
+ if Target_Dotclock >= Config.LVDS_Dual_Threshold then
+ return LVDS_Dual_Limits;
+ else
+ return LVDS_Single_Limits;
+ end if;
+ when HDMI => return HDMI_Analog_Limits;
+ when VGA => return HDMI_Analog_Limits;
+ when others => return All_Other_Limits;
+ end case;
+ end if;
+ end Select_Limits;
+
procedure On
(PLL : in T;
Port_Cfg : in Port_Config;
@@ -459,10 +488,10 @@
end case;
elsif Target_Clock <= 340_000_000 then
Calculate_Clock_Parameters
- (Display => Port_Cfg.Display,
- Target_Dotclock => Target_Clock,
+ (Target_Dotclock => Target_Clock,
-- should be, but doesn't has to be always the same:
Reference_Clock => 96_000_000,
+ Limits => Select_Limits (Port_Cfg.Display, Target_Clock),
Best_Clock => Clk,
Valid => Success);
else
diff --git a/common/g45/hw-gfx-gma-port_detect.adb b/common/g45/hw-gfx-gma-port_detect.adb
index 45ed2dd..6fdb527 100644
--- a/common/g45/hw-gfx-gma-port_detect.adb
+++ b/common/g45/hw-gfx-gma-port_detect.adb
@@ -74,25 +74,27 @@
begin
Config.Valid_Port (Analog) := True;
- Config.Valid_Port (LVDS) := Config.GMCH_GM45;
- for HDMI_Port in GMCH_HDMI_Port loop
- Registers.Is_Set_Mask
- (Register => GMCH_HDMI (HDMI_Port),
- Mask => PORT_DETECTED,
- Result => Detected);
- Config.Valid_Port (To_HDMI_Port (HDMI_Port)) := Detected;
- hotplug_mask_set := hotplug_mask_set or
- (if Detected then HDMI_PORT_HOTPLUG_EN (HDMI_Port) else 0);
- end loop;
- for DP_Port in GMCH_DP_Port loop
- Registers.Is_Set_Mask
- (Register => GMCH_DP (DP_Port),
- Mask => PORT_DETECTED,
- Result => Detected);
- Config.Valid_Port (To_DP_Port (DP_Port)) := Detected;
- hotplug_mask_set := hotplug_mask_set or
- (if Detected then DP_PORT_HOTPLUG_EN (DP_Port) else 0);
- end loop;
+ Config.Valid_Port (LVDS) := Config.GMCH_GM45 or Config.GMCH_GM965;
+ if not Config.GMCH_GM965 then
+ for HDMI_Port in GMCH_HDMI_Port loop
+ Registers.Is_Set_Mask
+ (Register => GMCH_HDMI (HDMI_Port),
+ Mask => PORT_DETECTED,
+ Result => Detected);
+ Config.Valid_Port (To_HDMI_Port (HDMI_Port)) := Detected;
+ hotplug_mask_set := hotplug_mask_set or
+ (if Detected then HDMI_PORT_HOTPLUG_EN (HDMI_Port) else 0);
+ end loop;
+ for DP_Port in GMCH_DP_Port loop
+ Registers.Is_Set_Mask
+ (Register => GMCH_DP (DP_Port),
+ Mask => PORT_DETECTED,
+ Result => Detected);
+ Config.Valid_Port (To_DP_Port (DP_Port)) := Detected;
+ hotplug_mask_set := hotplug_mask_set or
+ (if Detected then DP_PORT_HOTPLUG_EN (DP_Port) else 0);
+ end loop;
+ end if;
Registers.Write
(Register => Registers.PORT_HOTPLUG_EN,
Value => hotplug_mask_set);
diff --git a/common/g45/hw-gfx-gma-power_and_clocks.adb b/common/g45/hw-gfx-gma-power_and_clocks.adb
index c0bb683..e082a6d 100644
--- a/common/g45/hw-gfx-gma-power_and_clocks.adb
+++ b/common/g45/hw-gfx-gma-power_and_clocks.adb
@@ -46,10 +46,32 @@
(0 => GM45_3200, 1 => GM45_4000, 2 => GM45_5333, 4 => GM45_2667,
others => (others => 1));
+ -- Crestline (GM965) VCO divisor tables from Linux' i965gm_get_cdclk
+ CL_3200 : constant Div_Array := (16, 10, 8, others => 1);
+ CL_4000 : constant Div_Array := (20, 12, 10, others => 1);
+ CL_5333 : constant Div_Array := (24, 16, 14, others => 1);
+ CL_Divs : constant array (Natural range 0 .. 7) of Div_Array :=
+ (0 => CL_3200, 1 => CL_4000, 2 => CL_5333,
+ others => (others => 1));
+
HPLLVCO : Word32;
VCO_Sel : Natural range 0 .. 7;
begin
- if Config.Has_GMCH_Mobile_VCO then
+ if Config.GMCH_GM965 then
+ Registers.Read (Registers.GMCH_HPLLVCO_MOBILE, HPLLVCO);
+ VCO_Sel := Natural (HPLLVCO and 7);
+ VCO :=
+ (case VCO_Sel is
+ when 0 => 3_200_000_000,
+ when 1 => 4_000_000_000,
+ when 2 => 5_333_333_333,
+ when 3 => 6_400_000_000,
+ when 4 => 3_333_333_333,
+ when 5 => 3_566_666_667,
+ when 6 => 4_266_666_667,
+ when others => 0);
+ Divisors := CL_Divs (VCO_Sel);
+ elsif Config.Has_GMCH_Mobile_VCO then
Registers.Read (Registers.GMCH_HPLLVCO_MOBILE, HPLLVCO);
VCO_Sel := Natural (HPLLVCO and 7);
VCO :=
@@ -91,7 +113,14 @@
if PCI_Usable then
Get_VCO (VCO, Divisors);
PCI_Read16 (GCFGC, 16#f0#);
- if Config.Has_GMCH_Mobile_VCO then
+ if Config.GMCH_GM965 then
+ -- Linux i965gm_get_cdclk: cdclk_sel = ((tmp >> 8) & 0x1f) - 1
+ if (Shift_Right (GCFGC, 8) and 16#1f#) in 1 .. 3 then
+ CDClk_Sel := Natural (Shift_Right (GCFGC, 8) and 16#1f#) - 1;
+ else
+ CDClk_Sel := Div_Array'Last;
+ end if;
+ elsif Config.Has_GMCH_Mobile_VCO then
CDClk_Sel := Natural (Shift_Right (GCFGC, 12) and 1);
else
CDClk_Sel := Natural (Shift_Right (GCFGC, 4) and 7);
@@ -102,7 +131,9 @@
if Tmp_Clk in Config.CDClk_Range then
CDClk := Tmp_Clk;
else
- if Config.Has_GMCH_Mobile_VCO then
+ if Config.GMCH_GM965 then
+ CDClk := 4_000_000_000 / 20; -- 200 MHz
+ elsif Config.Has_GMCH_Mobile_VCO then
CDClk := 5_333_333_333 / 24;
else
CDClk := 5_333_333_333 / 28;