837 lines
28 KiB
Ada
837 lines
28 KiB
Ada
--
|
|
-- Copyright (C) 2014-2016 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; version 2 of the License.
|
|
--
|
|
-- 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.GFX.EDID;
|
|
with HW.GFX.GMA.Config;
|
|
with HW.GFX.GMA.DP_Info;
|
|
with HW.GFX.GMA.Registers;
|
|
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 System;
|
|
|
|
with HW.Debug;
|
|
with GNAT.Source_Info;
|
|
|
|
use type HW.Word8;
|
|
use type HW.Int32;
|
|
|
|
package body HW.GFX.GMA
|
|
with Refined_State =>
|
|
(State =>
|
|
(Registers.Address_State,
|
|
PLLs.State, Panel.Panel_State,
|
|
Cur_Configs, Allocated_PLLs, DP_Links,
|
|
HPD_Delay, Wait_For_HPD),
|
|
Init_State => Initialized,
|
|
Config_State => Config.Valid_Port_GPU,
|
|
Device_State =>
|
|
(Registers.Register_State, Registers.GTT_State))
|
|
is
|
|
|
|
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 ",
|
|
Digital1 => "Digital1",
|
|
Digital2 => "Digital2",
|
|
Digital3 => "Digital3",
|
|
Analog => "Analog ");
|
|
|
|
package Display_Controller renames Pipe_Setup;
|
|
|
|
type PLLs_Type is array (Config_Index) of PLLs.T;
|
|
|
|
type Links_Type is array (Config_Index) of DP_Link;
|
|
|
|
type HPD_Type is array (Port_Type) of Boolean;
|
|
type HPD_Delay_Type is array (Port_Type) of Time.T;
|
|
|
|
Cur_Configs : Configs_Type;
|
|
Allocated_PLLs : PLLs_Type;
|
|
DP_Links : Links_Type;
|
|
HPD_Delay : HPD_Delay_Type;
|
|
Wait_For_HPD : HPD_Type;
|
|
Initialized : Boolean := False;
|
|
|
|
subtype Active_Port_Type is Port_Type range Port_Type'Succ (Disabled) .. Port_Type'Last;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
function To_GPU_Port
|
|
(Configs : Configs_Type;
|
|
Idx : Config_Index)
|
|
return GPU_Port
|
|
is
|
|
begin
|
|
return
|
|
(case Config.CPU is
|
|
when Ironlake .. Ivybridge => -- everything but eDP through FDI/PCH
|
|
(if Config.Internal_Is_EDP and then Configs (Idx).Port = Internal
|
|
then
|
|
DIGI_A
|
|
else
|
|
(case Idx is
|
|
-- FDIs are fixed to the CPU pipe
|
|
when Primary => DIGI_B,
|
|
when Secondary => DIGI_C,
|
|
when Tertiary => DIGI_D)),
|
|
when Haswell .. Skylake => -- everything but VGA directly on CPU
|
|
(case Configs (Idx).Port is
|
|
when Disabled => GPU_Port'First,
|
|
when Internal => DIGI_A, -- LVDS not available
|
|
when Digital1 | DP1 => DIGI_B,
|
|
when Digital2 | DP2 => DIGI_C,
|
|
when Digital3 | DP3 => DIGI_D,
|
|
when Analog => DIGI_E));
|
|
end To_GPU_Port;
|
|
|
|
function To_PCH_Port (Port : Active_Port_Type) return PCH_Port
|
|
is
|
|
begin
|
|
return
|
|
(case Port is
|
|
when Internal => PCH_LVDS, -- will be ignored if Internal is DP
|
|
when Analog => PCH_DAC,
|
|
when Digital1 => PCH_HDMI_B,
|
|
when Digital2 => PCH_HDMI_C,
|
|
when Digital3 => PCH_HDMI_D,
|
|
when DP1 => PCH_DP_B,
|
|
when DP2 => PCH_DP_C,
|
|
when DP3 => PCH_DP_D);
|
|
end To_PCH_Port;
|
|
|
|
function To_Display_Type (Port : Active_Port_Type) return Display_Type
|
|
with Pre => True
|
|
is
|
|
begin
|
|
return
|
|
(case Port is
|
|
when Internal => Config.Internal_Display,
|
|
when Analog => VGA,
|
|
when Digital1 |
|
|
Digital2 |
|
|
Digital3 => HDMI,
|
|
when DP1 |
|
|
DP2 |
|
|
DP3 => DP);
|
|
end To_Display_Type;
|
|
|
|
procedure Configure_FDI_Link
|
|
(Port_Cfg : in out Port_Config;
|
|
Success : out Boolean)
|
|
with Pre => True
|
|
is
|
|
procedure Limit_Lane_Count
|
|
is
|
|
FDI_TX_CTL_FDI_TX_ENABLE : constant := 1 * 2 ** 31;
|
|
Enabled : Boolean;
|
|
begin
|
|
-- if DIGI_D enabled: (FDI names are off by one)
|
|
Registers.Is_Set_Mask
|
|
(Register => Registers.FDI_TX_CTL_C,
|
|
Mask => FDI_TX_CTL_FDI_TX_ENABLE,
|
|
Result => Enabled);
|
|
if Enabled then
|
|
Port_Cfg.FDI.Receiver_Caps.Max_Lane_Count := DP_Lane_Count_2;
|
|
end if;
|
|
end Limit_Lane_Count;
|
|
begin
|
|
Port_Cfg.FDI.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_2_7;
|
|
Port_Cfg.FDI.Receiver_Caps.Max_Lane_Count :=
|
|
Config.FDI_Lane_Count (Port_Cfg.Port);
|
|
Port_Cfg.FDI.Receiver_Caps.Enhanced_Framing := True;
|
|
if Config.Has_FDI_C and then Port_Cfg.Port = DIGI_C then
|
|
Limit_Lane_Count;
|
|
end if;
|
|
DP_Info.Preferred_Link_Setting (Port_Cfg.FDI, Port_Cfg.Mode, Success);
|
|
end Configure_FDI_Link;
|
|
|
|
procedure Fill_Port_Config
|
|
(Port_Cfg : out Port_Config;
|
|
Configs : in Configs_Type;
|
|
Idx : in Config_Index;
|
|
Success : out Boolean)
|
|
with Pre => True
|
|
is
|
|
begin
|
|
Success :=
|
|
Config.Supported_Pipe (Idx) and then
|
|
Config.Valid_Port (Configs (Idx).Port) and then
|
|
Configs (Idx).Port /= Disabled;
|
|
|
|
if Success then
|
|
declare
|
|
Port : constant Port_Type := Configs (Idx).Port;
|
|
Mode : constant Mode_Type := Configs (Idx).Mode;
|
|
Link : constant DP_Link := DP_Links (Idx);
|
|
begin
|
|
Port_Cfg := Port_Config'
|
|
(Port => To_GPU_Port (Configs, Idx),
|
|
PCH_Port => To_PCH_Port (Port),
|
|
Display => To_Display_Type (Port),
|
|
Mode => Mode,
|
|
Is_FDI => Config.FDI_Port (To_GPU_Port (Configs, Idx)),
|
|
FDI => Default_DP,
|
|
DP => Link);
|
|
if Port_Cfg.Mode.BPC = Auto_BPC then
|
|
Port_Cfg.Mode.BPC := Connector_Info.Default_BPC (Port_Cfg);
|
|
end if;
|
|
end;
|
|
else
|
|
Port_Cfg := Port_Config'
|
|
(Port => GPU_Port'First,
|
|
PCH_Port => PCH_Port'First,
|
|
Display => Display_Type'First,
|
|
Mode => Invalid_Mode,
|
|
Is_FDI => False,
|
|
FDI => Default_DP,
|
|
DP => Default_DP);
|
|
end if;
|
|
end Fill_Port_Config;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
function To_Controller
|
|
(Dsp_Config : Config_Index) return Display_Controller.Controller_Type
|
|
is
|
|
Result : Display_Controller.Controller_Type;
|
|
begin
|
|
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
|
|
|
|
case Dsp_Config is
|
|
when Primary =>
|
|
Result := Display_Controller.Controllers (Display_Controller.A);
|
|
when Secondary =>
|
|
Result := Display_Controller.Controllers (Display_Controller.B);
|
|
when Tertiary =>
|
|
Result := Display_Controller.Controllers (Display_Controller.C);
|
|
end case;
|
|
return Result;
|
|
end To_Controller;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
function To_Head
|
|
(N_Config : Config_Index;
|
|
Port : Active_Port_Type)
|
|
return Display_Controller.Head_Type
|
|
is
|
|
Result : Display_Controller.Head_Type;
|
|
begin
|
|
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
|
|
|
|
if Config.Has_EDP_Pipe and then Port = Internal then
|
|
Result := Display_Controller.Heads (Display_Controller.Head_EDP);
|
|
else
|
|
case N_Config is
|
|
when Primary =>
|
|
Result := Display_Controller.Heads (Display_Controller.Head_A);
|
|
when Secondary =>
|
|
Result := Display_Controller.Heads (Display_Controller.Head_B);
|
|
when Tertiary =>
|
|
Result := Display_Controller.Heads (Display_Controller.Head_C);
|
|
end case;
|
|
end if;
|
|
return Result;
|
|
end To_Head;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
procedure Legacy_VGA_Off
|
|
is
|
|
Reg8 : Word8;
|
|
begin
|
|
-- disable legacy VGA plane, taking over control now
|
|
Port_IO.OutB (VGA_SR_INDEX, VGA_SR01);
|
|
Port_IO.InB (Reg8, VGA_SR_DATA);
|
|
Port_IO.OutB (VGA_SR_DATA, Reg8 or 1 * 2 ** 5);
|
|
Time.U_Delay (100); -- PRM says 100us, Linux does 300
|
|
Registers.Set_Mask (Registers.VGACNTRL, 1 * 2 ** 31);
|
|
end Legacy_VGA_Off;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
function Port_Configured
|
|
(Configs : Configs_Type;
|
|
Port : Port_Type)
|
|
return Boolean
|
|
with
|
|
Global => null
|
|
is
|
|
begin
|
|
return Configs (Primary).Port = Port or
|
|
Configs (Secondary).Port = Port or
|
|
Configs (Tertiary).Port = Port;
|
|
end Port_Configured;
|
|
|
|
procedure Scan_Ports
|
|
(Configs : out Configs_Type;
|
|
Ports : in Port_List)
|
|
is
|
|
Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
|
|
Port_Idx : Port_List_Range := Port_List_Range'First;
|
|
Port_Cfg : Port_Config;
|
|
Success : Boolean := False;
|
|
begin
|
|
Configs := (Config_Index =>
|
|
(Port => Disabled,
|
|
Mode => Invalid_Mode,
|
|
Framebuffer => Default_FB));
|
|
|
|
for Config_Idx in Config_Index loop
|
|
while Ports (Port_Idx) /= Disabled loop
|
|
if not Port_Configured (Configs, Ports (Port_Idx)) then
|
|
Configs (Config_Idx).Port := Ports (Port_Idx);
|
|
Fill_Port_Config (Port_Cfg, Configs, Config_Idx, Success);
|
|
|
|
if Success then
|
|
-- May need power to probe port
|
|
if Port_Cfg.Display = DP then
|
|
Power_And_Clocks.Power_Up (Cur_Configs, Configs);
|
|
end if;
|
|
if Ports (Port_Idx) = Internal then
|
|
Panel.On;
|
|
end if;
|
|
|
|
Connector_Info.Read_EDID (Raw_EDID, Port_Cfg, Success);
|
|
end if;
|
|
|
|
if Success and then EDID.Has_Preferred_Mode (Raw_EDID) then
|
|
Configs (Config_Idx).Mode := EDID.Preferred_Mode (Raw_EDID);
|
|
else
|
|
Configs (Config_Idx).Port := Disabled;
|
|
|
|
if Ports (Port_Idx) = Internal and
|
|
not Port_Configured (Cur_Configs, Internal)
|
|
then
|
|
Panel.Off;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
exit when Port_Idx = Port_List_Range'Last;
|
|
Port_Idx := Port_List_Range'Succ (Port_Idx);
|
|
|
|
exit when Success;
|
|
end loop;
|
|
end loop;
|
|
|
|
Power_And_Clocks.Power_Set_To (Cur_Configs);
|
|
end Scan_Ports;
|
|
|
|
procedure Auto_Configure
|
|
(Configs : in out Configs_Type;
|
|
Keep_Power : in Boolean := False)
|
|
is
|
|
Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
|
|
Success : Boolean;
|
|
|
|
Config_Idx : Config_Index;
|
|
Port_Cfg : Port_Config;
|
|
|
|
function Free_Config return Boolean
|
|
with
|
|
Pre => True
|
|
is
|
|
begin
|
|
return Port_Configured (Configs, Disabled);
|
|
end Free_Config;
|
|
|
|
function First_Free_Config return Config_Index
|
|
with
|
|
Pre => Free_Config
|
|
is
|
|
begin
|
|
return (if Configs (Primary).Port = Disabled then Primary else
|
|
(if Configs (Secondary).Port = Disabled then Secondary
|
|
else Tertiary));
|
|
end First_Free_Config;
|
|
begin
|
|
-- TODO: Only check ports with hot-plug event?
|
|
|
|
if Config.Has_Internal_Display and then
|
|
not Keep_Power and then
|
|
not Port_Configured (Cur_Configs, Internal)
|
|
then
|
|
Panel.On (Wait => False);
|
|
end if;
|
|
|
|
-- Check if displays are still connected
|
|
for I in Config_Index loop
|
|
if Configs (I).Port /= Disabled then
|
|
Fill_Port_Config (Port_Cfg, Configs, I, Success);
|
|
if Success then
|
|
Connector_Info.Read_EDID
|
|
(Raw_EDID => Raw_EDID,
|
|
Port_Cfg => Port_Cfg,
|
|
Success => Success);
|
|
end if;
|
|
if not Success or else
|
|
not EDID.Has_Preferred_Mode (Raw_EDID) or else
|
|
Configs (I).Mode /= EDID.Preferred_Mode (Raw_EDID)
|
|
then
|
|
Configs (I).Port := Disabled;
|
|
end if;
|
|
end if;
|
|
end loop;
|
|
|
|
-- Add new displays as long as there is a free pipe config
|
|
for Port in Active_Port_Type loop
|
|
if Free_Config and then not Port_Configured (Configs, Port) then
|
|
Config_Idx := First_Free_Config;
|
|
Configs (Config_Idx).Port := Port;
|
|
Fill_Port_Config (Port_Cfg, Configs, Config_Idx, Success);
|
|
|
|
if Success then
|
|
-- Need power to probe port
|
|
if not Keep_Power and then To_Display_Type (Port) = DP then
|
|
Power_And_Clocks.Power_Up (Cur_Configs, Configs);
|
|
end if;
|
|
if not Keep_Power and then Port = Internal then
|
|
Panel.Wait_On;
|
|
end if;
|
|
|
|
Connector_Info.Read_EDID
|
|
(Raw_EDID => Raw_EDID,
|
|
Port_Cfg => Port_Cfg,
|
|
Success => Success);
|
|
end if;
|
|
|
|
if Success and then EDID.Has_Preferred_Mode (Raw_EDID) then
|
|
Configs (Config_Idx) := Config_Type'
|
|
(Port => Port,
|
|
Framebuffer => Configs (Config_Idx).Framebuffer,
|
|
Mode => EDID.Preferred_Mode (Raw_EDID));
|
|
else
|
|
Configs (Config_Idx).Port := Disabled;
|
|
end if;
|
|
end if;
|
|
end loop;
|
|
|
|
if not Keep_Power then
|
|
Power_And_Clocks.Power_Set_To (Cur_Configs);
|
|
|
|
if Config.Has_Internal_Display and then
|
|
not Port_Configured (Cur_Configs, Internal)
|
|
then
|
|
Panel.Off;
|
|
end if;
|
|
end if;
|
|
end Auto_Configure;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
procedure Update_Outputs (Configs : Configs_Type)
|
|
is
|
|
Did_Power_Up : Boolean := False;
|
|
|
|
HPD, HPD_Delay_Over, Success : Boolean;
|
|
Old_Config, New_Config : Config_Type;
|
|
Old_Configs : Configs_Type;
|
|
Port_Cfg : Port_Config;
|
|
|
|
procedure Check_HPD
|
|
(Port_Cfg : in Port_Config;
|
|
Port : in Port_Type;
|
|
Detected : out Boolean)
|
|
is
|
|
begin
|
|
HPD_Delay_Over := Time.Timed_Out (HPD_Delay (Port));
|
|
if HPD_Delay_Over then
|
|
Port_Detect.Hotplug_Detect (Port_Cfg, Detected);
|
|
HPD_Delay (Port) := Time.MS_From_Now (333);
|
|
else
|
|
Detected := False;
|
|
end if;
|
|
end Check_HPD;
|
|
begin
|
|
Old_Configs := Cur_Configs;
|
|
|
|
for I in Config_Index loop
|
|
HPD := False;
|
|
|
|
Old_Config := Cur_Configs (I);
|
|
New_Config := Configs (I);
|
|
|
|
Fill_Port_Config (Port_Cfg, Old_Configs, I, Success);
|
|
if Success then
|
|
Check_HPD (Port_Cfg, Old_Config.Port, HPD);
|
|
end if;
|
|
|
|
-- Connector changed?
|
|
if (Success and then HPD) or
|
|
Old_Config.Port /= New_Config.Port or
|
|
Old_Config.Mode /= New_Config.Mode
|
|
then
|
|
if Old_Config.Port /= Disabled then
|
|
if Success then
|
|
pragma Debug (Debug.New_Line);
|
|
pragma Debug (Debug.Put_Line
|
|
("Disabling port " & Port_Names (Old_Config.Port)));
|
|
|
|
Connectors.Pre_Off (Port_Cfg);
|
|
|
|
Display_Controller.Off
|
|
(To_Controller (I), To_Head (I, Old_Config.Port));
|
|
|
|
Connectors.Post_Off (Port_Cfg);
|
|
end if;
|
|
|
|
-- Free PLL
|
|
PLLs.Free (Allocated_PLLs (I));
|
|
|
|
Cur_Configs (I).Port := Disabled;
|
|
end if;
|
|
|
|
if New_Config.Port /= Disabled then
|
|
Fill_Port_Config (Port_Cfg, Configs, I, Success);
|
|
|
|
if Success and then Wait_For_HPD (New_Config.Port) then
|
|
Check_HPD (Port_Cfg, New_Config.Port, Success);
|
|
Wait_For_HPD (New_Config.Port) := not Success;
|
|
end if;
|
|
|
|
if Success then
|
|
pragma Debug (Debug.New_Line);
|
|
pragma Debug (Debug.Put_Line
|
|
("Trying to enable port " & Port_Names (New_Config.Port)));
|
|
|
|
if not Did_Power_Up then
|
|
Power_And_Clocks.Power_Up (Old_Configs, Configs);
|
|
Did_Power_Up := True;
|
|
end if;
|
|
|
|
if Port_Cfg.Is_FDI then
|
|
Configure_FDI_Link (Port_Cfg, Success);
|
|
end if;
|
|
end if;
|
|
|
|
if Success then
|
|
Connector_Info.Preferred_Link_Setting
|
|
(Port_Cfg => Port_Cfg,
|
|
Success => Success);
|
|
end if;
|
|
|
|
while Success loop
|
|
pragma Loop_Invariant (New_Config.Port in Active_Port_Type);
|
|
|
|
PLLs.Alloc
|
|
(Port_Cfg => Port_Cfg,
|
|
PLL => Allocated_PLLs (I),
|
|
Success => Success);
|
|
|
|
if Success then
|
|
for Try in 1 .. 2 loop
|
|
pragma Loop_Invariant
|
|
(New_Config.Port in Active_Port_Type);
|
|
|
|
Connectors.Pre_On
|
|
(Port_Cfg => Port_Cfg,
|
|
PLL_Hint => PLLs.Register_Value
|
|
(Allocated_PLLs (I)),
|
|
Pipe_Hint => Display_Controller.Get_Pipe_Hint
|
|
(To_Head (I, New_Config.Port)),
|
|
Success => Success);
|
|
|
|
if Success then
|
|
Display_Controller.On
|
|
(Controller => To_Controller (I),
|
|
Head => To_Head (I, New_Config.Port),
|
|
Port_Cfg => Port_Cfg,
|
|
Framebuffer => New_Config.Framebuffer);
|
|
|
|
Connectors.Post_On
|
|
(Port_Cfg => Port_Cfg,
|
|
PLL_Hint => PLLs.Register_Value
|
|
(Allocated_PLLs (I)),
|
|
Success => Success);
|
|
|
|
if not Success then
|
|
Display_Controller.Off
|
|
(To_Controller (I),
|
|
To_Head (I, New_Config.Port));
|
|
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 (I));
|
|
end if;
|
|
|
|
Connector_Info.Next_Link_Setting
|
|
(Port_Cfg => Port_Cfg,
|
|
Success => Success);
|
|
end loop;
|
|
|
|
if Success then
|
|
pragma Debug (Debug.Put_Line
|
|
("Enabled port " & Port_Names (New_Config.Port)));
|
|
Cur_Configs (I) := New_Config;
|
|
DP_Links (I) := Port_Cfg.DP;
|
|
else
|
|
Wait_For_HPD (New_Config.Port) := True;
|
|
if New_Config.Port = Internal then
|
|
Panel.Off;
|
|
end if;
|
|
end if;
|
|
else
|
|
Cur_Configs (I) := New_Config;
|
|
end if;
|
|
elsif Old_Config.Framebuffer /= New_Config.Framebuffer and
|
|
Old_Config.Port /= Disabled
|
|
then
|
|
Display_Controller.Update_Offset
|
|
(Controller => To_Controller (I),
|
|
Framebuffer => New_Config.Framebuffer);
|
|
Cur_Configs (I) := New_Config;
|
|
end if;
|
|
end loop;
|
|
|
|
if Did_Power_Up then
|
|
Power_And_Clocks.Power_Down (Old_Configs, Configs, Cur_Configs);
|
|
end if;
|
|
|
|
end Update_Outputs;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
procedure Initialize
|
|
(MMIO_Base : in Word64 := 0;
|
|
Write_Delay : in Word64 := 0;
|
|
Success : out Boolean)
|
|
with
|
|
Refined_Global =>
|
|
(In_Out =>
|
|
(Config.Valid_Port_GPU,
|
|
Registers.Register_State, Port_IO.State),
|
|
Input =>
|
|
(Time.State),
|
|
Output =>
|
|
(Registers.Address_State,
|
|
PLLs.State, Panel.Panel_State,
|
|
Cur_Configs, Allocated_PLLs, DP_Links,
|
|
HPD_Delay, Wait_For_HPD, Initialized))
|
|
is
|
|
use type HW.Word64;
|
|
|
|
Now : constant Time.T := Time.Now;
|
|
|
|
procedure Check_Platform (Success : out Boolean)
|
|
is
|
|
Audio_VID_DID : Word32;
|
|
begin
|
|
case Config.CPU is
|
|
when Haswell .. Skylake =>
|
|
Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
|
|
when Ironlake .. Ivybridge =>
|
|
Registers.Read (Registers.PCH_AUD_VID_DID, Audio_VID_DID);
|
|
end case;
|
|
Success :=
|
|
(case Config.CPU is
|
|
when Skylake => Audio_VID_DID = 16#8086_2809#,
|
|
when Broadwell => Audio_VID_DID = 16#8086_2808#,
|
|
when Haswell => Audio_VID_DID = 16#8086_2807#,
|
|
when Ivybridge |
|
|
Sandybridge => Audio_VID_DID = 16#8086_2806# or
|
|
Audio_VID_DID = 16#8086_2805#,
|
|
when Ironlake => Audio_VID_DID = 16#8086_2804#); -- not sure
|
|
end Check_Platform;
|
|
begin
|
|
pragma Warnings (GNATprove, Off, "unused variable ""Write_Delay""",
|
|
Reason => "Write_Delay is used for debugging only");
|
|
|
|
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
|
|
|
|
pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
|
|
|
|
Wait_For_HPD := HPD_Type'(others => False);
|
|
HPD_Delay := HPD_Delay_Type'(others => Now);
|
|
DP_Links := Links_Type'(others => HW.GFX.Default_DP);
|
|
Allocated_PLLs := (others => PLLs.Invalid);
|
|
Cur_Configs := Configs_Type'
|
|
(others => Config_Type'
|
|
(Port => Disabled,
|
|
Framebuffer => HW.GFX.Default_FB,
|
|
Mode => HW.GFX.Invalid_Mode));
|
|
Registers.Set_Register_Base
|
|
(if MMIO_Base /= 0 then
|
|
MMIO_Base
|
|
else
|
|
Config.Default_MMIO_Base);
|
|
PLLs.Initialize;
|
|
|
|
Check_Platform (Success);
|
|
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;
|
|
|
|
Power_And_Clocks.Pre_All_Off;
|
|
|
|
Legacy_VGA_Off;
|
|
|
|
Connectors.Pre_All_Off;
|
|
Display_Controller.All_Off;
|
|
Connectors.Post_All_Off;
|
|
PLLs.All_Off;
|
|
|
|
Power_And_Clocks.Post_All_Off;
|
|
|
|
-------------------- Now restart from a clean state ---------------------
|
|
Power_And_Clocks.Initialize;
|
|
|
|
Initialized := True;
|
|
|
|
end Initialize;
|
|
|
|
function Is_Initialized return Boolean
|
|
with
|
|
Refined_Post => Is_Initialized'Result = Initialized
|
|
is
|
|
begin
|
|
return Initialized;
|
|
end Is_Initialized;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
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 Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32)
|
|
is
|
|
FB_Size : constant Pos32 :=
|
|
FB.Stride * FB.Height * Pos32 (((FB.BPC * 4) / 8));
|
|
Phys_Addr : GTT_Address_Type := GTT_Address_Type (Phys_FB);
|
|
begin
|
|
for Idx in GTT_Range range 0 .. GTT_Range (((FB_Size + 4095) / 4096) - 1)
|
|
loop
|
|
Registers.Write_GTT
|
|
(GTT_Page => Idx,
|
|
Device_Address => Phys_Addr,
|
|
Valid => True);
|
|
Phys_Addr := Phys_Addr + 4096;
|
|
end loop;
|
|
end Setup_Default_GTT;
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
procedure Dump_Configs (Configs : Configs_Type)
|
|
is
|
|
subtype Pipe_Name is String (1 .. 9);
|
|
type Pipe_Name_Array is array (Config_Index) of Pipe_Name;
|
|
Pipe_Names : constant Pipe_Name_Array :=
|
|
(Primary => "Primary ",
|
|
Secondary => "Secondary",
|
|
Tertiary => "Tertiary ");
|
|
begin
|
|
Debug.New_Line;
|
|
Debug.Put_Line ("CONFIG => ");
|
|
for Pipe in Config_Index loop
|
|
if Pipe = Config_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 (" Stride => ");
|
|
Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
|
|
Debug.Put_Line (",");
|
|
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_Int16 (Configs (Pipe).Mode.H_Visible);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" H_Sync_Begin => ");
|
|
Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_Begin);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" H_Sync_End => ");
|
|
Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_End);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" H_Total => ");
|
|
Debug.Put_Int16 (Configs (Pipe).Mode.H_Total);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" V_Visible => ");
|
|
Debug.Put_Int16 (Configs (Pipe).Mode.V_Visible);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" V_Sync_Begin => ");
|
|
Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_Begin);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" V_Sync_End => ");
|
|
Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_End);
|
|
Debug.Put_Line (",");
|
|
Debug.Put (" V_Total => ");
|
|
Debug.Put_Int16 (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 /= Config_Index'Last then
|
|
Debug.Put_Line (")),");
|
|
else
|
|
Debug.Put_Line (")));");
|
|
end if;
|
|
end loop;
|
|
end Dump_Configs;
|
|
|
|
end HW.GFX.GMA;
|