965 lines
34 KiB
Ada
965 lines
34 KiB
Ada
--
|
|
-- Copyright (C) 2014-2019 secunet Security Networks AG
|
|
-- Copyright (C) 2017 Nico Huber <nico.h@gmx.de>
|
|
--
|
|
-- 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.MMIO_Range;
|
|
pragma Elaborate_All (HW.MMIO_Range);
|
|
with HW.PCI.Dev;
|
|
pragma Elaborate_All (HW.PCI.Dev);
|
|
|
|
with HW.GFX.GMA.Config;
|
|
with HW.GFX.GMA.Config_Helpers;
|
|
with HW.GFX.GMA.Registers;
|
|
with HW.GFX.GMA.PCode;
|
|
with HW.GFX.GMA.Power_And_Clocks;
|
|
with HW.GFX.GMA.Panel;
|
|
with HW.GFX.GMA.PLLs;
|
|
with HW.GFX.GMA.Port_Detect;
|
|
with HW.GFX.GMA.Connectors;
|
|
with HW.GFX.GMA.Connector_Info;
|
|
with HW.GFX.GMA.Pipe_Setup;
|
|
|
|
with HW.Debug;
|
|
with GNAT.Source_Info;
|
|
|
|
use type HW.Int32;
|
|
|
|
package body HW.GFX.GMA
|
|
with Refined_State =>
|
|
(State =>
|
|
(Config.Variable,
|
|
PCI_Usable,
|
|
Dev.Address_State,
|
|
Registers.Address_State,
|
|
PCode.Mailbox_Ready,
|
|
PLLs.State, Panel.Panel_State,
|
|
Cur_Configs, Allocated_PLLs,
|
|
HPD_Delay, Wait_For_HPD,
|
|
Linear_FB_Base),
|
|
Init_State => Initialized,
|
|
Device_State =>
|
|
(Dev.PCI_State, Registers.Register_State, Registers.GTT_State))
|
|
is
|
|
pragma Disable_Atomic_Synchronization;
|
|
|
|
subtype Port_Name is String (1 .. 8);
|
|
type Port_Name_Array is array (Port_Type) of Port_Name;
|
|
Port_Names : constant Port_Name_Array :=
|
|
(Disabled => "Disabled",
|
|
Internal => "Internal",
|
|
DP1 => "DP1 ",
|
|
DP2 => "DP2 ",
|
|
DP3 => "DP3 ",
|
|
HDMI1 => "HDMI1 ",
|
|
HDMI2 => "HDMI2 ",
|
|
HDMI3 => "HDMI3 ",
|
|
Analog => "Analog ");
|
|
|
|
package Dev is new HW.PCI.Dev (PCI.Address'(0, 2, 0));
|
|
|
|
package Display_Controller renames Pipe_Setup;
|
|
|
|
type PLLs_Type is array (Pipe_Index) of PLLs.T;
|
|
|
|
type HPD_Type is array (Port_Type) of Boolean;
|
|
type HPD_Delay_Type is array (Active_Port_Type) of Time.T;
|
|
|
|
Allocated_PLLs : PLLs_Type;
|
|
HPD_Delay : HPD_Delay_Type;
|
|
Wait_For_HPD : HPD_Type;
|
|
Initialized : Boolean := False;
|
|
|
|
Linear_FB_Base : Word64;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
PCH_RAWCLK_FREQ_MASK : constant := 16#3ff# * 2 ** 0;
|
|
|
|
function PCH_RAWCLK_FREQ (Freq : Frequency_Type) return Word32
|
|
is
|
|
begin
|
|
return Word32 (Freq / 1_000_000);
|
|
end PCH_RAWCLK_FREQ;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
procedure Enable_Output
|
|
(Pipe : in Pipe_Index;
|
|
Pipe_Cfg : in Pipe_Config;
|
|
Success : out Boolean)
|
|
with
|
|
Pre =>
|
|
Pipe_Cfg.Port in Active_Port_Type and
|
|
Config_Helpers.Valid_FB (Pipe_Cfg.Framebuffer, Pipe_Cfg.Mode)
|
|
is
|
|
Port_Cfg : Port_Config;
|
|
begin
|
|
pragma Debug (Debug.New_Line);
|
|
pragma Debug (Debug.Put_Line
|
|
("Trying to enable port " & Port_Names (Pipe_Cfg.Port)));
|
|
|
|
Config_Helpers.Fill_Port_Config
|
|
(Port_Cfg, Pipe, Pipe_Cfg.Port, Pipe_Cfg.Mode, Success);
|
|
|
|
if Success then
|
|
Connector_Info.Preferred_Link_Setting (Port_Cfg, Success);
|
|
end if;
|
|
|
|
-- loop over all possible DP-lane configurations
|
|
-- (non-DP ports use a single fake configuration)
|
|
while Success loop
|
|
pragma Loop_Invariant
|
|
(Pipe_Cfg.Port in Active_Port_Type and
|
|
Port_Cfg.Mode = Port_Cfg.Mode'Loop_Entry);
|
|
|
|
PLLs.Alloc
|
|
(Port_Cfg => Port_Cfg,
|
|
PLL => Allocated_PLLs (Pipe),
|
|
Success => Success);
|
|
|
|
if Success then
|
|
-- try each DP-lane configuration twice
|
|
for Try in 1 .. 2 loop
|
|
pragma Loop_Invariant
|
|
(Pipe_Cfg.Port in Active_Port_Type);
|
|
|
|
-- Clear pending hot-plug events before every try
|
|
Port_Detect.Clear_Hotplug_Detect (Pipe_Cfg.Port);
|
|
|
|
Connectors.Pre_On
|
|
(Pipe => Pipe,
|
|
Port_Cfg => Port_Cfg,
|
|
PLL_Hint => PLLs.Register_Value (Allocated_PLLs (Pipe)),
|
|
Success => Success);
|
|
|
|
if Success then
|
|
Display_Controller.On
|
|
(Pipe => Pipe,
|
|
Port_Cfg => Port_Cfg,
|
|
Framebuffer => Pipe_Cfg.Framebuffer,
|
|
Cursor => Pipe_Cfg.Cursor);
|
|
|
|
Connectors.Post_On
|
|
(Pipe => Pipe,
|
|
Port_Cfg => Port_Cfg,
|
|
PLL_Hint => PLLs.Register_Value (Allocated_PLLs (Pipe)),
|
|
Success => Success);
|
|
|
|
if not Success then
|
|
Display_Controller.Off (Pipe);
|
|
Connectors.Post_Off (Port_Cfg);
|
|
end if;
|
|
end if;
|
|
|
|
exit when Success;
|
|
end loop;
|
|
exit when Success; -- connection established => stop loop
|
|
|
|
-- connection failed
|
|
PLLs.Free (Allocated_PLLs (Pipe));
|
|
end if;
|
|
|
|
Connector_Info.Next_Link_Setting (Port_Cfg, Success);
|
|
end loop;
|
|
|
|
if Success then
|
|
pragma Debug (Debug.Put_Line
|
|
("Enabled port " & Port_Names (Pipe_Cfg.Port)));
|
|
else
|
|
Wait_For_HPD (Pipe_Cfg.Port) := True;
|
|
if Pipe_Cfg.Port = Internal then
|
|
Panel.Off;
|
|
end if;
|
|
end if;
|
|
end Enable_Output;
|
|
|
|
procedure Disable_Output (Pipe : Pipe_Index; Pipe_Cfg : Pipe_Config)
|
|
is
|
|
Port_Cfg : Port_Config;
|
|
Success : Boolean;
|
|
begin
|
|
Config_Helpers.Fill_Port_Config
|
|
(Port_Cfg, Pipe, Pipe_Cfg.Port, Pipe_Cfg.Mode, Success);
|
|
if Success then
|
|
pragma Debug (Debug.New_Line);
|
|
pragma Debug (Debug.Put_Line
|
|
("Disabling port " & Port_Names (Pipe_Cfg.Port)));
|
|
pragma Debug (Debug.New_Line);
|
|
|
|
Connectors.Pre_Off (Port_Cfg);
|
|
Display_Controller.Off (Pipe);
|
|
Connectors.Post_Off (Port_Cfg);
|
|
|
|
PLLs.Free (Allocated_PLLs (Pipe));
|
|
end if;
|
|
end Disable_Output;
|
|
|
|
procedure Update_Outputs (Configs : Pipe_Configs)
|
|
is
|
|
procedure Check_HPD (Port : in Active_Port_Type; Detected : out Boolean)
|
|
is
|
|
HPD_Delay_Over : constant Boolean := Time.Timed_Out (HPD_Delay (Port));
|
|
begin
|
|
if HPD_Delay_Over then
|
|
Port_Detect.Hotplug_Detect (Port, Detected);
|
|
HPD_Delay (Port) := Time.MS_From_Now (333);
|
|
else
|
|
Detected := False;
|
|
end if;
|
|
end Check_HPD;
|
|
|
|
Scaler_Reservation : Display_Controller.Scaler_Reservation :=
|
|
Display_Controller.Null_Scaler_Reservation;
|
|
|
|
Update_Power : Boolean := False;
|
|
Update_CDClk : Boolean;
|
|
Old_Configs,
|
|
New_Configs : Pipe_Configs;
|
|
|
|
function Full_Update (Cur_Config, New_Config : Pipe_Config) return Boolean
|
|
is
|
|
begin
|
|
return
|
|
Cur_Config.Port /= New_Config.Port
|
|
or else
|
|
Cur_Config.Mode /= New_Config.Mode
|
|
or else
|
|
(Config.Use_PDW_For_EDP_Scaling and then
|
|
(Cur_Config.Port = Internal and
|
|
Requires_Scaling (Cur_Config) /= Requires_Scaling (New_Config)))
|
|
or else
|
|
(Config.Has_GMCH_PFIT_CONTROL and then
|
|
(Requires_Scaling (Cur_Config) /= Requires_Scaling (New_Config) or
|
|
Scaling_Type (Cur_Config) /= Scaling_Type (New_Config)));
|
|
end Full_Update;
|
|
begin
|
|
Old_Configs := Cur_Configs;
|
|
New_Configs := Configs;
|
|
|
|
-- validate new configs, filter invalid configs and those waiting for HPD
|
|
for Pipe in Pipe_Index loop
|
|
declare
|
|
Success : Boolean := True;
|
|
Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
|
|
New_Config : Pipe_Config renames New_Configs (Pipe);
|
|
begin
|
|
if New_Config.Port /= Disabled then
|
|
if Wait_For_HPD (New_Config.Port) then
|
|
Check_HPD (New_Config.Port, Success);
|
|
Wait_For_HPD (New_Config.Port) := not Success;
|
|
end if;
|
|
|
|
Success := Success and then
|
|
Config_Helpers.Validate_Config
|
|
(New_Config.Framebuffer, New_Config.Mode, Pipe);
|
|
|
|
if Success and then Requires_Scaling (New_Config) then
|
|
Display_Controller.Reserve_Scaler
|
|
(Success, Scaler_Reservation, Pipe);
|
|
end if;
|
|
|
|
if not Success then
|
|
New_Config.Port := Disabled;
|
|
end if;
|
|
end if;
|
|
end;
|
|
pragma Loop_Invariant
|
|
(for all P in Pipe_Index'First .. Pipe =>
|
|
New_Configs (P).Port = Disabled or
|
|
Config_Helpers.Valid_FB
|
|
(New_Configs (P).Framebuffer, New_Configs (P).Mode));
|
|
end loop;
|
|
|
|
-- limit dotclocks to maximum CDClk, if we are about
|
|
-- to switch CDClk, all pipes have to be disabled
|
|
Power_And_Clocks.Limit_Dotclocks (New_Configs, Update_CDClk);
|
|
|
|
-- disable all pipes that changed or had a hot-plug event
|
|
for Pipe in Pipe_Index loop
|
|
declare
|
|
Unplug_Detected : Boolean;
|
|
Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
|
|
New_Config : Pipe_Config renames New_Configs (Pipe);
|
|
begin
|
|
if Cur_Config.Port /= Disabled then
|
|
Check_HPD (Cur_Config.Port, Unplug_Detected);
|
|
|
|
if Update_CDClk or
|
|
Unplug_Detected or
|
|
Full_Update (Cur_Config, New_Config)
|
|
then
|
|
Disable_Output (Pipe, Cur_Config);
|
|
Cur_Config.Port := Disabled;
|
|
Update_Power := True;
|
|
end if;
|
|
end if;
|
|
end;
|
|
end loop;
|
|
|
|
-- switch CDClk if necessary and possible, limit dotclocks accordingly
|
|
if Update_CDClk then
|
|
Power_And_Clocks.Update_CDClk (New_Configs);
|
|
end if;
|
|
|
|
-- enable all pipes that changed and should be active
|
|
for Pipe in Pipe_Index loop
|
|
declare
|
|
Success : Boolean;
|
|
Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
|
|
New_Config : Pipe_Config renames New_Configs (Pipe);
|
|
begin
|
|
-- full update
|
|
if New_Config.Port /= Disabled and
|
|
Full_Update (Cur_Config, New_Config)
|
|
then
|
|
Power_And_Clocks.Power_Up (Old_Configs, New_Configs);
|
|
Update_Power := True;
|
|
|
|
Enable_Output (Pipe, New_Config, Success);
|
|
if Success then
|
|
Cur_Config := New_Config;
|
|
end if;
|
|
|
|
-- update framebuffer offset only
|
|
elsif New_Config.Port /= Disabled and
|
|
Cur_Config.Framebuffer /= New_Config.Framebuffer
|
|
then
|
|
Display_Controller.Setup_FB
|
|
(Pipe, New_Config.Mode, New_Config.Framebuffer);
|
|
Display_Controller.Update_Cursor
|
|
(Pipe, New_Config.Framebuffer, New_Config.Cursor);
|
|
Cur_Config := New_Config;
|
|
end if;
|
|
end;
|
|
end loop;
|
|
|
|
if Update_Power then
|
|
Power_And_Clocks.Power_Down (Old_Configs, New_Configs, Cur_Configs);
|
|
end if;
|
|
end Update_Outputs;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
procedure Update_Cursor (Pipe : Pipe_Index; Cursor : Cursor_Type)
|
|
is
|
|
begin
|
|
Cur_Configs (Pipe).Cursor := Cursor;
|
|
Display_Controller.Update_Cursor
|
|
(Pipe, Cur_Configs (Pipe).Framebuffer, Cur_Configs (Pipe).Cursor);
|
|
end Update_Cursor;
|
|
|
|
procedure Place_Cursor
|
|
(Pipe : Pipe_Index;
|
|
X : Cursor_Pos;
|
|
Y : Cursor_Pos)
|
|
is
|
|
begin
|
|
Cur_Configs (Pipe).Cursor.Center_X := X;
|
|
Cur_Configs (Pipe).Cursor.Center_Y := Y;
|
|
Display_Controller.Place_Cursor
|
|
(Pipe, Cur_Configs (Pipe).Framebuffer, Cur_Configs (Pipe).Cursor);
|
|
end Place_Cursor;
|
|
|
|
procedure Move_Cursor
|
|
(Pipe : Pipe_Index;
|
|
X : Cursor_Pos;
|
|
Y : Cursor_Pos)
|
|
is
|
|
function Cap_Add (A, B : Cursor_Pos) return Cursor_Pos is
|
|
(if A + B < 0
|
|
then Int32'Max (Cursor_Pos'First, A + B)
|
|
else Int32'Min (Cursor_Pos'Last, A + B));
|
|
begin
|
|
Place_Cursor
|
|
(Pipe => Pipe,
|
|
X => Cap_Add (Cur_Configs (Pipe).Cursor.Center_X, X),
|
|
Y => Cap_Add (Cur_Configs (Pipe).Cursor.Center_Y, Y));
|
|
end Move_Cursor;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
procedure Initialize
|
|
(Write_Delay : in Word64 := 0;
|
|
Clean_State : in Boolean := False;
|
|
Success : out Boolean)
|
|
with
|
|
Refined_Global =>
|
|
(Input => (Time.State),
|
|
In_Out => (Dev.PCI_State, Registers.Register_State, Port_IO.State),
|
|
Output =>
|
|
(PCI_Usable,
|
|
Config.Variable,
|
|
Dev.Address_State,
|
|
Registers.Address_State,
|
|
PCode.Mailbox_Ready,
|
|
PLLs.State, Panel.Panel_State,
|
|
Cur_Configs, Allocated_PLLs,
|
|
HPD_Delay, Wait_For_HPD,
|
|
Linear_FB_Base, Initialized))
|
|
is
|
|
use type HW.Word64;
|
|
|
|
function MMIO_GTT_Offset return Natural is
|
|
(if Config.Has_64bit_GTT
|
|
then Registers.MMIO_GTT_64_Offset
|
|
else Registers.MMIO_GTT_32_Offset);
|
|
PCI_MMIO_Base, PCI_GTT_Base : Word64;
|
|
|
|
Now : constant Time.T := Time.Now;
|
|
|
|
procedure Check_Platform (Success : out Boolean)
|
|
is
|
|
Audio_VID_DID : Word32;
|
|
begin
|
|
case Config.Gen is
|
|
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 .. Skylake =>
|
|
Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
|
|
end case;
|
|
Success :=
|
|
((Config.Gen_Broxton and Audio_VID_DID = 16#8086_280a#) or
|
|
(Config.CPU_Kabylake and Audio_VID_DID = 16#8086_280b#) or
|
|
(Config.CPU_Skylake and Audio_VID_DID = 16#8086_2809#) or
|
|
(Config.CPU_Broadwell and Audio_VID_DID = 16#8086_2808#) or
|
|
(Config.CPU_Haswell and Audio_VID_DID = 16#8086_2807#) or
|
|
((Config.CPU_Ivybridge or
|
|
Config.CPU_Sandybridge) and (Audio_VID_DID = 16#8086_2806# or
|
|
Audio_VID_DID = 16#8086_2805#)) or
|
|
(Config.CPU_Ironlake and Audio_VID_DID = 16#0000_0000#) or
|
|
(Config.Gen_G45 and (Audio_VID_DID = 16#8086_2801# or
|
|
Audio_VID_DID = 16#8086_2802# or
|
|
Audio_VID_DID = 16#8086_2803#)));
|
|
end Check_Platform;
|
|
|
|
procedure Check_Platform_PCI (Success : out Boolean)
|
|
is
|
|
use type HW.Word16;
|
|
Vendor, Device : Word16;
|
|
begin
|
|
Dev.Read16 (Vendor, PCI.Vendor_Id);
|
|
Dev.Read16 (Device, PCI.Device_Id);
|
|
|
|
Config.Detect_CPU (Device);
|
|
Success := Vendor = 16#8086# and Config.Compatible_GPU (Device);
|
|
end Check_Platform_PCI;
|
|
begin
|
|
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
|
|
|
|
pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
|
|
|
|
PCI_Usable := False;
|
|
Linear_FB_Base := 0;
|
|
PCode.Mailbox_Ready := False;
|
|
Wait_For_HPD := HPD_Type'(others => False);
|
|
HPD_Delay := HPD_Delay_Type'(others => Now);
|
|
Allocated_PLLs := (others => PLLs.Invalid);
|
|
Cur_Configs := Pipe_Configs'
|
|
(others => Pipe_Config'
|
|
(Port => Disabled,
|
|
Framebuffer => HW.GFX.Default_FB,
|
|
Cursor => Default_Cursor,
|
|
Mode => HW.GFX.Invalid_Mode));
|
|
Config.Variable := Config.Initial_Settings;
|
|
Registers.Set_Register_Base (Config.Default_MMIO_Base);
|
|
PLLs.Initialize;
|
|
|
|
Dev.Initialize (Success);
|
|
|
|
if Success then
|
|
Check_Platform_PCI (Success);
|
|
if Success then
|
|
Dev.Map (PCI_MMIO_Base, PCI.Res0, Length => MMIO_GTT_Offset);
|
|
Dev.Map (PCI_GTT_Base, PCI.Res0, Offset => MMIO_GTT_Offset);
|
|
if PCI_MMIO_Base /= 0 and PCI_GTT_Base /= 0 then
|
|
Registers.Set_Register_Base (PCI_MMIO_Base, PCI_GTT_Base);
|
|
PCI_Usable := True;
|
|
else
|
|
pragma Debug (Debug.Put_Line
|
|
("ERROR: Couldn't map resoure0."));
|
|
Success := Config.Default_MMIO_Base_Set;
|
|
end if;
|
|
end if;
|
|
else
|
|
pragma Debug (Debug.Put_Line
|
|
("WARNING: Couldn't initialize PCI dev."));
|
|
Success := Config.Default_MMIO_Base_Set;
|
|
|
|
if Success then
|
|
Check_Platform (Success);
|
|
end if;
|
|
end if;
|
|
|
|
if not Success then
|
|
pragma Debug (Debug.Put_Line ("ERROR: Incompatible CPU or PCH."));
|
|
|
|
Panel.Static_Init; -- for flow analysis
|
|
|
|
Initialized := False;
|
|
return;
|
|
end if;
|
|
|
|
Panel.Setup_PP_Sequencer;
|
|
Port_Detect.Initialize;
|
|
Connectors.Initialize;
|
|
|
|
if Clean_State then
|
|
Power_And_Clocks.Pre_All_Off;
|
|
Connectors.Pre_All_Off;
|
|
Display_Controller.All_Off;
|
|
Connectors.Post_All_Off;
|
|
PLLs.All_Off;
|
|
Power_And_Clocks.Post_All_Off;
|
|
Registers.Clear_Fences;
|
|
else
|
|
-- According to PRMs, VGA plane is the only thing
|
|
-- that's enabled by default after reset...
|
|
Display_Controller.Legacy_VGA_Off;
|
|
-- ... along with some DDI port bits since Skylake.
|
|
Connectors.Post_Reset_Off;
|
|
end if;
|
|
|
|
-------------------- Now restart from a clean state ---------------------
|
|
Power_And_Clocks.Initialize;
|
|
|
|
if Config.Has_PCH then
|
|
Registers.Unset_And_Set_Mask
|
|
(Register => Registers.PCH_RAWCLK_FREQ,
|
|
Mask_Unset => PCH_RAWCLK_FREQ_MASK,
|
|
Mask_Set => PCH_RAWCLK_FREQ (Config.Default_RawClk_Freq));
|
|
end if;
|
|
|
|
Initialized := True;
|
|
|
|
end Initialize;
|
|
|
|
function Is_Initialized return Boolean
|
|
with
|
|
Refined_Post => Is_Initialized'Result = Initialized
|
|
is
|
|
begin
|
|
return Initialized;
|
|
end Is_Initialized;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
pragma Warnings
|
|
(GNATprove, Off, """Registers.Register_State"" * is not modified*",
|
|
Reason => "Power_Up_VGA is only effective in certain configurations.");
|
|
procedure Power_Up_VGA
|
|
with
|
|
Refined_Global =>
|
|
(Input => (Cur_Configs, Config.Variable, Time.State),
|
|
In_Out => (Registers.Register_State),
|
|
Proof_In => (Initialized))
|
|
is
|
|
Fake_Config : constant Pipe_Configs :=
|
|
(Primary =>
|
|
(Port => Analog,
|
|
Framebuffer => HW.GFX.Default_FB,
|
|
Cursor => Default_Cursor,
|
|
Mode => HW.GFX.Invalid_Mode),
|
|
others =>
|
|
(Port => Disabled,
|
|
Framebuffer => HW.GFX.Default_FB,
|
|
Cursor => Default_Cursor,
|
|
Mode => HW.GFX.Invalid_Mode));
|
|
begin
|
|
Power_And_Clocks.Power_Up (Cur_Configs, Fake_Config);
|
|
end Power_Up_VGA;
|
|
pragma Warnings
|
|
(GNATprove, Off, "no check message justified*", Reason => "see below");
|
|
pragma Annotate
|
|
(GNATprove, Intentional, "unused global",
|
|
"Power_Up_VGA is only effective in certain configurations.");
|
|
pragma Warnings (GNATprove, On, "no check message justified*");
|
|
pragma Warnings
|
|
(GNATprove, On, """Registers.Register_State"" * is not modified*");
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
function FB_First_Page (FB : Framebuffer_Type) return Natural is
|
|
(Natural (Phys_Offset (FB) / GTT_Page_Size));
|
|
function FB_Pages (FB : Framebuffer_Type) return Natural is
|
|
(Natural (Div_Round_Up (FB_Size (FB), GTT_Page_Size)));
|
|
function FB_Last_Page (FB : Framebuffer_Type) return Natural is
|
|
(FB_First_Page (FB) + FB_Pages (FB) - 1);
|
|
|
|
-- Check basics and that it fits in GTT. For 90 degree rotations,
|
|
-- the Offset should be above GTT_Rotation_Offset. The latter will
|
|
-- be subtracted for the aperture mapping.
|
|
function Valid_FB (FB : Framebuffer_Type) return Boolean is
|
|
(Valid_Stride (FB) and
|
|
FB_First_Page (FB) in GTT_Range and
|
|
FB_Last_Page (FB) in GTT_Range and
|
|
(not Rotation_90 (FB) or
|
|
(FB_Last_Page (FB) + GTT_Rotation_Offset in GTT_Range and
|
|
FB.Offset >= Word32 (GTT_Rotation_Offset) * GTT_Page_Size)));
|
|
|
|
-- Also check that we don't overflow the GTT's 39-bit space
|
|
-- (always true with a 32-bit base)
|
|
function Valid_Phys_FB (FB : Framebuffer_Type; Phys_Base : Word32)
|
|
return Boolean is
|
|
(Valid_FB (FB) and
|
|
Int64 (Phys_Base) + Int64 (Phys_Offset (FB)) + Int64 (FB_Size (FB)) <=
|
|
Int64 (GTT_Address_Type'Last))
|
|
with
|
|
Ghost;
|
|
|
|
procedure Write_GTT
|
|
(GTT_Page : GTT_Range;
|
|
Device_Address : GTT_Address_Type;
|
|
Valid : Boolean)
|
|
is
|
|
begin
|
|
Registers.Write_GTT (GTT_Page, Device_Address, Valid);
|
|
end Write_GTT;
|
|
|
|
procedure Read_GTT
|
|
(Device_Address : out GTT_Address_Type;
|
|
Valid : out Boolean;
|
|
GTT_Page : in GTT_Range)
|
|
is
|
|
begin
|
|
Registers.Read_GTT (Device_Address, Valid, GTT_Page);
|
|
end Read_GTT;
|
|
|
|
procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_Base : Word32)
|
|
with
|
|
Pre => Is_Initialized and Valid_Phys_FB (FB, Phys_Base)
|
|
is
|
|
Phys_Addr : GTT_Address_Type :=
|
|
GTT_Address_Type (Phys_Base) + GTT_Address_Type (Phys_Offset (FB));
|
|
begin
|
|
for Idx in FB_First_Page (FB) .. FB_Last_Page (FB) loop
|
|
Registers.Write_GTT
|
|
(GTT_Page => Idx,
|
|
Device_Address => Phys_Addr,
|
|
Valid => True);
|
|
Phys_Addr := Phys_Addr + GTT_Page_Size;
|
|
end loop;
|
|
|
|
if Rotation_90 (FB) and FB.Tiling = Y_Tiled and FB.V_Stride >= 32 then
|
|
declare
|
|
V_Pages : constant Natural := Natural (FB.V_Stride) / 32;
|
|
Bytes_Per_Row : constant GTT_Address_Type :=
|
|
GTT_Address_Type (Pixel_To_Bytes (32 * FB.Stride, FB));
|
|
begin
|
|
Phys_Addr := GTT_Address_Type (Phys_Base) +
|
|
GTT_Address_Type (Phys_Offset (FB)) +
|
|
GTT_Address_Type (FB_Size (FB));
|
|
for Page in FB_First_Page (FB) .. FB_Last_Page (FB) loop
|
|
Phys_Addr := Phys_Addr - Bytes_Per_Row;
|
|
Registers.Write_GTT
|
|
(GTT_Page => GTT_Rotation_Offset + Page,
|
|
Device_Address => Phys_Addr,
|
|
Valid => True);
|
|
|
|
if (Page - FB_First_Page (FB) + 1) mod V_Pages = 0 then
|
|
Phys_Addr := Phys_Addr + GTT_Page_Size +
|
|
GTT_Address_Type (V_Pages) * Bytes_Per_Row;
|
|
end if;
|
|
end loop;
|
|
end;
|
|
end if;
|
|
end Setup_Default_GTT;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
use type HW.Word16;
|
|
subtype Stolen_Size_Range is Int64 range 0 .. 2 ** 33;
|
|
|
|
function GGMS_Gen4 (GGC : Word16) return Natural is
|
|
(Natural (Shift_Right (GGC, 8) and 16#07#));
|
|
function GTT_Size_Gen4 (GGC : Word16) return Natural is
|
|
(if GGMS_Gen4 (GGC) in 1 .. 3 then
|
|
(GGMS_Gen4 (GGC) + 1) * 2 ** 19 else 0);
|
|
|
|
function GMS_Gen4 (GGC : Word16) return Natural is
|
|
(Natural (Shift_Right (GGC, 4) and 16#0f#));
|
|
Valid_Stolen_Size_Gen4 : constant
|
|
array (Natural range 1 .. 13) of Stolen_Size_Range :=
|
|
(1, 4, 8, 16, 32, 48, 64, 128, 256, 96, 160, 224, 352);
|
|
function Stolen_Size_Gen4 (GGC : Word16) return Stolen_Size_Range is
|
|
(if GMS_Gen4 (GGC) in Valid_Stolen_Size_Gen4'Range then
|
|
Valid_Stolen_Size_Gen4 (GMS_Gen4 (GGC)) * 2 ** 20 else 0);
|
|
|
|
function GTT_Size_Gen6 (GGC : Word16) return Natural is
|
|
(Natural (Shift_Right (GGC, 8) and 16#03#) * 2 ** 20);
|
|
|
|
function Stolen_Size_Gen6 (GGC : Word16) return Stolen_Size_Range is
|
|
(Stolen_Size_Range (Shift_Right (GGC, 3) and 16#1f#) * 32 * 2 ** 20);
|
|
|
|
function GGMS_Gen8 (GGC : Word16) return Natural is
|
|
(Natural (Shift_Right (GGC, 6) and 16#03#));
|
|
function GTT_Size_Gen8 (GGC : Word16) return Natural is
|
|
(if GGMS_Gen8 (GGC) /= 0 then
|
|
Natural (Shift_Left (Word32'(1), 20 + GGMS_Gen8 (GGC))) else 0);
|
|
|
|
function GMS_Gen8 (GGC : Word16) return Stolen_Size_Range is
|
|
(Stolen_Size_Range (Shift_Right (GGC, 8) and 16#ff#));
|
|
function Stolen_Size_Gen8 (GGC : Word16) return Stolen_Size_Range is
|
|
(GMS_Gen8 (GGC) * 32 * 2 ** 20);
|
|
|
|
function Stolen_Size_Gen9 (GGC : Word16) return Stolen_Size_Range is
|
|
(if GMS_Gen8 (GGC) < 16#f0# then
|
|
Stolen_Size_Gen8 (GGC)
|
|
else
|
|
(GMS_Gen8 (GGC) - 16#f0# + 1) * 4 * 2 ** 20);
|
|
|
|
procedure Decode_Stolen
|
|
(GTT_Size : out Natural;
|
|
Stolen_Size : out Stolen_Size_Range)
|
|
with
|
|
Pre => Is_Initialized
|
|
is
|
|
GGC_Reg : constant PCI.Index :=
|
|
(if Config.Gen_G45 or Config.CPU_Ironlake then 16#52# else 16#50#);
|
|
GGC : Word16;
|
|
begin
|
|
Dev.Read16 (GGC, GGC_Reg);
|
|
if Config.Gen_G45 or Config.CPU_Ironlake then
|
|
GTT_Size := GTT_Size_Gen4 (GGC);
|
|
Stolen_Size := Stolen_Size_Gen4 (GGC);
|
|
elsif Config.CPU_Sandybridge or Config.CPU_Ivybridge or Config.CPU_Haswell
|
|
then
|
|
GTT_Size := GTT_Size_Gen6 (GGC);
|
|
Stolen_Size := Stolen_Size_Gen6 (GGC);
|
|
elsif Config.CPU_Broadwell then
|
|
GTT_Size := GTT_Size_Gen8 (GGC);
|
|
Stolen_Size := Stolen_Size_Gen8 (GGC);
|
|
else
|
|
GTT_Size := GTT_Size_Gen8 (GGC);
|
|
Stolen_Size := Stolen_Size_Gen9 (GGC);
|
|
end if;
|
|
end Decode_Stolen;
|
|
|
|
-- Additional runtime validation that FB fits stolen memory and aperture.
|
|
procedure Validate_FB (FB : Framebuffer_Type; Valid : out Boolean)
|
|
with
|
|
Pre => Is_Initialized,
|
|
Post => (if Valid then Valid_FB (FB))
|
|
is
|
|
GTT_Size, Aperture_Size : Natural;
|
|
Stolen_Size : Stolen_Size_Range;
|
|
begin
|
|
Valid := Valid_FB (FB);
|
|
|
|
if Valid then
|
|
Decode_Stolen (GTT_Size, Stolen_Size);
|
|
Dev.Resource_Size (Aperture_Size, PCI.Res2);
|
|
Valid :=
|
|
FB_Last_Page (FB) < GTT_Size / Config.GTT_PTE_Size and
|
|
FB_Last_Page (FB) < Natural (Stolen_Size / GTT_Page_Size) and
|
|
FB_Last_Page (FB) < Aperture_Size / GTT_Page_Size;
|
|
pragma Debug (not Valid, Debug.Put_Line
|
|
("Stolen memory too small to hold framebuffer."));
|
|
end if;
|
|
end Validate_FB;
|
|
|
|
procedure Setup_Default_FB
|
|
(FB : in Framebuffer_Type;
|
|
Clear : in Boolean := True;
|
|
Success : out Boolean)
|
|
is
|
|
GMA_Phys_Base : constant PCI.Index := 16#5c#;
|
|
GMA_Phys_Base_Mask : constant := 16#fff0_0000#;
|
|
|
|
Phys_Base : Word32;
|
|
begin
|
|
Validate_FB (FB, Success);
|
|
|
|
if Success then
|
|
Dev.Read32 (Phys_Base, GMA_Phys_Base);
|
|
Phys_Base := Phys_Base and GMA_Phys_Base_Mask;
|
|
Success := Phys_Base /= GMA_Phys_Base_Mask and Phys_Base /= 0;
|
|
pragma Debug (not Success, Debug.Put_Line
|
|
("Failed to read stolen memory base."));
|
|
|
|
if Success then
|
|
if FB.Tiling in XY_Tiling then
|
|
Registers.Add_Fence
|
|
(First_Page => FB_First_Page (FB),
|
|
Last_Page => FB_Last_Page (FB),
|
|
Tiling => FB.Tiling,
|
|
Pitch => FB_Pitch (FB.Stride, FB),
|
|
Success => Success);
|
|
end if;
|
|
pragma Debug (not Success, Debug.Put_Line
|
|
("Tiled framebuffer but no fence regs available."));
|
|
end if;
|
|
|
|
if Success then
|
|
Setup_Default_GTT (FB, Phys_Base);
|
|
end if;
|
|
end if;
|
|
|
|
if Success and then Clear then
|
|
declare
|
|
use type HW.Word64;
|
|
Linear_FB : Word64;
|
|
begin
|
|
Map_Linear_FB (Linear_FB, FB);
|
|
if Linear_FB /= 0 then
|
|
Framebuffer_Filler.Fill (Linear_FB, FB);
|
|
end if;
|
|
end;
|
|
end if;
|
|
end Setup_Default_FB;
|
|
|
|
procedure Map_Linear_FB (Linear_FB : out Word64; FB : in Framebuffer_Type)
|
|
is
|
|
use type HW.Word64;
|
|
|
|
Valid : Boolean;
|
|
begin
|
|
Linear_FB := 0;
|
|
|
|
if Linear_FB_Base = 0 then
|
|
Dev.Map (Linear_FB_Base, PCI.Res2);
|
|
pragma Debug
|
|
(Linear_FB_Base = 0, Debug.Put_Line ("Failed to map resource2."));
|
|
end if;
|
|
|
|
if Linear_FB_Base /= 0 then
|
|
Validate_FB (FB, Valid);
|
|
if Valid then
|
|
Linear_FB := Linear_FB_Base + Word64 (Phys_Offset (FB));
|
|
end if;
|
|
end if;
|
|
end Map_Linear_FB;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
procedure Dump_Configs (Configs : Pipe_Configs)
|
|
is
|
|
subtype Pipe_Name is String (1 .. 9);
|
|
type Pipe_Name_Array is array (Pipe_Index) of Pipe_Name;
|
|
Pipe_Names : constant Pipe_Name_Array :=
|
|
(Primary => "Primary ",
|
|
Secondary => "Secondary",
|
|
Tertiary => "Tertiary ");
|
|
|
|
subtype Tiling_Name is String (1 .. 7);
|
|
type Tiling_Name_Array is array (Tiling_Type) of Tiling_Name;
|
|
Tilings : constant Tiling_Name_Array :=
|
|
(Linear => "Linear ",
|
|
X_Tiled => "X_Tiled",
|
|
Y_Tiled => "Y_Tiled");
|
|
|
|
subtype Rotation_Name is String (1 .. 11);
|
|
type Rotation_Name_Array is array (Rotation_Type) of Rotation_Name;
|
|
Rotations : constant Rotation_Name_Array :=
|
|
(No_Rotation => "No_Rotation",
|
|
Rotated_90 => "Rotated_90 ",
|
|
Rotated_180 => "Rotated_180",
|
|
Rotated_270 => "Rotated_270");
|
|
begin
|
|
Debug.New_Line;
|
|
Debug.Put_Line ("CONFIG =>");
|
|
for Pipe in Pipe_Index loop
|
|
if Pipe = Pipe_Index'First then
|
|
Debug.Put (" (");
|
|
else
|
|
Debug.Put (" ");
|
|
end if;
|
|
Debug.Put_Line (Pipe_Names (Pipe) & " =>");
|
|
Debug.Put_Line
|
|
(" (Port => " & Port_Names (Configs (Pipe).Port) & ",");
|
|
Debug.Put_Line (" Framebuffer =>");
|
|
Debug.Put (" (Width => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" Height => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" Start_X => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Framebuffer.Start_X);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" Start_Y => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Framebuffer.Start_Y);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" Stride => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" V_Stride => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Framebuffer.V_Stride);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" Tiling => ");
|
|
Debug.Put_Line (Tilings (Configs (Pipe).Framebuffer.Tiling) & ",");
|
|
Debug.Put (" Rotation => ");
|
|
Debug.Put_Line (Rotations (Configs (Pipe).Framebuffer.Rotation) & ",");
|
|
Debug.Put (" Offset => ");
|
|
Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" BPC => ");
|
|
Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
|
|
Debug.Put_Line ("),");
|
|
Debug.Put_Line (" Mode =>");
|
|
Debug.Put (" (Dotclock => ");
|
|
Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" H_Visible => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Mode.H_Visible);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" H_Sync_Begin => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Mode.H_Sync_Begin);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" H_Sync_End => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Mode.H_Sync_End);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" H_Total => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Mode.H_Total);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" V_Visible => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Mode.V_Visible);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" V_Sync_Begin => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Mode.V_Sync_Begin);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" V_Sync_End => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Mode.V_Sync_End);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" V_Total => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Mode.V_Total);
|
|
Debug.Put_Line (",");
|
|
Debug.Put_Line (" H_Sync_Active_High => " &
|
|
(if Configs (Pipe).Mode.H_Sync_Active_High
|
|
then "True,"
|
|
else "False,"));
|
|
Debug.Put_Line (" V_Sync_Active_High => " &
|
|
(if Configs (Pipe).Mode.V_Sync_Active_High
|
|
then "True,"
|
|
else "False,"));
|
|
Debug.Put (" BPC => ");
|
|
Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
|
|
if Pipe /= Pipe_Index'Last then
|
|
Debug.Put_Line (")),");
|
|
else
|
|
Debug.Put_Line (")));");
|
|
end if;
|
|
end loop;
|
|
end Dump_Configs;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
procedure PCI_Read16 (Value : out Word16; Offset : HW.PCI.Index) is
|
|
begin
|
|
Dev.Read16 (Value, Offset);
|
|
end PCI_Read16;
|
|
|
|
end HW.GFX.GMA;
|