From 8c45bcfc4d024c866e4c2b92b1768b413ec8be9c Mon Sep 17 00:00:00 2001 From: Nico Huber Date: Sun, 20 Nov 2016 17:30:57 +0100 Subject: [PATCH] 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 Reviewed-on: https://review.coreboot.org/17757 Reviewed-by: Adrian-Ken Rueegsegger --- common/Makefile.inc | 4 + common/hw-gfx-gma-config_helpers.adb | 210 ++++++++++++++ common/hw-gfx-gma-config_helpers.ads | 45 +++ common/hw-gfx-gma-display_probing.adb | 208 ++++++++++++++ common/hw-gfx-gma-display_probing.ads | 26 ++ common/hw-gfx-gma.adb | 393 +------------------------- common/hw-gfx-gma.ads | 17 +- 7 files changed, 512 insertions(+), 391 deletions(-) create mode 100644 common/hw-gfx-gma-config_helpers.adb create mode 100644 common/hw-gfx-gma-config_helpers.ads create mode 100644 common/hw-gfx-gma-display_probing.adb create mode 100644 common/hw-gfx-gma-display_probing.ads diff --git a/common/Makefile.inc b/common/Makefile.inc index 9745eb6c11..11215ee411 100644 --- a/common/Makefile.inc +++ b/common/Makefile.inc @@ -7,9 +7,13 @@ gfxinit-y += hw-gfx-dp_training.adb 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 0000000000..31406cf7e2 --- /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 0000000000..b56e1b1b08 --- /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 0000000000..7320004d7e --- /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 0000000000..3d1e9147ad --- /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 4b09d51923..0a5b1b51b8 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 @@ is 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 @@ 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 - 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 @@ 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 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 @@ is 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 @@ is 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 baf16daf96..114f87e362 100644 --- a/common/hw-gfx-gma.ads +++ b/common/hw-gfx-gma.ads @@ -47,8 +47,6 @@ is 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 @@ is 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 @@ is 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;