gma: Split out config derivation and port probing
The GMA package has grown way too big. Move derivation of the internal
configuration into new package `Config_Helpers`, EDID probing into new
package `Display_Probing`.
Change-Id: Ib49ac7b00367be4295d18dba3afd1a0692e0497f
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/17757
Reviewed-by: Adrian-Ken Rueegsegger <ken@codelabs.ch>
diff --git a/common/Makefile.inc b/common/Makefile.inc
index 9745eb6..11215ee 100644
--- a/common/Makefile.inc
+++ b/common/Makefile.inc
@@ -7,9 +7,13 @@
gfxinit-y += hw-gfx-dp_training.ads
gfxinit-y += hw-gfx-edid.adb
gfxinit-y += hw-gfx-edid.ads
+gfxinit-y += hw-gfx-gma-config_helpers.adb
+gfxinit-y += hw-gfx-gma-config_helpers.ads
gfxinit-y += hw-gfx-gma-connector_info.adb
gfxinit-y += hw-gfx-gma-connector_info.ads
gfxinit-y += hw-gfx-gma-connectors.ads
+gfxinit-y += hw-gfx-gma-display_probing.adb
+gfxinit-y += hw-gfx-gma-display_probing.ads
gfxinit-y += hw-gfx-gma-dp_aux_ch.ads
gfxinit-y += hw-gfx-gma-dp_aux_request.adb
gfxinit-y += hw-gfx-gma-dp_aux_request.ads
diff --git a/common/hw-gfx-gma-config_helpers.adb b/common/hw-gfx-gma-config_helpers.adb
new file mode 100644
index 0000000..31406cf
--- /dev/null
+++ b/common/hw-gfx-gma-config_helpers.adb
@@ -0,0 +1,210 @@
+--
+-- Copyright (C) 2015-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; 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.GFX.GMA.Config;
+with HW.GFX.GMA.Connector_Info;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+
+package body HW.GFX.GMA.Config_Helpers
+is
+
+ function To_GPU_Port
+ (Pipe : Pipe_Index;
+ Port : Active_Port_Type)
+ 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 Port = Internal then
+ DIGI_A
+ else
+ (case Pipe 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 Port is
+ when Internal => DIGI_A, -- LVDS not available
+ when HDMI1 | DP1 => DIGI_B,
+ when HDMI2 | DP2 => DIGI_C,
+ when HDMI3 | 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 HDMI1 => PCH_HDMI_B,
+ when HDMI2 => PCH_HDMI_C,
+ when HDMI3 => 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
+ is
+ begin
+ return Display_Type'
+ (case Port is
+ when Internal => Config.Internal_Display,
+ when Analog => VGA,
+ when HDMI1 .. HDMI3 => HDMI,
+ when DP1 .. DP3 => DP);
+ end To_Display_Type;
+
+ ----------------------------------------------------------------------------
+
+ -- Prepares link rate and lane count settings for an FDI connection.
+ 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;
+
+ -- Derives an internal port config.
+ --
+ -- This is where the magic happens that hides the hardware details
+ -- from libgfxinit's users. We have to map the pipe (Pipe_Index),
+ -- the user visible port (Port_Type) and the modeline (Mode_Type)
+ -- that we are supposed to output to an internal representation
+ -- (Port_Config) that applies to the selected hardware generation
+ -- (in GMA.Config).
+ procedure Fill_Port_Config
+ (Port_Cfg : out Port_Config;
+ Pipe : in Pipe_Index;
+ Port : in Port_Type;
+ Mode : in Mode_Type;
+ Success : out Boolean)
+ is
+ begin
+ Success :=
+ Config.Supported_Pipe (Pipe) and then
+ Config.Valid_Port (Port) and then
+ Port /= Disabled; -- Valid_Port should already cover this, but the
+ -- array is writeable, so it's hard to prove this.
+
+ if Success then
+ Port_Cfg := Port_Config'
+ (Port => To_GPU_Port (Pipe, Port),
+ PCH_Port => To_PCH_Port (Port),
+ Display => To_Display_Type (Port),
+ Mode => Mode,
+ Is_FDI => Config.Is_FDI_Port (Port),
+ FDI => Default_DP,
+ DP => Default_DP);
+
+ if Port_Cfg.Is_FDI then
+ Configure_FDI_Link (Port_Cfg, Success);
+ end if;
+
+ if Success then
+ if Port_Cfg.Mode.BPC = Auto_BPC then
+ Port_Cfg.Mode.BPC := Connector_Info.Default_BPC (Port_Cfg);
+ end if;
+
+ if Port_Cfg.Display = HDMI then
+ declare
+ pragma Assert (Config.HDMI_Max_Clock_24bpp * 8
+ / Port_Cfg.Mode.BPC >= Frequency_Type'First);
+ Max_Dotclock : constant Frequency_Type :=
+ Config.HDMI_Max_Clock_24bpp * 8 / Port_Cfg.Mode.BPC;
+ begin
+ if Port_Cfg.Mode.Dotclock > Max_Dotclock then
+ pragma Debug (Debug.Put ("Dotclock "));
+ pragma Debug (Debug.Put_Int64 (Port_Cfg.Mode.Dotclock));
+ pragma Debug (Debug.Put (" too high, limiting to "));
+ pragma Debug (Debug.Put_Int64 (Max_Dotclock));
+ pragma Debug (Debug.Put_Line ("."));
+ Port_Cfg.Mode.Dotclock := Max_Dotclock;
+ end if;
+ end;
+ end if;
+ end if;
+ 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;
+
+ ----------------------------------------------------------------------------
+
+ -- Validates that a given configuration should work with
+ -- a given framebuffer.
+ function Validate_Config
+ (Framebuffer : Framebuffer_Type;
+ Port_Cfg : Port_Config;
+ Pipe : Pipe_Index)
+ return Boolean
+ is
+ begin
+ -- No downscaling
+ -- Respect maximum scalable width
+ -- VGA plane is only allowed on the primary pipe
+ -- Only 32bpp RGB (ignored for VGA plane)
+ -- Stride must be a multiple of 64 (ignored for VGA plane)
+ return
+ ((Framebuffer.Width = Pos32 (Port_Cfg.Mode.H_Visible) and
+ Framebuffer.Height = Pos32 (Port_Cfg.Mode.V_Visible)) or
+ (Framebuffer.Width <= Config.Maximum_Scalable_Width (Pipe) and
+ Framebuffer.Width <= Pos32 (Port_Cfg.Mode.H_Visible) and
+ Framebuffer.Height <= Pos32 (Port_Cfg.Mode.V_Visible))) and
+ (Framebuffer.Offset /= VGA_PLANE_FRAMEBUFFER_OFFSET or Pipe = Primary)
+ and
+ (Framebuffer.Offset = VGA_PLANE_FRAMEBUFFER_OFFSET or
+ (Framebuffer.BPC = 8 and
+ Framebuffer.Stride mod 64 = 0));
+ end Validate_Config;
+
+end HW.GFX.GMA.Config_Helpers;
diff --git a/common/hw-gfx-gma-config_helpers.ads b/common/hw-gfx-gma-config_helpers.ads
new file mode 100644
index 0000000..b56e1b1
--- /dev/null
+++ b/common/hw-gfx-gma-config_helpers.ads
@@ -0,0 +1,45 @@
+--
+-- Copyright (C) 2015-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; 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;
+
+private package HW.GFX.GMA.Config_Helpers
+is
+
+ function To_PCH_Port (Port : Active_Port_Type) return PCH_Port;
+
+ function To_Display_Type (Port : Active_Port_Type) return Display_Type;
+
+ procedure Fill_Port_Config
+ (Port_Cfg : out Port_Config;
+ Pipe : in Pipe_Index;
+ Port : in Port_Type;
+ Mode : in Mode_Type;
+ Success : out Boolean);
+
+ ----------------------------------------------------------------------------
+
+ use type HW.Pos32;
+ function Validate_Config
+ (Framebuffer : Framebuffer_Type;
+ Port_Cfg : Port_Config;
+ Pipe : Pipe_Index)
+ return Boolean
+ with
+ Post =>
+ (if Validate_Config'Result then
+ Framebuffer.Width <= Pos32 (Port_Cfg.Mode.H_Visible) and
+ Framebuffer.Height <= Pos32 (Port_Cfg.Mode.V_Visible));
+
+end HW.GFX.GMA.Config_Helpers;
diff --git a/common/hw-gfx-gma-display_probing.adb b/common/hw-gfx-gma-display_probing.adb
new file mode 100644
index 0000000..7320004
--- /dev/null
+++ b/common/hw-gfx-gma-display_probing.adb
@@ -0,0 +1,208 @@
+--
+-- Copyright (C) 2015-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; 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.GFX.I2C;
+with HW.GFX.EDID;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Config_Helpers;
+with HW.GFX.GMA.I2C;
+with HW.GFX.GMA.DP_Aux_Ch;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.Power_And_Clocks;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Display_Probing
+is
+
+ function Port_Configured
+ (Configs : Pipe_Configs;
+ 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;
+
+ -- DP and HDMI share physical pins.
+ function Sibling_Port (Port : Port_Type) return Port_Type
+ is
+ begin
+ return
+ (case Port is
+ when HDMI1 => DP1,
+ when HDMI2 => DP2,
+ when HDMI3 => DP3,
+ when DP1 => HDMI1,
+ when DP2 => HDMI2,
+ when DP3 => HDMI3,
+ when others => Disabled);
+ end Sibling_Port;
+
+ function Has_Sibling_Port (Port : Port_Type) return Boolean
+ is
+ begin
+ return Sibling_Port (Port) /= Disabled;
+ end Has_Sibling_Port;
+
+ procedure Read_EDID
+ (Raw_EDID : out EDID.Raw_EDID_Data;
+ Port : in Active_Port_Type;
+ Success : out Boolean)
+ with
+ Post => (if Success then EDID.Valid (Raw_EDID))
+ is
+ Raw_EDID_Length : GFX.I2C.Transfer_Length := Raw_EDID'Length;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ for I in 1 .. 2 loop
+ if Config_Helpers.To_Display_Type (Port) = DP then
+ -- May need power to read edid
+ declare
+ Temp_Configs : Pipe_Configs := Cur_Configs;
+ begin
+ Temp_Configs (Primary).Port := Port;
+ Power_And_Clocks.Power_Up (Cur_Configs, Temp_Configs);
+ end;
+
+ declare
+ DP_Port : constant GMA.DP_Port :=
+ (case Port is
+ when Internal => DP_A,
+ when DP1 => DP_B,
+ when DP2 => DP_C,
+ when DP3 => DP_D,
+ when others => GMA.DP_Port'First);
+ begin
+ DP_Aux_Ch.I2C_Read
+ (Port => DP_Port,
+ Address => 16#50#,
+ Length => Raw_EDID_Length,
+ Data => Raw_EDID,
+ Success => Success);
+ end;
+ else
+ I2C.I2C_Read
+ (Port => (if Port = Analog
+ then Config.Analog_I2C_Port
+ else Config_Helpers.To_PCH_Port (Port)),
+ Address => 16#50#,
+ Length => Raw_EDID_Length,
+ Data => Raw_EDID,
+ Success => Success);
+ end if;
+ exit when not Success; -- don't retry if reading itself failed
+
+ pragma Debug (Debug.Put_Buffer ("EDID", Raw_EDID, Raw_EDID_Length));
+ EDID.Sanitize (Raw_EDID, Success);
+ exit when Success;
+ end loop;
+ end Read_EDID;
+
+ procedure Probe_Port
+ (Pipe_Cfg : in out Pipe_Config;
+ Port : in Active_Port_Type;
+ Success : out Boolean)
+ with Pre => True
+ is
+ Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
+ begin
+ Success := Config.Valid_Port (Port);
+
+ if Success then
+ if Port = Internal then
+ Panel.On;
+ end if;
+ Read_EDID (Raw_EDID, Port, Success);
+ end if;
+
+ if Success and then
+ (EDID.Compatible_Display
+ (Raw_EDID, Config_Helpers.To_Display_Type (Port)) and
+ EDID.Has_Preferred_Mode (Raw_EDID))
+ then
+ Pipe_Cfg.Port := Port;
+ Pipe_Cfg.Mode := EDID.Preferred_Mode (Raw_EDID);
+
+ pragma Warnings (GNATprove, Off, "unused assignment to ""Raw_EDID""",
+ Reason => "We just want to check if it's readable.");
+ if Has_Sibling_Port (Port) then
+ -- Probe sibling port too and bail out if something is detected.
+ -- This is a precaution for adapters that expose the pins of a
+ -- port for both HDMI/DVI and DP (like some ThinkPad docks). A
+ -- user might have attached both by accident and there are ru-
+ -- mors of displays that got fried by applying the wrong signal.
+ declare
+ Have_Sibling_EDID : Boolean;
+ begin
+ Read_EDID (Raw_EDID, Sibling_Port (Port), Have_Sibling_EDID);
+ if Have_Sibling_EDID then
+ Pipe_Cfg.Port := Disabled;
+ Success := False;
+ end if;
+ end;
+ end if;
+ pragma Warnings (GNATprove, On, "unused assignment to ""Raw_EDID""");
+ else
+ Success := False;
+ if Port = Internal then
+ Panel.Off;
+ end if;
+ end if;
+ end Probe_Port;
+
+ procedure Scan_Ports
+ (Configs : out Pipe_Configs;
+ Ports : in Port_List;
+ Max_Pipe : in Pipe_Index := Pipe_Index'Last)
+ is
+ Port_Idx : Port_List_Range := Port_List_Range'First;
+ Success : Boolean;
+ begin
+ Configs := (Pipe_Index =>
+ (Port => Disabled,
+ Mode => Invalid_Mode,
+ Framebuffer => Default_FB));
+
+ for Pipe in Pipe_Index range
+ Pipe_Index'First .. Pipe_Index'Min (Max_Pipe, Config.Max_Pipe)
+ loop
+ while Ports (Port_Idx) /= Disabled loop
+ if not Port_Configured (Configs, Ports (Port_Idx)) and
+ (not Has_Sibling_Port (Ports (Port_Idx)) or
+ not Port_Configured (Configs, Sibling_Port (Ports (Port_Idx))))
+ then
+ Probe_Port (Configs (Pipe), Ports (Port_Idx), Success);
+ else
+ Success := False;
+ 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;
+
+ -- Restore power settings
+ Power_And_Clocks.Power_Set_To (Cur_Configs);
+ end Scan_Ports;
+
+end HW.GFX.GMA.Display_Probing;
diff --git a/common/hw-gfx-gma-display_probing.ads b/common/hw-gfx-gma-display_probing.ads
new file mode 100644
index 0000000..3d1e914
--- /dev/null
+++ b/common/hw-gfx-gma-display_probing.ads
@@ -0,0 +1,26 @@
+--
+-- Copyright (C) 2015-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; 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.
+--
+
+package HW.GFX.GMA.Display_Probing
+is
+
+ type Port_List_Range is range 0 .. 7;
+ type Port_List is array (Port_List_Range) of Port_Type;
+
+ procedure Scan_Ports
+ (Configs : out Pipe_Configs;
+ Ports : in Port_List;
+ Max_Pipe : in Pipe_Index := Pipe_Index'Last);
+
+end HW.GFX.GMA.Display_Probing;
diff --git a/common/hw-gfx-gma.adb b/common/hw-gfx-gma.adb
index 4b09d51..0a5b1b5 100644
--- a/common/hw-gfx-gma.adb
+++ b/common/hw-gfx-gma.adb
@@ -12,12 +12,8 @@
-- GNU General Public License for more details.
--
-with HW.GFX.I2C;
-with HW.GFX.EDID;
with HW.GFX.GMA.Config;
-with HW.GFX.GMA.I2C;
-with HW.GFX.GMA.DP_Aux_Ch;
-with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.Config_Helpers;
with HW.GFX.GMA.Registers;
with HW.GFX.GMA.Power_And_Clocks;
with HW.GFX.GMA.Panel;
@@ -70,15 +66,12 @@
type HPD_Type is array (Port_Type) of Boolean;
type HPD_Delay_Type is array (Port_Type) of Time.T;
- Cur_Configs : Pipe_Configs;
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;
-
----------------------------------------------------------------------------
PCH_RAWCLK_FREQ_MASK : constant := 16#3ff# * 2 ** 0;
@@ -91,200 +84,6 @@
----------------------------------------------------------------------------
- function To_GPU_Port
- (Pipe : Pipe_Index;
- Port : Active_Port_Type)
- 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 Port = Internal then
- DIGI_A
- else
- (case Pipe 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 Port is
- when Internal => DIGI_A, -- LVDS not available
- when HDMI1 | DP1 => DIGI_B,
- when HDMI2 | DP2 => DIGI_C,
- when HDMI3 | DP3 => DIGI_D,
- when Analog => DIGI_E));
- end To_GPU_Port;
-
- function To_PCH_Port (Port : Active_Port_Type) return PCH_Port
- with Pre => True
- is
- begin
- return
- (case Port is
- when Internal => PCH_LVDS, -- will be ignored if Internal is DP
- when Analog => PCH_DAC,
- when HDMI1 => PCH_HDMI_B,
- when HDMI2 => PCH_HDMI_C,
- when HDMI3 => 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 Display_Type'
- (case Port is
- when Internal => Config.Internal_Display,
- when Analog => VGA,
- when HDMI1 .. HDMI3 => HDMI,
- when DP1 .. DP3 => DP);
- end To_Display_Type;
-
- -- Prepares link rate and lane count settings for an FDI connection.
- 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;
-
- -- Validates that a given configuration should work with
- -- a given framebuffer.
- function Validate_Config
- (Framebuffer : Framebuffer_Type;
- Port_Cfg : Port_Config;
- I : Pipe_Index)
- return Boolean
- with
- Post =>
- (if Validate_Config'Result then
- Framebuffer.Width <= Pos32 (Port_Cfg.Mode.H_Visible) and
- Framebuffer.Height <= Pos32 (Port_Cfg.Mode.V_Visible))
- is
- begin
- -- No downscaling
- -- Respect maximum scalable width
- -- VGA plane is only allowed on the primary pipe
- -- Only 32bpp RGB (ignored for VGA plane)
- -- Stride must be a multiple of 64 (ignored for VGA plane)
- return
- ((Framebuffer.Width = Pos32 (Port_Cfg.Mode.H_Visible) and
- Framebuffer.Height = Pos32 (Port_Cfg.Mode.V_Visible)) or
- (Framebuffer.Width <= Config.Maximum_Scalable_Width (I) and
- Framebuffer.Width <= Pos32 (Port_Cfg.Mode.H_Visible) and
- Framebuffer.Height <= Pos32 (Port_Cfg.Mode.V_Visible))) and
- (Framebuffer.Offset /= VGA_PLANE_FRAMEBUFFER_OFFSET or I = Primary) and
- (Framebuffer.Offset = VGA_PLANE_FRAMEBUFFER_OFFSET or
- (Framebuffer.BPC = 8 and
- Framebuffer.Stride mod 64 = 0));
- end Validate_Config;
-
- -- Derives an internal port config.
- --
- -- This is where the magic happens that hides the hardware details
- -- from libgfxinit's users. We have to map the pipe (Pipe_Index),
- -- the user visible port (Port_Type) and the modeline (Mode_Type)
- -- that we are supposed to output to an internal representation
- -- (Port_Config) that applies to the selected hardware generation
- -- (in GMA.Config).
- procedure Fill_Port_Config
- (Port_Cfg : out Port_Config;
- Pipe : in Pipe_Index;
- Port : in Port_Type;
- Mode : in Mode_Type;
- Success : out Boolean)
- with Pre => True
- is
- begin
- Success :=
- GMA.Config.Supported_Pipe (Pipe) and then
- GMA.Config.Valid_Port (Port) and then
- Port /= Disabled; -- Valid_Port should already cover this, but the
- -- array is writeable, so it's hard to prove this.
-
- if Success then
- declare
- Link : constant DP_Link := DP_Links (Pipe);
- begin
- Port_Cfg := Port_Config'
- (Port => To_GPU_Port (Pipe, Port),
- PCH_Port => To_PCH_Port (Port),
- Display => To_Display_Type (Port),
- Mode => Mode,
- Is_FDI => GMA.Config.Is_FDI_Port (Port),
- FDI => Default_DP,
- DP => Link);
- end;
-
- if Port_Cfg.Is_FDI then
- Configure_FDI_Link (Port_Cfg, Success);
- end if;
-
- if Success then
- if Port_Cfg.Mode.BPC = Auto_BPC then
- Port_Cfg.Mode.BPC := Connector_Info.Default_BPC (Port_Cfg);
- end if;
-
- if Port_Cfg.Display = HDMI then
- declare
- pragma Assert (Config.HDMI_Max_Clock_24bpp * 8
- / Port_Cfg.Mode.BPC >= Frequency_Type'First);
- Max_Dotclock : constant Frequency_Type :=
- Config.HDMI_Max_Clock_24bpp * 8 / Port_Cfg.Mode.BPC;
- begin
- if Port_Cfg.Mode.Dotclock > Max_Dotclock then
- pragma Debug (Debug.Put ("Dotclock "));
- pragma Debug (Debug.Put_Int64 (Port_Cfg.Mode.Dotclock));
- pragma Debug (Debug.Put (" too high, limiting to "));
- pragma Debug (Debug.Put_Int64 (Max_Dotclock));
- pragma Debug (Debug.Put_Line ("."));
- Port_Cfg.Mode.Dotclock := Max_Dotclock;
- end if;
- end;
- end if;
- end if;
- 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 : Pipe_Index) return Display_Controller.Controller_Type
is
@@ -345,185 +144,6 @@
----------------------------------------------------------------------------
- function Port_Configured
- (Configs : Pipe_Configs;
- 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;
-
- -- DP and HDMI share physical pins.
- function Sibling_Port (Port : Port_Type) return Port_Type
- is
- begin
- return
- (case Port is
- when HDMI1 => DP1,
- when HDMI2 => DP2,
- when HDMI3 => DP3,
- when DP1 => HDMI1,
- when DP2 => HDMI2,
- when DP3 => HDMI3,
- when others => Disabled);
- end Sibling_Port;
-
- function Has_Sibling_Port (Port : Port_Type) return Boolean
- is
- begin
- return Sibling_Port (Port) /= Disabled;
- end Has_Sibling_Port;
-
- procedure Read_EDID
- (Raw_EDID : out EDID.Raw_EDID_Data;
- Port : in Active_Port_Type;
- Success : out Boolean)
- with
- Post => (if Success then EDID.Valid (Raw_EDID))
- is
- Raw_EDID_Length : GFX.I2C.Transfer_Length := Raw_EDID'Length;
- begin
- pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
-
- for I in 1 .. 2 loop
- if To_Display_Type (Port) = DP then
- -- May need power to read edid
- declare
- Temp_Configs : Pipe_Configs := Cur_Configs;
- begin
- Temp_Configs (Primary).Port := Port;
- Power_And_Clocks.Power_Up (Cur_Configs, Temp_Configs);
- end;
-
- declare
- DP_Port : constant GMA.DP_Port :=
- (case Port is
- when Internal => DP_A,
- when DP1 => DP_B,
- when DP2 => DP_C,
- when DP3 => DP_D,
- when others => GMA.DP_Port'First);
- begin
- DP_Aux_Ch.I2C_Read
- (Port => DP_Port,
- Address => 16#50#,
- Length => Raw_EDID_Length,
- Data => Raw_EDID,
- Success => Success);
- end;
- else
- I2C.I2C_Read
- (Port => (if Port = Analog
- then Config.Analog_I2C_Port
- else To_PCH_Port (Port)),
- Address => 16#50#,
- Length => Raw_EDID_Length,
- Data => Raw_EDID,
- Success => Success);
- end if;
- exit when not Success; -- don't retry if reading itself failed
-
- pragma Debug (Debug.Put_Buffer ("EDID", Raw_EDID, Raw_EDID_Length));
- EDID.Sanitize (Raw_EDID, Success);
- exit when Success;
- end loop;
- end Read_EDID;
-
- procedure Probe_Port
- (Pipe_Cfg : in out Pipe_Config;
- Port : in Active_Port_Type;
- Success : out Boolean)
- with Pre => True
- is
- Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
- begin
- Success := Config.Valid_Port (Port);
-
- if Success then
- if Port = Internal then
- Panel.On;
- end if;
- Read_EDID (Raw_EDID, Port, Success);
- end if;
-
- if Success and then
- (EDID.Compatible_Display (Raw_EDID, To_Display_Type (Port)) and
- EDID.Has_Preferred_Mode (Raw_EDID))
- then
- Pipe_Cfg.Port := Port;
- Pipe_Cfg.Mode := EDID.Preferred_Mode (Raw_EDID);
-
- pragma Warnings (GNATprove, Off, "unused assignment to ""Raw_EDID""",
- Reason => "We just want to check if it's readable.");
- if Has_Sibling_Port (Port) then
- -- Probe sibling port too and bail out if something is detected.
- -- This is a precaution for adapters that expose the pins of a
- -- port for both HDMI/DVI and DP (like some ThinkPad docks). A
- -- user might have attached both by accident and there are ru-
- -- mors of displays that got fried by applying the wrong signal.
- declare
- Have_Sibling_EDID : Boolean;
- begin
- Read_EDID (Raw_EDID, Sibling_Port (Port), Have_Sibling_EDID);
- if Have_Sibling_EDID then
- Pipe_Cfg.Port := Disabled;
- Success := False;
- end if;
- end;
- end if;
- pragma Warnings (GNATprove, On, "unused assignment to ""Raw_EDID""");
- else
- Success := False;
- if Port = Internal then
- Panel.Off;
- end if;
- end if;
- end Probe_Port;
-
- procedure Scan_Ports
- (Configs : out Pipe_Configs;
- Ports : in Port_List;
- Max_Pipe : in Pipe_Index := Pipe_Index'Last)
- is
- Port_Idx : Port_List_Range := Port_List_Range'First;
- Success : Boolean;
- begin
- Configs := (Pipe_Index =>
- (Port => Disabled,
- Mode => Invalid_Mode,
- Framebuffer => Default_FB));
-
- for Pipe in Pipe_Index range
- Pipe_Index'First .. Pipe_Index'Min (Max_Pipe, Config.Max_Pipe)
- loop
- while Ports (Port_Idx) /= Disabled loop
- if not Port_Configured (Configs, Ports (Port_Idx)) and
- (not Has_Sibling_Port (Ports (Port_Idx)) or
- not Port_Configured (Configs, Sibling_Port (Ports (Port_Idx))))
- then
- Probe_Port (Configs (Pipe), Ports (Port_Idx), Success);
- else
- Success := False;
- 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;
-
- -- Restore power settings
- Power_And_Clocks.Power_Set_To (Cur_Configs);
- end Scan_Ports;
-
- ----------------------------------------------------------------------------
-
procedure Update_Outputs (Configs : Pipe_Configs)
is
Did_Power_Up : Boolean := False;
@@ -556,8 +176,9 @@
Old_Config := Cur_Configs (I);
New_Config := Configs (I);
- Fill_Port_Config
+ Config_Helpers.Fill_Port_Config
(Port_Cfg, I, Old_Configs (I).Port, Old_Configs (I).Mode, Success);
+ Port_Cfg.DP := DP_Links (I);
if Success then
Check_HPD (Port_Cfg, Old_Config.Port, HPD);
end if;
@@ -588,11 +209,13 @@
end if;
if New_Config.Port /= Disabled then
- Fill_Port_Config
+ Config_Helpers.Fill_Port_Config
(Port_Cfg, I, Configs (I).Port, Configs (I).Mode, Success);
- Success := Success and then
- Validate_Config (New_Config.Framebuffer, Port_Cfg, I);
+ if Success then
+ Success := Config_Helpers.Validate_Config
+ (New_Config.Framebuffer, Port_Cfg, I);
+ end if;
if Success and then Wait_For_HPD (New_Config.Port) then
Check_HPD (Port_Cfg, New_Config.Port, Success);
diff --git a/common/hw-gfx-gma.ads b/common/hw-gfx-gma.ads
index baf16da..114f87e 100644
--- a/common/hw-gfx-gma.ads
+++ b/common/hw-gfx-gma.ads
@@ -47,8 +47,6 @@
HDMI2, -- or DVI
HDMI3, -- or DVI
Analog);
- type Port_List_Range is range 0 .. 7;
- type Port_List is array (Port_List_Range) of Port_Type;
type Pipe_Config is record
Port : Port_Type;
@@ -78,10 +76,6 @@
procedure Legacy_VGA_Off;
- procedure Scan_Ports
- (Configs : out Pipe_Configs;
- Ports : in Port_List;
- Max_Pipe : in Pipe_Index := Pipe_Index'Last);
procedure Update_Outputs (Configs : Pipe_Configs);
pragma Warnings (GNATprove, Off, "subprogram ""Dump_Configs"" has no effect",
@@ -99,6 +93,17 @@
private
+ ----------------------------------------------------------------------------
+ -- State tracking for the currently configured pipes
+
+ Cur_Configs : Pipe_Configs with Part_Of => State;
+
+ ----------------------------------------------------------------------------
+ -- Internal representation of a single pipe's configuration
+
+ subtype Active_Port_Type is Port_Type
+ range Port_Type'Succ (Disabled) .. Port_Type'Last;
+
type GPU_Port is (DIGI_A, DIGI_B, DIGI_C, DIGI_D, DIGI_E);
subtype Digital_Port is GPU_Port range DIGI_A .. DIGI_E;