| -- |
| -- 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.Wait_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; |
| 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, |
| 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 |
| 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 |
| 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; |
| |
| end HW.GFX.GMA.Display_Probing; |