gma i945: Add Pineview support
Add Pineview desktop and mobile variants to the existing i945/Gen3
code path. Pineview is close to i945, but Linux' i915 treats a few
pieces differently and those differences need explicit handling:
- add Pineview/Pineview_M CPU types and PCI IDs 0xa001/0xa011
- detect Pineview-M as a mobile Gen3 platform so LVDS is exposed
- decode Pineview GCFGC display clock selections for 133/167/200/
267/333/444 MHz CDClk
- use the mobile/Pineview CLKCFG FSB encoding for HRAW clock
- keep the separate BAR3 Gen3 GTT layout, but use the i965/G33 style
PTE encoding for Pineview's 36-bit DMA addresses
- add a separate Pineview DPLL calculation/programming path instead of
widening the i945 PLL ranges; Pineview has a one-hot N encoding in
the frequency-parameter (FP) register, no M1 divider, M = M2 + 2,
and P1 at bit 15
The i945 PLL type ranges and calculation path remain unchanged.
Tested with:
make clean && make DEBUG=1 cnf=configs/ironlake
make clean && make DEBUG=1 cnf=configs/ironlake_edp
make clean && make DEBUG=1 cnf=configs/i945
Tested with coreboot on Foxconn D41S.
Change-Id: I8bda5d27e1c071bd59bd9a8751dc1ea31b25614f
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-on: https://review.sourcearcade.org/c/libgfxinit/+/515
Tested-by: Ada SPARK <gnatbot@sourcearcade.org>
Reviewed-by: Nico Huber <nico.h@gmx.de>
diff --git a/common/hw-gfx-gma-config.ads.template b/common/hw-gfx-gma-config.ads.template
index 0fd40c3..b784038 100644
--- a/common/hw-gfx-gma-config.ads.template
+++ b/common/hw-gfx-gma-config.ads.template
@@ -29,7 +29,7 @@
when Tigerlake => Tigerlake);
CPU_Last : constant CPU_Type :=
(case Gen is
- when I945 => I945GM,
+ when I945 => Pineview_M,
when G45 => GM45,
when Ironlake => Ivybridge,
when Haswell => Broadwell,
@@ -164,6 +164,10 @@
GMCH_I945GM : <i945bool> := Gen_I945 and then CPU = I945GM;
GMCH_GM965 : <g45bool> := Gen_G45 and then CPU = GM965;
+ CPU_Pineview : <i945bool> := Gen_I945 and then CPU = Pineview;
+ CPU_Pineview_M : <i945bool> := Gen_I945 and then CPU = Pineview_M;
+ CPU_Any_Pineview : <i945bool> := CPU_Pineview or CPU_Pineview_M;
+ GMCH_Gen3_Mobile : <i945bool> := GMCH_I945GM or CPU_Pineview_M;
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;
@@ -275,9 +279,9 @@
Has_GMCH_RawClk : <genbool> := Up_To_G45;
Has_GMCH_Mobile_VCO : <g45bool> := GMCH_GM45 or GMCH_GM965;
- ---------- I945-specific: ----
+ ---------- Gen3/i945-specific: ----
Has_I945_GTT_BAR : <genbool> := Gen_I945;
- Has_I945_Simple_GTT_PTE : <genbool> := Gen_I945;
+ Has_I945_Simple_GTT_PTE : <i945bool> := Gen_I945 and then not CPU_Any_Pineview;
Has_Gen3_Fences : <genbool> := Gen_I945;
-- Pre-i965: LVDS encoder can only source from Pipe B (Secondary)
LVDS_Needs_Pipe_B : <genbool> := Gen_I945;
@@ -628,6 +632,8 @@
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 Pineview => Device_Id = 16#a001#,
+ when Pineview_M => Device_Id = 16#a011#,
when G45 => (Device_Id and 16#ff02#) = 16#2e02#,
when GM45 => (Device_Id and 16#fffe#) = 16#2a42#,
when Ironlake => (Device_Id and 16#fff3#) = 16#0042#,
diff --git a/common/hw-gfx-gma.ads b/common/hw-gfx-gma.ads
index c33c513..c0c1687 100644
--- a/common/hw-gfx-gma.ads
+++ b/common/hw-gfx-gma.ads
@@ -41,6 +41,8 @@
(I945G,
I945GM,
GM965,
+ Pineview,
+ Pineview_M,
G45,
GM45,
Ironlake,
diff --git a/common/i945/hw-gfx-gma-plls.adb b/common/i945/hw-gfx-gma-plls.adb
index a5e272c..fde7f63 100644
--- a/common/i945/hw-gfx-gma-plls.adb
+++ b/common/i945/hw-gfx-gma-plls.adb
@@ -130,6 +130,75 @@
P2_Threshold => 200_000_000,
VCO_Lower => 1_400_000_000, VCO_Upper => 2_800_000_000);
+ -- Pineview has a different Gen3 DPLL encoding. Its N divider is encoded
+ -- as a one-hot value in the frequency-parameter (FP) register, M1 is
+ -- reserved as 0, M = M2 + 2, and P1 starts at bit 15.
+
+ subtype PNV_N_Range is Int64 range 3 .. 6;
+ subtype PNV_M_Range is Int64 range 2 .. 256;
+ subtype PNV_M2_Range is Int64 range 0 .. 254;
+ subtype PNV_P_Range is Int64 range 5 .. 112;
+ subtype PNV_P1_Range is Int64 range 1 .. 8;
+ subtype PNV_P2_Range is Int64 range 5 .. 14;
+ subtype PNV_VCO_Range is Int64 range 1700000000 .. 3500000000;
+
+ type PNV_Clock_Type is
+ record
+ N : PNV_N_Range;
+ M2 : PNV_M2_Range;
+ P1 : PNV_P1_Range;
+ P2 : PNV_P2_Range;
+ M : PNV_M_Range;
+ P : PNV_P_Range;
+ VCO : PNV_VCO_Range;
+ Reference_Clock : Clock_Range;
+ Dotclock : Clock_Range;
+ end record;
+
+ Invalid_PNV_Clock : constant PNV_Clock_Type := PNV_Clock_Type'
+ (N => PNV_N_Range'Last,
+ M2 => PNV_M2_Range'Last,
+ P1 => PNV_P1_Range'Last,
+ P2 => PNV_P2_Range'Last,
+ Reference_Clock => Clock_Range'Last,
+ M => PNV_M_Range'Last,
+ P => PNV_P_Range'Last,
+ VCO => PNV_VCO_Range'Last,
+ Dotclock => Clock_Range'Last);
+
+ type PNV_Limits_Type is
+ record
+ M_Lower : PNV_M_Range;
+ M_Upper : PNV_M_Range;
+ M2_Lower : PNV_M2_Range;
+ M2_Upper : PNV_M2_Range;
+ P_Lower : PNV_P_Range;
+ P_Upper : PNV_P_Range;
+ P1_Lower : PNV_P1_Range;
+ P1_Upper : PNV_P1_Range;
+ P2_Fast : PNV_P2_Range;
+ P2_Slow : PNV_P2_Range;
+ P2_Threshold : Clock_Range;
+ end record;
+
+ -- Pineview SDVO/DAC limits (from Linux pnv_limits_sdvo)
+ Pineview_SDVO_DAC_Limits : constant PNV_Limits_Type := PNV_Limits_Type'
+ (M_Lower => 2, M_Upper => 256,
+ M2_Lower => 0, M2_Upper => 254,
+ P_Lower => 5, P_Upper => 80,
+ P1_Lower => 1, P1_Upper => 8,
+ P2_Fast => 5, P2_Slow => 10,
+ P2_Threshold => 200_000_000);
+
+ -- Pineview LVDS limits (from Linux pnv_limits_lvds)
+ Pineview_LVDS_Limits : constant PNV_Limits_Type := PNV_Limits_Type'
+ (M_Lower => 2, M_Upper => 256,
+ M2_Lower => 0, M2_Upper => 254,
+ P_Lower => 7, P_Upper => 112,
+ P1_Lower => 1, P1_Upper => 8,
+ P2_Fast => 14, P2_Slow => 14,
+ P2_Threshold => 112_000_000);
+
----------------------------------------------------------------------------
type Regs is array (DPLLs) of Registers.Registers_Index;
@@ -139,7 +208,8 @@
DPLL_VGA_MODE_DIS : constant := 1 * 2 ** 28;
DPLL_P2_10_OR_14 : constant := 0 * 2 ** 24;
DPLL_P2_5_OR_7 : constant := 1 * 2 ** 24;
- DPLL_P1_DIVIDER_SHIFT : constant := 16;
+ DPLL_P1_DIVIDER_SHIFT : constant := 16;
+ DPLL_P1_DIVIDER_SHIFT_PINEVIEW : constant := 15;
DPLL_SDVOCLK : constant := 2 * 2 ** 13;
-- i945 does not use DPLL_PULSE_PHASE (bits 12:9, Gen4+ only)
@@ -348,6 +418,133 @@
end Calculate_Clock_Parameters;
+ procedure Verify_Pineview_Parameters
+ (N : in PNV_N_Range;
+ M2 : in PNV_M2_Range;
+ P1 : in PNV_P1_Range;
+ P2 : in PNV_P2_Range;
+ Reference_Clock : in Clock_Range;
+ Current_Limits : in PNV_Limits_Type;
+ Result : out PNV_Clock_Type;
+ Valid : out Boolean)
+ with
+ Global => null,
+ Pre => True,
+ Post => True
+ is
+ M : Int64;
+ P : Int64;
+ VCO : Int64;
+ Dotclock : Int64;
+ begin
+ pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ M := M2 + 2;
+ P := P1 * P2;
+ VCO := (Int64 (Reference_Clock) * M) / N;
+ Dotclock := VCO / P;
+
+ Valid :=
+ Current_Limits.P1_Lower <= P1 and P1 <= Current_Limits.P1_Upper and
+ (Current_Limits.P2_Fast = P2 or P2 = Current_Limits.P2_Slow) and
+ Current_Limits.P_Lower <= P and P <= Current_Limits.P_Upper and
+ Current_Limits.M2_Lower <= M2 and M2 <= Current_Limits.M2_Upper and
+ Current_Limits.M_Lower <= M and M <= Current_Limits.M_Upper and
+ PNV_VCO_Range'First <= VCO and VCO <= PNV_VCO_Range'Last and
+ Int64 (Clock_Range'First) <= Dotclock and
+ Dotclock <= Int64 (Clock_Range'Last);
+
+ if Valid
+ then
+ Result := PNV_Clock_Type'
+ (N => N,
+ M2 => M2,
+ P1 => P1,
+ P2 => P2,
+ Reference_Clock => Reference_Clock,
+ M => M,
+ P => P,
+ VCO => VCO,
+ Dotclock => Clock_Range (Dotclock));
+ else
+ Result := Invalid_PNV_Clock;
+ end if;
+ end Verify_Pineview_Parameters;
+
+ procedure Calculate_Pineview_Clock_Parameters
+ (Display : in Display_Type;
+ Target_Dotclock : in Clock_Range;
+ Reference_Clock : in Clock_Range;
+ Best_Clock : out PNV_Clock_Type;
+ Valid : out Boolean)
+ with
+ Global => null,
+ Pre => True,
+ Post => True
+ is
+ Limits : constant PNV_Limits_Type :=
+ (case Display is
+ when LVDS => Pineview_LVDS_Limits,
+ when others => Pineview_SDVO_DAC_Limits);
+
+ P2 : PNV_P2_Range;
+ Best_Delta : Int64 := Int64'Last;
+ Current_Delta : Int64;
+ Current_Clock : PNV_Clock_Type;
+ Registers_Valid : Boolean;
+ begin
+ pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Valid := False;
+ Best_Clock := Invalid_PNV_Clock;
+
+ if Target_Dotclock <= Limits.P2_Threshold then
+ P2 := Limits.P2_Slow;
+ else
+ P2 := Limits.P2_Fast;
+ end if;
+
+ -- Use the newer libgfxinit search order (N, then M2, then P1) and
+ -- descending M2/P1 loops. Linux's Pineview code iterates M2 outermost
+ -- and ascending; this only affects ties between parameter sets with
+ -- the same delta.
+ for N in PNV_N_Range loop
+ for M2 in reverse PNV_M2_Range range Limits.M2_Lower .. Limits.M2_Upper
+ loop
+ pragma Loop_Invariant (True);
+ for P1 in reverse PNV_P1_Range range Limits.P1_Lower .. Limits.P1_Upper
+ loop
+ Verify_Pineview_Parameters
+ (N => N,
+ M2 => M2,
+ P1 => P1,
+ P2 => P2,
+ Reference_Clock => Reference_Clock,
+ Current_Limits => Limits,
+ Result => Current_Clock,
+ Valid => Registers_Valid);
+
+ if Registers_Valid
+ then
+ if Current_Clock.Dotclock > Target_Dotclock
+ then
+ Current_Delta := Current_Clock.Dotclock - Target_Dotclock;
+ else
+ Current_Delta := Target_Dotclock - Current_Clock.Dotclock;
+ end if;
+
+ if Current_Delta < Best_Delta
+ then
+ Best_Delta := Current_Delta;
+ Best_Clock := Current_Clock;
+ Valid := True;
+ end if;
+ end if;
+ end loop;
+ end loop;
+ end loop;
+ end Calculate_Pineview_Clock_Parameters;
+
procedure Program_DPLL
(PLL : DPLLs;
Display : Display_Type;
@@ -389,27 +586,87 @@
Shift_Left (Encoded_P1, DPLL_P1_DIVIDER_SHIFT));
end Program_DPLL;
+ procedure Program_Pineview_DPLL
+ (PLL : DPLLs;
+ Display : Display_Type;
+ Clk : PNV_Clock_Type)
+ with
+ Global => (In_Out => Registers.Register_State),
+ Pre => True,
+ Post => True
+ is
+ FP, Encoded_P1, Encoded_P2 : Word32;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ -- Pineview frequency-parameter (FP) register: N is one-hot,
+ -- M1 is reserved, M2 is raw.
+ FP :=
+ Shift_Left (Shift_Left (Word32'(1), Natural (Clk.N)), FP_N_SHIFT) or
+ Shift_Left (Word32 (Clk.M2), FP_M2_SHIFT);
+
+ Registers.Write (FP0 (PLL), FP);
+ Registers.Write (FP1 (PLL), FP);
+
+ Encoded_P1 := Shift_Left (1, Natural (Clk.P1) - 1);
+
+ if Clk.P2 = 5 or Clk.P2 = 7
+ then
+ Encoded_P2 := DPLL_P2_5_OR_7;
+ else
+ Encoded_P2 := DPLL_P2_10_OR_14;
+ end if;
+
+ Registers.Write
+ (Register => DPLL (PLL),
+ Value => DPLL_Mode (Display) or
+ DPLL_VGA_MODE_DIS or
+ Encoded_P2 or
+ Shift_Left (Encoded_P1, DPLL_P1_DIVIDER_SHIFT_PINEVIEW));
+ end Program_Pineview_DPLL;
+
procedure On
(PLL : in T;
Port_Cfg : in Port_Config;
Success : out Boolean)
is
Target_Clock : constant Frequency_Type := Port_Cfg.Mode.Dotclock;
- Clk : Clock_Type;
begin
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
Success := PLL in DPLLs;
- Clk := Invalid_Clock;
if Success then
if Target_Clock <= 400_000_000 then
- Calculate_Clock_Parameters
- (Display => Port_Cfg.Display,
- Target_Dotclock => Target_Clock,
- Reference_Clock => 96_000_000,
- Best_Clock => Clk,
- Valid => Success);
+ if Config.CPU_Any_Pineview then
+ declare
+ Clk : PNV_Clock_Type;
+ begin
+ Calculate_Pineview_Clock_Parameters
+ (Display => Port_Cfg.Display,
+ Target_Dotclock => Target_Clock,
+ Reference_Clock => 96_000_000,
+ Best_Clock => Clk,
+ Valid => Success);
+ if Success then
+ Program_Pineview_DPLL (PLL, Port_Cfg.Display, Clk);
+ end if;
+ end;
+ else
+ declare
+ Clk : Clock_Type;
+ begin
+ Calculate_Clock_Parameters
+ (Display => Port_Cfg.Display,
+ Target_Dotclock => Target_Clock,
+ Reference_Clock => 96_000_000,
+ Best_Clock => Clk,
+ Valid => Success);
+ if Success then
+ Program_DPLL (PLL, Port_Cfg.Display, Clk);
+ end if;
+ end;
+ end if;
else
Success := False;
pragma Debug (Debug.Put ("WARNING: Targeted clock too high: "));
@@ -422,8 +679,6 @@
end if;
if Success then
- Program_DPLL (PLL, Port_Cfg.Display, Clk);
-
Registers.Set_Mask (DPLL (PLL), DPLL_VCO_ENABLE);
Registers.Posting_Read (DPLL (PLL));
Time.U_Delay (150);
diff --git a/common/i945/hw-gfx-gma-port_detect.adb b/common/i945/hw-gfx-gma-port_detect.adb
index af82c17..f2b29f0 100644
--- a/common/i945/hw-gfx-gma-port_detect.adb
+++ b/common/i945/hw-gfx-gma-port_detect.adb
@@ -31,8 +31,8 @@
-- i945: VGA (ADPA) is always present
Config.Valid_Port (Analog) := True;
- -- LVDS is only present on mobile (i945GM)
- Config.Valid_Port (LVDS) := Config.GMCH_I945GM;
+ -- LVDS is only present on mobile Gen3 parts (i945GM/Pineview-M)
+ Config.Valid_Port (LVDS) := Config.GMCH_Gen3_Mobile;
-- Enable CRT hotplug detection
Registers.Write
diff --git a/common/i945/hw-gfx-gma-power_and_clocks.adb b/common/i945/hw-gfx-gma-power_and_clocks.adb
index 2f6404a..6bc4555 100644
--- a/common/i945/hw-gfx-gma-power_and_clocks.adb
+++ b/common/i945/hw-gfx-gma-power_and_clocks.adb
@@ -31,7 +31,7 @@
HRAWCLK_333 : constant Frequency_Type := 333_333_333; -- FSB 1333
HRAWCLK_400 : constant Frequency_Type := 400_000_000; -- FSB 1600 (desktop)
- -- i945 CDClk values (from Linux kernel intel_cdclk.c):
+ -- i945/Pineview CDClk values (from Linux kernel intel_cdclk.c):
--
-- i945G (desktop): fixed 400 MHz CDClk
--
@@ -39,6 +39,14 @@
-- GC_LOW_FREQUENCY_ENABLE (bit 7) => 133 MHz
-- GC_DISPLAY_CLOCK_190_200_MHZ (0 << 4) => 200 MHz (default)
-- GC_DISPLAY_CLOCK_333_320_MHZ (4 << 4) => 320 MHz
+ --
+ -- Pineview: read from GCFGC PCI register (0xF0)
+ -- GC_DISPLAY_CLOCK_267_MHZ_PNV (0 << 4) => 267 MHz
+ -- GC_DISPLAY_CLOCK_333_MHZ_PNV (1 << 4) => 333 MHz
+ -- GC_DISPLAY_CLOCK_444_MHZ_PNV (2 << 4) => 444 MHz
+ -- GC_DISPLAY_CLOCK_200_MHZ_PNV (5 << 4) => 200 MHz
+ -- GC_DISPLAY_CLOCK_133_MHZ_PNV (6 << 4) => 133 MHz
+ -- GC_DISPLAY_CLOCK_167_MHZ_PNV (7 << 4) => 167 MHz
procedure Get_CDClk (CDClk : out Config.CDClk_Range)
is
@@ -47,10 +55,26 @@
GC_LOW_FREQUENCY_ENABLE : constant Word16 := 1 * 2 ** 7;
GC_DISPLAY_CLOCK_MASK : constant Word16 := 7 * 2 ** 4;
GC_DISPLAY_CLOCK_320_MHZ : constant Word16 := 4 * 2 ** 4;
+ GC_DISPLAY_CLOCK_267_MHZ_PNV : constant Word16 := 0 * 2 ** 4;
+ GC_DISPLAY_CLOCK_333_MHZ_PNV : constant Word16 := 1 * 2 ** 4;
+ GC_DISPLAY_CLOCK_444_MHZ_PNV : constant Word16 := 2 * 2 ** 4;
+ GC_DISPLAY_CLOCK_200_MHZ_PNV : constant Word16 := 5 * 2 ** 4;
+ GC_DISPLAY_CLOCK_133_MHZ_PNV : constant Word16 := 6 * 2 ** 4;
+ GC_DISPLAY_CLOCK_167_MHZ_PNV : constant Word16 := 7 * 2 ** 4;
GCFGC : Word16;
begin
- if Config.GMCH_I945GM then
+ if Config.CPU_Any_Pineview then
+ PCI_Read16 (GCFGC, 16#f0#);
+ CDClk :=
+ (case GCFGC and GC_DISPLAY_CLOCK_MASK is
+ when GC_DISPLAY_CLOCK_267_MHZ_PNV => 266_666_667,
+ when GC_DISPLAY_CLOCK_333_MHZ_PNV => 333_333_333,
+ when GC_DISPLAY_CLOCK_444_MHZ_PNV => 444_444_444,
+ when GC_DISPLAY_CLOCK_200_MHZ_PNV => 200_000_000,
+ when GC_DISPLAY_CLOCK_167_MHZ_PNV => 166_666_667,
+ when others => 133_333_333);
+ elsif Config.GMCH_I945GM then
PCI_Read16 (GCFGC, 16#f0#);
if (GCFGC and GC_LOW_FREQUENCY_ENABLE) /= 0 then
CDClk := 133_333_333;
@@ -66,7 +90,7 @@
end Get_CDClk;
-- hrawclk = FSB / 4. The CLKCFG FSB encoding differs between
- -- mobile (i945GM) and desktop (i945G), see CLKCFG_FSB_MASK comment above.
+ -- mobile/Pineview and desktop (i945G), see CLKCFG_FSB_MASK comment above.
procedure Get_Raw_Clock (Raw_Clock : out Frequency_Type)
is
CLK_CFG : Word32;
@@ -75,7 +99,7 @@
Registers.Read
(Register => Registers.GMCH_CLKCFG,
Value => CLK_CFG);
- if Config.GMCH_I945GM then
+ if Config.GMCH_Gen3_Mobile or Config.CPU_Any_Pineview then
Raw_Clock := (case Freq_Sel (CLK_CFG and CLKCFG_FSB_MASK) is
when 0 => HRAWCLK_100, -- FSB 400
when 1 => HRAWCLK_133, -- FSB 533