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;
diff --git a/common/hw-gfx-gma-config.ads.template b/common/hw-gfx-gma-config.ads.template
index 93e7ca4..a041e2c 100644
--- a/common/hw-gfx-gma-config.ads.template
+++ b/common/hw-gfx-gma-config.ads.template
@@ -19,7 +19,7 @@
CPU_First : constant CPU_Type :=
(case Gen is
when I945 => I945G,
- when G45 => G45,
+ when G45 => GM965,
when Ironlake => Ironlake,
when Haswell => Haswell,
when Broxton => Broxton,
@@ -159,6 +159,7 @@
Tigerlake_On : <genbool> := Gen >= Tigerlake;
GMCH_I945GM : <i945bool> := Gen_I945 and then CPU = I945GM;
+ GMCH_GM965 : <g45bool> := Gen_G45 and then CPU = GM965;
GMCH_GM45 : <g45bool> := Gen_G45 and then CPU = GM45;
CPU_Ironlake : <ilkbool> := Gen_Ironlake and then CPU = Ironlake;
CPU_Sandybridge : <ilkbool> := Gen_Ironlake and then CPU = Sandybridge;
@@ -268,7 +269,7 @@
---------- Clocks: -----------
Has_GMCH_RawClk : <genbool> := Up_To_G45;
- Has_GMCH_Mobile_VCO : <g45bool> := GMCH_GM45;
+ Has_GMCH_Mobile_VCO : <g45bool> := GMCH_GM45 or GMCH_GM965;
---------- I945-specific: ----
Has_I945_GTT_BAR : <genbool> := Gen_I945;
@@ -454,6 +455,19 @@
GMA_Base_Is_64bit : constant Boolean := Config.Tigerlake_On;
+ AUD_VID_DID_Offset : <ilkhswvar> Natural :=
+ (if Gen_G45 then 16#06_2020#
+ elsif Gen_Ironlake then 16#0e_5020#
+ else 16#06_5020#);
+
+ -- Default 32-bit GTT offset for static MMIO instantiation.
+ Default_MMIO_GTT_32_Offset : constant Natural :=
+ (if Has_I945_GTT_BAR then 16#00_0000# else 16#20_0000#);
+ MMIO_GTT_32_Offset : <g45var> Natural :=
+ (if GMCH_GM965 then 16#0008_0000# else Default_MMIO_GTT_32_Offset);
+ MMIO_GTT_64_Offset : constant Natural := 16#80_0000#;
+ MMIO_GTT_Offset : <g45hswvar> Natural :=
+ (if Has_64bit_GTT then MMIO_GTT_64_Offset else MMIO_GTT_32_Offset);
GTT_PTE_Size : <hswvar> Natural := (if Has_64bit_GTT then 8 else 4);
Fence_Base : <i945ilkvar> Natural :=
@@ -607,6 +621,7 @@
return Boolean is
(case CPU is
when I945G => Device_Id = 16#2772#,
+ when GM965 => (Device_Id and 16#ffef#) = 16#2a02#,
when I945GM => Device_Id = 16#27a2# or Device_Id = 16#27ae#,
when G45 => (Device_Id and 16#ff02#) = 16#2e02#,
when GM45 => (Device_Id and 16#fffe#) = 16#2a42#,
diff --git a/common/hw-gfx-gma-pipe_setup.adb b/common/hw-gfx-gma-pipe_setup.adb
index cb5369e..8975778 100644
--- a/common/hw-gfx-gma-pipe_setup.adb
+++ b/common/hw-gfx-gma-pipe_setup.adb
@@ -310,7 +310,8 @@
end if;
if Config.Has_DSP_Linoff and then FB.Tiling = Linear then
- pragma Assert_And_Cut (True);
+ pragma Assert_And_Cut
+ (FB.Start_Y * FB.Stride + FB.Start_X in Pixel_Type);
declare
Linear_Offset : constant Pixel_Type :=
FB.Start_Y * FB.Stride + FB.Start_X;
diff --git a/common/hw-gfx-gma-registers.adb b/common/hw-gfx-gma-registers.adb
index eb12b0d..a59a4f7 100644
--- a/common/hw-gfx-gma-registers.adb
+++ b/common/hw-gfx-gma-registers.adb
@@ -45,13 +45,19 @@
----------------------------------------------------------------------------
+ MMIO_GTT_32_Size : constant := 16#20_0000#;
+ -- Limit Broadwell+ to 4MiB to have a stable
+ -- interface (i.e. same number of entries):
+ MMIO_GTT_64_Size : constant := 16#40_0000#;
+
type GTT_PTE_32 is mod 2 ** 32;
type GTT_Registers_32 is array (GTT_Range) of GTT_PTE_32
with
Volatile_Components,
Size => MMIO_GTT_32_Size * 8;
package GTT_32 is new MMIO_Range
- (Base_Addr => Config.Default_MMIO_Base + MMIO_GTT_32_Offset,
+ (Base_Addr =>
+ Config.Default_MMIO_Base + Word64 (Config.Default_MMIO_GTT_32_Offset),
Element_T => GTT_PTE_32,
Index_T => GTT_Range,
Array_T => GTT_Registers_32);
@@ -62,7 +68,7 @@
Volatile_Components,
Size => MMIO_GTT_64_Size * 8;
package GTT_64 is new MMIO_Range
- (Base_Addr => Config.Default_MMIO_Base + MMIO_GTT_64_Offset,
+ (Base_Addr => Config.Default_MMIO_Base + Word64 (Config.MMIO_GTT_64_Offset),
Element_T => GTT_PTE_64,
Index_T => GTT_Range,
Array_T => GTT_Registers_64);
@@ -347,6 +353,14 @@
pragma Debug (Verbose, Debug.Put_Line (Registers_Index'Image (Register)));
end Read;
+ procedure Read_AUD_VID_DID (Value : out Word32)
+ is
+ begin
+ Regs.Read
+ (Value,
+ Registers_Range (Config.AUD_VID_DID_Offset / Register_Width));
+ end Read_AUD_VID_DID;
+
----------------------------------------------------------------------------
-- Read a specific register to post a previous write
@@ -566,8 +580,9 @@
begin
Regs.Set_Base_Address (Base);
if GTT_Base = 0 then
- GTT_32.Set_Base_Address (Base + MMIO_GTT_32_Offset);
- GTT_64.Set_Base_Address (Base + MMIO_GTT_64_Offset);
+ GTT_32.Set_Base_Address
+ (Base + Word64 (Config.Default_MMIO_GTT_32_Offset));
+ GTT_64.Set_Base_Address (Base + Word64 (Config.MMIO_GTT_64_Offset));
else
GTT_32.Set_Base_Address (GTT_Base);
GTT_64.Set_Base_Address (GTT_Base);
diff --git a/common/hw-gfx-gma-registers.ads b/common/hw-gfx-gma-registers.ads
index 404dda3..c9454c9 100644
--- a/common/hw-gfx-gma-registers.ads
+++ b/common/hw-gfx-gma-registers.ads
@@ -24,14 +24,6 @@
Initializes => Address_State
is
- MMIO_GTT_32_Size : constant := 16#20_0000#;
- MMIO_GTT_32_Offset : constant := 16#20_0000#;
-
- -- Limit Broadwell+ to 4MiB to have a stable
- -- interface (i.e. same number of entries):
- MMIO_GTT_64_Size : constant := 16#40_0000#;
- MMIO_GTT_64_Offset : constant := 16#80_0000#;
-
type Registers_Invalid_Index is
(Invalid_Register, -- Allow a placeholder when access is not acceptable
@@ -2320,6 +2312,13 @@
Post => True;
pragma Warnings (GNATprove, On, "unused variable ""Verbose""");
+ procedure Read_AUD_VID_DID (Value : out Word32)
+ with
+ Global => (In_Out => Register_State),
+ Depends => ((Value, Register_State) => Register_State),
+ Pre => True,
+ Post => True;
+
procedure Write
(Register : Registers_Index;
Value : Word32)
diff --git a/common/hw-gfx-gma.adb b/common/hw-gfx-gma.adb
index d1fb7b1..7c336a2 100644
--- a/common/hw-gfx-gma.adb
+++ b/common/hw-gfx-gma.adb
@@ -506,35 +506,23 @@
is
use type HW.Word64;
- function MMIO_GTT_Offset return Natural is
- (if Config.Has_I945_GTT_BAR
- then 0 -- i945: GTT is on separate BAR3, not within BAR0
- elsif Config.Has_64bit_GTT
- then Registers.MMIO_GTT_64_Offset
- else Registers.MMIO_GTT_32_Offset);
PCI_MMIO_Base, PCI_GTT_Base : Word64;
+ function Default_GTT_Base return Word64 is
+ (Config.Default_MMIO_Base + Word64 (Config.MMIO_GTT_Offset));
+
Now : constant Time.T := Time.Now;
procedure Check_Platform (Success : out Boolean)
is
Audio_VID_DID : Word32;
begin
- if Config.Gen_I945 then
- -- i945 has no integrated audio DID to verify
+ if Config.Gen_I945 or Config.GMCH_GM965 then
+ -- i945 and GM965 have no integrated audio DID to verify.
Success := True;
return;
end if;
- case Config.Gen is
- when I945 =>
- Audio_VID_DID := 0; -- unreachable due to early return
- when G45 =>
- Registers.Read (Registers.G4X_AUD_VID_DID, Audio_VID_DID);
- when Ironlake =>
- Registers.Read (Registers.PCH_AUD_VID_DID, Audio_VID_DID);
- when Haswell .. Tigerlake =>
- Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
- end case;
+ Registers.Read_AUD_VID_DID (Audio_VID_DID);
Success :=
((Config.Gen_Broxton and Audio_VID_DID = 16#8086_280a#) or
(Config.CPU_Kabylake and Audio_VID_DID = 16#8086_280b#) or
@@ -580,7 +568,7 @@
Cursor => Default_Cursor,
Mode => HW.GFX.Invalid_Mode));
Config.Variable := Config.Initial_Settings;
- Registers.Set_Register_Base (Config.Default_MMIO_Base);
+ Registers.Set_Register_Base (Config.Default_MMIO_Base, Default_GTT_Base);
PLLs.Initialize;
Dev.Initialize (Success);
@@ -593,8 +581,10 @@
Dev.Map (PCI_MMIO_Base, PCI.Res0);
Dev.Map (PCI_GTT_Base, PCI.Res3);
else
- Dev.Map (PCI_MMIO_Base, PCI.Res0, Length => MMIO_GTT_Offset);
- Dev.Map (PCI_GTT_Base, PCI.Res0, Offset => MMIO_GTT_Offset);
+ Dev.Map
+ (PCI_MMIO_Base, PCI.Res0, Length => Config.MMIO_GTT_Offset);
+ Dev.Map
+ (PCI_GTT_Base, PCI.Res0, Offset => Config.MMIO_GTT_Offset);
end if;
if PCI_MMIO_Base /= 0 and PCI_GTT_Base /= 0 then
Registers.Set_Register_Base (PCI_MMIO_Base, PCI_GTT_Base);
@@ -869,6 +859,11 @@
-- from Gen4+. Match the Linux driver and use the BAR size.
Dev.Resource_Size (GTT_Size, PCI.Res3);
Stolen_Size := Stolen_Size_Gen4 (GGC);
+ elsif Config.GMCH_GM965 then
+ -- GM965 has no GGMS field in GGC. The GTT is a fixed 512 KiB
+ -- region in the upper half of the 1 MiB GTTMMADR BAR.
+ GTT_Size := 512 * 2 ** 10;
+ Stolen_Size := Stolen_Size_Gen4 (GGC);
elsif Config.Gen_G45 or Config.CPU_Ironlake then
GTT_Size := GTT_Size_Gen4 (GGC);
Stolen_Size := Stolen_Size_Gen4 (GGC);
@@ -901,6 +896,9 @@
if Config.Has_I945_GTT_BAR then
-- i945 GTT is on a separate BAR3; its size is the BAR size.
Dev.Resource_Size (GTT_Size, PCI.Res3);
+ elsif Config.GMCH_GM965 then
+ -- GM965 has no GGMS field in GGC; its GTT is fixed at 512 KiB.
+ GTT_Size := 512 * 2 ** 10;
else
-- Gen4+: GTT size is encoded in the GGC register.
declare
diff --git a/common/hw-gfx-gma.ads b/common/hw-gfx-gma.ads
index 3777b45..69cc41b 100644
--- a/common/hw-gfx-gma.ads
+++ b/common/hw-gfx-gma.ads
@@ -40,6 +40,7 @@
type CPU_Type is
(I945G,
I945GM,
+ GM965,
G45,
GM45,
Ironlake,