236 lines
7.8 KiB
Ada
236 lines
7.8 KiB
Ada
|
--
|
||
|
-- Copyright (C) 2015-2016 secunet Security Networks AG
|
||
|
-- Copyright (C) 2019 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.GFX.I2C;
|
||
|
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.Port_Detect;
|
||
|
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 is
|
||
|
(Configs (Primary).Port = Port or
|
||
|
Configs (Secondary).Port = Port or
|
||
|
Configs (Tertiary).Port = Port);
|
||
|
|
||
|
-- DP and HDMI share physical pins.
|
||
|
function Sibling_Port (Port : Port_Type) return Port_Type is
|
||
|
(case Port is
|
||
|
when HDMI1 => DP1,
|
||
|
when HDMI2 => DP2,
|
||
|
when HDMI3 => DP3,
|
||
|
when DP1 => HDMI1,
|
||
|
when DP2 => HDMI2,
|
||
|
when DP3 => HDMI3,
|
||
|
when others => Disabled);
|
||
|
|
||
|
function Has_Sibling_Port (Port : Port_Type) return Boolean is
|
||
|
(Sibling_Port (Port) /= Disabled);
|
||
|
|
||
|
function Is_DVI_I (Port : Active_Port_Type) return Boolean is
|
||
|
(Config.Have_DVI_I and
|
||
|
(Port = Analog or
|
||
|
Config_Helpers.To_PCH_Port (Port) = Config.Analog_I2C_Port));
|
||
|
|
||
|
procedure Read_EDID
|
||
|
(Raw_EDID : out EDID.Raw_EDID_Data;
|
||
|
Port : in Active_Port_Type;
|
||
|
Success : out Boolean)
|
||
|
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 and CDClk 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);
|
||
|
Power_And_Clocks.Enable_CDClk;
|
||
|
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.Wait_On;
|
||
|
end if;
|
||
|
Read_EDID (Raw_EDID, Port, Success);
|
||
|
end if;
|
||
|
|
||
|
if Success and then
|
||
|
((not Is_DVI_I (Port) or 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;
|
||
|
end if;
|
||
|
end Probe_Port;
|
||
|
|
||
|
procedure Scan_Ports
|
||
|
(Configs : out Pipe_Configs;
|
||
|
Ports : in Port_List := All_Ports;
|
||
|
Max_Pipe : in Pipe_Index := Pipe_Index'Last;
|
||
|
Keep_Power : in Boolean := False)
|
||
|
is
|
||
|
Probe_Internal : Boolean := False;
|
||
|
|
||
|
Port_Idx : Port_List_Range := Port_List_Range'First;
|
||
|
Success : Boolean;
|
||
|
begin
|
||
|
Configs := (Pipe_Index =>
|
||
|
(Port => Disabled,
|
||
|
Mode => Invalid_Mode,
|
||
|
Cursor => Default_Cursor,
|
||
|
Framebuffer => Default_FB));
|
||
|
|
||
|
-- Turn panel on early to probe other ports during the power on delay.
|
||
|
for Idx in Port_List_Range loop
|
||
|
exit when Ports (Idx) = Disabled;
|
||
|
if Ports (Idx) = Internal then
|
||
|
Panel.On (Wait => False);
|
||
|
Probe_Internal := True;
|
||
|
exit;
|
||
|
end if;
|
||
|
end loop;
|
||
|
|
||
|
for Pipe in Pipe_Index range
|
||
|
Pipe_Index'First .. Pipe_Index'Min (Max_Pipe, Config.Max_Pipe)
|
||
|
loop
|
||
|
Success := False;
|
||
|
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;
|
||
|
exit when not Success;
|
||
|
end loop;
|
||
|
|
||
|
-- Restore power settings
|
||
|
if not Keep_Power then
|
||
|
Power_And_Clocks.Power_Set_To (Cur_Configs);
|
||
|
end if;
|
||
|
|
||
|
-- Turn panel power off if probing failed.
|
||
|
if Probe_Internal and not Port_Configured (Configs, Internal) then
|
||
|
Panel.Off;
|
||
|
end if;
|
||
|
end Scan_Ports;
|
||
|
|
||
|
procedure Hotplug_Events (Ports : out Port_List)
|
||
|
is
|
||
|
I : Port_List_Range := Port_List_Range'First;
|
||
|
Detected : Boolean;
|
||
|
begin
|
||
|
Ports := (others => Disabled);
|
||
|
for P in Active_Port_Type loop
|
||
|
Port_Detect.Hotplug_Detect (P, Detected);
|
||
|
if Detected then
|
||
|
Ports (I) := P;
|
||
|
exit when I = Port_List_Range'Last;
|
||
|
I := Port_List_Range'Succ (I);
|
||
|
end if;
|
||
|
end loop;
|
||
|
end Hotplug_Events;
|
||
|
|
||
|
end HW.GFX.GMA.Display_Probing;
|