blob: 595f8f3ca87da6b239b9bc107faf6bace4e404a3 [file] [log] [blame]
Angel Pons8d5c24d2021-03-01 15:59:58 +01001--
2-- Copyright (C) 2021 Angel Pons <th3fanbus@gmail.com>
3--
4-- This program is free software; you can redistribute it and/or modify
5-- it under the terms of the GNU General Public License as published by
6-- the Free Software Foundation; either version 2 of the License, or
7-- (at your option) any later version.
8--
9-- This program is distributed in the hope that it will be useful,
10-- but WITHOUT ANY WARRANTY; without even the implied warranty of
11-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-- GNU General Public License for more details.
13--
14
15with HW.Debug;
16with HW.Time;
17with GNAT.Source_Info;
18
19with HW.GFX.DP_Defs;
20with HW.GFX.I2C;
21
22use type HW.Word8;
23
24package body HW.GFX.DP_Dual_Mode is
25
26 DUAL_MODE_I2C_ADDR : constant := 16#40#;
27
28 DUAL_MODE_ADAPTOR_ID : constant := 16#10#;
29 LSPCON_MODE_CHANGE : constant := 16#40#;
30 LSPCON_CURRENT_MODE : constant := 16#41#;
31
32 LSPCON_MODE_LS : constant := 0;
33 LSPCON_MODE_PCON : constant := 1;
34
35 procedure Read_LSPCON_Mode
36 (Port : in T;
37 Mode : out Word8;
38 Success : out Boolean)
39 is
40 begin
41 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
42 for I in 1 .. 6 loop
43 Aux_Ch.I2C_Read_Byte
44 (Port => Port,
45 Address => DUAL_MODE_I2C_ADDR,
46 Offset => LSPCON_CURRENT_MODE,
47 Value => Mode,
48 Success => Success);
49 exit when Success;
50 Time.U_Delay (500);
51 end loop;
52 end Read_LSPCON_Mode;
53
54 procedure Write_LSPCON_Mode
55 (Port : in T;
56 Mode : in Word8;
57 Success : out Boolean)
58 is
59 begin
60 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
61 for I in 1 .. 6 loop
62 Aux_Ch.I2C_Write_Byte
63 (Port => Port,
64 Address => DUAL_MODE_I2C_ADDR,
65 Offset => LSPCON_MODE_CHANGE,
66 Value => Mode,
67 Success => Success);
68 exit when Success;
69 Time.U_Delay (500);
70 end loop;
71 end Write_LSPCON_Mode;
72
73 pragma Warnings
74 (GNATprove, Off, "subprogram ""Dump_LSPCON_Mode"" has no effect",
75 Reason => "It's only used for debugging");
76 procedure Dump_LSPCON_Mode (Mode : in Word8)
77 is
78 begin
79 pragma Debug (Debug.Put ("Current LSPCON mode: "));
80 case Mode is
81 when LSPCON_MODE_LS => pragma Debug (Debug.Put_Line ("Level Shifter"));
82 when LSPCON_MODE_PCON => pragma Debug (Debug.Put_Line ("Protocol Converter"));
83 when others =>
84 pragma Debug (Debug.Put ("Unknown ("));
85 pragma Debug (Debug.Put_Word8 (Mode));
86 pragma Debug (Debug.Put_Line (")"));
87 end case;
88 end Dump_LSPCON_Mode;
89
90 procedure Auto_Configure_LSPCON (Port : in T)
91 is
92 Wanted_Mode : constant Word8 := LSPCON_MODE_PCON;
93 Timeout : Time.T;
94 Timed_Out : Boolean;
95 Success : Boolean;
96 Mode : Word8;
97 begin
98 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
99
100 Read_LSPCON_Mode (Port, Mode, Success);
101
102 if not Success then
103 pragma Debug (Debug.Put_Line ("Could not determine LSPCON mode."));
104 return;
105 end if;
106
107 Dump_LSPCON_Mode (Mode);
108
109 if Mode = Wanted_Mode then
110 pragma Debug (Debug.Put_Line ("LSPCON mode is good enough."));
111 return;
112 end if;
113
114 -- Change to PCON mode if necessary
115 Write_LSPCON_Mode (Port, Wanted_Mode, Success);
116
117 if not Success then
118 pragma Debug (Debug.Put_Line ("Could not write new LSPCON mode."));
119 return;
120 end if;
121
122 Timeout := Time.MS_From_Now (2000);
123 loop
124 Read_LSPCON_Mode (Port, Mode, Success);
125
126 if not Success then
127 pragma Debug (Debug.Put_Line ("Could not confirm LSPCON mode change."));
128 return;
129 end if;
130
131 if Mode = Wanted_Mode then
132 pragma Debug (Debug.Put_Line ("Successfully set LSPCON to PCON mode."));
133 return;
134 end if;
135
136 Timed_Out := Time.Timed_Out (Timeout);
137 exit when Timed_Out;
138 end loop;
139
140 pragma Debug (Debug.Put_Line ("Timed out waiting for LSPCON mode change."));
141 Dump_LSPCON_Mode (Mode);
142 end Auto_Configure_LSPCON;
143
144 subtype HDMI_ID_Index is Natural range 0 .. 15;
145 subtype HDMI_ID_Data is Buffer (HDMI_ID_Index);
146
147 procedure Read_Dual_Mode_HDMI_ID
148 (Port : in T;
149 HDMI_ID : out HDMI_ID_Data;
150 Success : out Boolean)
151 is
152 Length : I2C.Transfer_Length := HDMI_ID'Length;
153 Buffer : I2C.Transfer_Data;
154 begin
155 Aux_Ch.I2C_Read
156 (Port => Port,
157 Address => DUAL_MODE_I2C_ADDR,
158 Length => Length,
159 Data => Buffer,
160 Success => Success);
161
162 Success := Success and then Length = HDMI_ID'Length;
163 if Success then
164 HDMI_ID := Buffer (HDMI_ID'Range);
165 else
166 HDMI_ID := HDMI_ID_Data'(others => 0);
167 end if;
168 end Read_Dual_Mode_HDMI_ID;
169
170 function Is_HDMI_Adaptor (Actual_ID : in HDMI_ID_Data) return Boolean
171 is
172 -- "DP-HDMI ADAPTOR\x04"
173 Expected_ID : constant HDMI_ID_Data :=
174 (16#44#, 16#50#, 16#2d#, 16#48#, 16#44#, 16#4d#, 16#49#, 16#20#,
175 16#41#, 16#44#, 16#41#, 16#50#, 16#54#, 16#4f#, 16#52#, 16#04#);
176 begin
177 return Expected_ID = Actual_ID;
178 end Is_HDMI_Adaptor;
179
180 procedure Detect_LSPCON (Port : in T; Success : out Boolean)
181 is
182 HDMI_ID : HDMI_ID_Data;
183 Adaptor_ID : Word8;
184 begin
185 Read_Dual_Mode_HDMI_ID (Port, HDMI_ID, Success);
186 Success := Success and then Is_HDMI_Adaptor (HDMI_ID);
187 if Success then
188 Aux_Ch.I2C_Read_Byte
189 (Port => Port,
190 Address => DUAL_MODE_I2C_ADDR,
191 Offset => DUAL_MODE_ADAPTOR_ID,
192 Value => Adaptor_ID,
193 Success => Success);
194
195 Success := Success and then Adaptor_ID = 16#a8#;
196 end if;
197 end Detect_LSPCON;
198
199 procedure Switch_LSPCON (Port : in T)
200 is
201 Has_LSPCON : Boolean;
202 begin
203 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
204 Detect_LSPCON (Port, Has_LSPCON);
205 if Has_LSPCON then
206 Auto_Configure_LSPCON (Port);
207 end if;
208 end Switch_LSPCON;
209
210end HW.GFX.DP_Dual_Mode;