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/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;