blob: 595f8f3ca87da6b239b9bc107faf6bace4e404a3 [file] [log] [blame]
--
-- Copyright (C) 2021 Angel Pons <th3fanbus@gmail.com>
--
-- 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.Debug;
with HW.Time;
with GNAT.Source_Info;
with HW.GFX.DP_Defs;
with HW.GFX.I2C;
use type HW.Word8;
package body HW.GFX.DP_Dual_Mode is
DUAL_MODE_I2C_ADDR : constant := 16#40#;
DUAL_MODE_ADAPTOR_ID : constant := 16#10#;
LSPCON_MODE_CHANGE : constant := 16#40#;
LSPCON_CURRENT_MODE : constant := 16#41#;
LSPCON_MODE_LS : constant := 0;
LSPCON_MODE_PCON : constant := 1;
procedure Read_LSPCON_Mode
(Port : in T;
Mode : out Word8;
Success : out Boolean)
is
begin
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
for I in 1 .. 6 loop
Aux_Ch.I2C_Read_Byte
(Port => Port,
Address => DUAL_MODE_I2C_ADDR,
Offset => LSPCON_CURRENT_MODE,
Value => Mode,
Success => Success);
exit when Success;
Time.U_Delay (500);
end loop;
end Read_LSPCON_Mode;
procedure Write_LSPCON_Mode
(Port : in T;
Mode : in Word8;
Success : out Boolean)
is
begin
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
for I in 1 .. 6 loop
Aux_Ch.I2C_Write_Byte
(Port => Port,
Address => DUAL_MODE_I2C_ADDR,
Offset => LSPCON_MODE_CHANGE,
Value => Mode,
Success => Success);
exit when Success;
Time.U_Delay (500);
end loop;
end Write_LSPCON_Mode;
pragma Warnings
(GNATprove, Off, "subprogram ""Dump_LSPCON_Mode"" has no effect",
Reason => "It's only used for debugging");
procedure Dump_LSPCON_Mode (Mode : in Word8)
is
begin
pragma Debug (Debug.Put ("Current LSPCON mode: "));
case Mode is
when LSPCON_MODE_LS => pragma Debug (Debug.Put_Line ("Level Shifter"));
when LSPCON_MODE_PCON => pragma Debug (Debug.Put_Line ("Protocol Converter"));
when others =>
pragma Debug (Debug.Put ("Unknown ("));
pragma Debug (Debug.Put_Word8 (Mode));
pragma Debug (Debug.Put_Line (")"));
end case;
end Dump_LSPCON_Mode;
procedure Auto_Configure_LSPCON (Port : in T)
is
Wanted_Mode : constant Word8 := LSPCON_MODE_PCON;
Timeout : Time.T;
Timed_Out : Boolean;
Success : Boolean;
Mode : Word8;
begin
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
Read_LSPCON_Mode (Port, Mode, Success);
if not Success then
pragma Debug (Debug.Put_Line ("Could not determine LSPCON mode."));
return;
end if;
Dump_LSPCON_Mode (Mode);
if Mode = Wanted_Mode then
pragma Debug (Debug.Put_Line ("LSPCON mode is good enough."));
return;
end if;
-- Change to PCON mode if necessary
Write_LSPCON_Mode (Port, Wanted_Mode, Success);
if not Success then
pragma Debug (Debug.Put_Line ("Could not write new LSPCON mode."));
return;
end if;
Timeout := Time.MS_From_Now (2000);
loop
Read_LSPCON_Mode (Port, Mode, Success);
if not Success then
pragma Debug (Debug.Put_Line ("Could not confirm LSPCON mode change."));
return;
end if;
if Mode = Wanted_Mode then
pragma Debug (Debug.Put_Line ("Successfully set LSPCON to PCON mode."));
return;
end if;
Timed_Out := Time.Timed_Out (Timeout);
exit when Timed_Out;
end loop;
pragma Debug (Debug.Put_Line ("Timed out waiting for LSPCON mode change."));
Dump_LSPCON_Mode (Mode);
end Auto_Configure_LSPCON;
subtype HDMI_ID_Index is Natural range 0 .. 15;
subtype HDMI_ID_Data is Buffer (HDMI_ID_Index);
procedure Read_Dual_Mode_HDMI_ID
(Port : in T;
HDMI_ID : out HDMI_ID_Data;
Success : out Boolean)
is
Length : I2C.Transfer_Length := HDMI_ID'Length;
Buffer : I2C.Transfer_Data;
begin
Aux_Ch.I2C_Read
(Port => Port,
Address => DUAL_MODE_I2C_ADDR,
Length => Length,
Data => Buffer,
Success => Success);
Success := Success and then Length = HDMI_ID'Length;
if Success then
HDMI_ID := Buffer (HDMI_ID'Range);
else
HDMI_ID := HDMI_ID_Data'(others => 0);
end if;
end Read_Dual_Mode_HDMI_ID;
function Is_HDMI_Adaptor (Actual_ID : in HDMI_ID_Data) return Boolean
is
-- "DP-HDMI ADAPTOR\x04"
Expected_ID : constant HDMI_ID_Data :=
(16#44#, 16#50#, 16#2d#, 16#48#, 16#44#, 16#4d#, 16#49#, 16#20#,
16#41#, 16#44#, 16#41#, 16#50#, 16#54#, 16#4f#, 16#52#, 16#04#);
begin
return Expected_ID = Actual_ID;
end Is_HDMI_Adaptor;
procedure Detect_LSPCON (Port : in T; Success : out Boolean)
is
HDMI_ID : HDMI_ID_Data;
Adaptor_ID : Word8;
begin
Read_Dual_Mode_HDMI_ID (Port, HDMI_ID, Success);
Success := Success and then Is_HDMI_Adaptor (HDMI_ID);
if Success then
Aux_Ch.I2C_Read_Byte
(Port => Port,
Address => DUAL_MODE_I2C_ADDR,
Offset => DUAL_MODE_ADAPTOR_ID,
Value => Adaptor_ID,
Success => Success);
Success := Success and then Adaptor_ID = 16#a8#;
end if;
end Detect_LSPCON;
procedure Switch_LSPCON (Port : in T)
is
Has_LSPCON : Boolean;
begin
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
Detect_LSPCON (Port, Has_LSPCON);
if Has_LSPCON then
Auto_Configure_LSPCON (Port);
end if;
end Switch_LSPCON;
end HW.GFX.DP_Dual_Mode;