blob: 7a7bd786f8464d23094f33b5eff2357232f903dc [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
Nico Huberf80c3e42019-05-10 13:44:02 +02002-- Copyright (C) 2015-2016, 2019 secunet Security Networks AG
Nico Huber41b18ca2017-08-27 02:03:04 +02003-- Copyright (C) 2017 Nico Huber <nico.h@gmx.de>
Nico Huber83693c82016-10-08 22:17:55 +02004--
5-- This program is free software; you can redistribute it and/or modify
6-- it under the terms of the GNU General Public License as published by
Nico Huber125a29e2016-10-18 00:23:54 +02007-- the Free Software Foundation; either version 2 of the License, or
8-- (at your option) any later version.
Nico Huber83693c82016-10-08 22:17:55 +02009--
10-- This program is distributed in the hope that it will be useful,
11-- but WITHOUT ANY WARRANTY; without even the implied warranty of
12-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13-- GNU General Public License for more details.
14--
15
16with Ada.Unchecked_Conversion;
17
18with HW.Debug;
19with GNAT.Source_Info;
20
21with HW.Time;
22with HW.GFX.DP_Defs;
23
24package body HW.GFX.DP_Training is
25
26 pragma Warnings (GNATprove, Off, "unused initial value of ""Port""*",
27 Reason => "Needed for a common interface");
28 function Training_Set
29 (Port : T;
30 Train_Set : DP_Info.Train_Set)
31 return Word8
32 is
33 use type DP_Info.DP_Voltage_Swing;
34 use type DP_Info.DP_Pre_Emph;
35 use type Word8;
36 Value : Word8;
37 begin
38 case Train_Set.Voltage_Swing is
39 when DP_Info.VS_Level_0 => Value := 16#00#;
40 when DP_Info.VS_Level_1 => Value := 16#01#;
41 when DP_Info.VS_Level_2 => Value := 16#02#;
42 when DP_Info.VS_Level_3 => Value := 16#03#;
43 end case;
44 if Train_Set.Voltage_Swing = Max_V_Swing (Port) then
45 Value := Value or 16#04#;
46 end if;
47
48 case Train_Set.Pre_Emph is
49 when DP_Info.Emph_Level_0 => Value := Value or 16#00#;
50 when DP_Info.Emph_Level_1 => Value := Value or 16#08#;
51 when DP_Info.Emph_Level_2 => Value := Value or 16#10#;
52 when DP_Info.Emph_Level_3 => Value := Value or 16#18#;
53 end case;
54 if Train_Set.Pre_Emph = Max_Pre_Emph (Port, Train_Set) then
55 Value := Value or 16#20#;
56 end if;
57
58 return Value;
59 end Training_Set;
60 pragma Warnings (GNATprove, On, "unused initial value of ""Port""*");
61
62 ----------------------------------------------------------------------------
63
64 function Lane_Count (Link : DP_Link) return Positive
65 with
66 Post => Lane_Count'Result <= 4
67 is
68 begin
69 return Positive (Lane_Count_As_Integer (Link.Lane_Count));
70 end Lane_Count;
71
72 procedure Sink_Init
73 (Port : in Aux_T;
74 Link : in DP_Link;
75 Success : out Boolean)
76 is
77 use type Word8;
78 function Link_Rate_As_Word8 is new Ada.Unchecked_Conversion
79 (Source => DP_Bandwidth, Target => Word8);
80 Data : DP_Defs.Aux_Payload;
81 begin
82 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
83
84 Data :=
85 (0 => Link_Rate_As_Word8 (Link.Bandwidth),
86 1 => Word8 (Lane_Count (Link)),
87 others => 0); -- Don't care
88
89 if Link.Enhanced_Framing then
90 Data (1) := Data (1) or 16#80#;
91 end if;
92
93 Aux_Ch.Aux_Write
94 (Port => Port,
95 Address => 16#00100#, -- LINK_BW_SET, LANE_COUNT_SET
96 Length => 2,
97 Data => Data,
98 Success => Success);
99 Success := Success or Link.Opportunistic_Training;
100
101 if Success then
102 Data (0) := 16#00#; -- no downspread
103 Data (1) := 16#01#; -- ANSI8B10B coding
104
105 Aux_Ch.Aux_Write
106 (Port => Port,
107 Address => 16#00107#, -- DOWNSPREAD_CTRL,
108 Length => 2, -- MAIN_LINK_CHANNEL_CODING_SET
109 Data => Data,
110 Success => Success);
111 Success := Success or Link.Opportunistic_Training;
112 end if;
113 end Sink_Init;
114
115 procedure Sink_Set_Training_Pattern
Nico Huber94fb9162019-05-10 13:44:11 +0200116 (Port : in T;
Nico Huber83693c82016-10-08 22:17:55 +0200117 Link : in DP_Link;
118 Pattern : in DP_Info.Training_Pattern;
Nico Huber94fb9162019-05-10 13:44:11 +0200119 Train_Set : in DP_Info.Train_Set;
Nico Huber83693c82016-10-08 22:17:55 +0200120 Success : out Boolean)
121 is
122 use type DP_Info.Training_Pattern;
123
124 type TP_Array is array (DP_Info.Training_Pattern) of Word8;
125 TP : constant TP_Array := TP_Array'
126 (DP_Info.TP_1 => 16#21#, DP_Info.TP_2 => 16#22#, DP_Info.TP_3 => 16#23#,
127 DP_Info.TP_Idle => 16#00#, DP_Info.TP_None => 16#00#);
Nico Huber94fb9162019-05-10 13:44:11 +0200128 T_Set : constant Word8 := Training_Set (Port, Train_Set);
Nico Huber83693c82016-10-08 22:17:55 +0200129
130 Data : DP_Defs.Aux_Payload;
131 Length : Positive := 1;
132 begin
133 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
134
Nico Huber94fb9162019-05-10 13:44:11 +0200135 Data := (TP (Pattern), others => 0);
Nico Huber83693c82016-10-08 22:17:55 +0200136 if Pattern < DP_Info.TP_Idle then
137 Length := Length + Lane_Count (Link);
Nico Huber94fb9162019-05-10 13:44:11 +0200138 Data (1 .. Lane_Count (Link)) := (others => T_Set);
Nico Huber83693c82016-10-08 22:17:55 +0200139 end if;
Nico Huber94fb9162019-05-10 13:44:11 +0200140
Nico Huber83693c82016-10-08 22:17:55 +0200141 Aux_Ch.Aux_Write
Nico Huber94fb9162019-05-10 13:44:11 +0200142 (Port => To_Aux (Port),
Nico Huber83693c82016-10-08 22:17:55 +0200143 Address => 16#00102#, -- TRAINING_PATTERN_SET
144 Length => Length,
145 Data => Data,
146 Success => Success);
147 end Sink_Set_Training_Pattern;
148
149 procedure Sink_Set_Signal_Levels
150 (Port : in T;
151 DP : in Aux_T;
152 Link : in DP_Link;
153 Train_Set : in DP_Info.Train_Set;
154 Success : out Boolean)
155 is
156 Data : DP_Defs.Aux_Payload;
157 T_Set : constant Word8 := Training_Set (Port, Train_Set);
158 begin
159 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
160
161 Data := (others => 0); -- Initialize
162 Data (0 .. Lane_Count (Link) - 1) := (others => T_Set);
163
164 Aux_Ch.Aux_Write
165 (Port => DP,
166 Address => 16#00103#, -- TRAINING_LANEx_SET
167 Length => Lane_Count (Link),
168 Data => Data,
169 Success => Success);
170 end Sink_Set_Signal_Levels;
171
172 pragma Warnings (GNATprove, Off, "unused initial value of ""Port""*",
173 Reason => "Needed for a common interface");
174 procedure Sink_Adjust_Training
175 (Port : in T;
176 DP : in Aux_T;
177 Link : in DP_Link;
178 Train_Set : in out DP_Info.Train_Set;
179 CR_Done : in out Boolean;
180 EQ_Done : out Boolean;
181 Success : out Boolean)
182 is
183 use type DP_Info.DP_Voltage_Swing;
184 use type DP_Info.DP_Pre_Emph;
185
186 Status : DP_Info.Link_Status;
187 CR_Was_Done : constant Boolean := CR_Done;
188
189 pragma Warnings
190 (GNATprove, Off, "subprogram ""Dump_Link_Status"" has no effect*",
191 Reason => "It's only used for debugging");
192 procedure Dump_Link_Status
193 is
194 begin
195 Debug.New_Line;
196 Debug.Put_Line ("Link Status:");
197
198 for Lane in DP_Info.Lane_Index range 0
199 .. DP_Info.Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
200 loop
201 Debug.Put (" Lane");
202 Debug.Put_Int8 (Int8 (Lane));
203 Debug.Put_Line (":");
204
Nico Huberfb80da92026-07-02 15:01:13 +0200205 Debug.Put (" CR_Done : ");
206 if Status.Lanes (Lane).CR_Done then
207 Debug.Put_Line ("1");
208 else
209 Debug.Put_Line ("0");
210 end if;
211 Debug.Put (" Channel_EQ_Done: ");
212 if Status.Lanes (Lane).Channel_EQ_Done then
213 Debug.Put_Line ("1");
214 else
215 Debug.Put_Line ("0");
216 end if;
217 Debug.Put (" Symbol_Locked : ");
218 if Status.Lanes (Lane).Symbol_Locked then
219 Debug.Put_Line ("1");
220 else
221 Debug.Put_Line ("0");
222 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200223 end loop;
224
Nico Huberfb80da92026-07-02 15:01:13 +0200225 Debug.Put (" Interlane_Align_Done: ");
226 if Status.Interlane_Align_Done then
227 Debug.Put_Line ("1");
228 else
229 Debug.Put_Line ("0");
230 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200231
232 for Lane in DP_Info.Lane_Index range 0
233 .. DP_Info.Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
234 loop
235 Debug.Put (" Adjust");
236 Debug.Put_Int8 (Int8 (Lane));
237 Debug.Put_Line (":");
238
239 Debug.Put (" Voltage_Swing: ");
240 Debug.Put_Int8 (Int8 (DP_Info.DP_Voltage_Swing'Pos
241 (Status.Adjust_Requests (Lane).Voltage_Swing)));
242 Debug.New_Line;
243 Debug.Put (" Pre_Emph : ");
244 Debug.Put_Int8 (Int8 (DP_Info.DP_Pre_Emph'Pos
245 (Status.Adjust_Requests (Lane).Pre_Emph)));
246 Debug.New_Line;
247 end loop;
248
249 Debug.New_Line;
250 end Dump_Link_Status;
251 begin
252 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
253
254 DP_Info.Read_Link_Status
255 (Port => DP,
256 Status => Status,
257 Success => Success);
258
259 pragma Debug (Success, Dump_Link_Status);
260
261 CR_Done := Success and then DP_Info.All_CR_Done (Status, Link);
262 EQ_Done := Success and then DP_Info.All_EQ_Done (Status, Link);
263 Success := Success and then (CR_Done or not CR_Was_Done);
264
Nico Huber41b18ca2017-08-27 02:03:04 +0200265 -- Voltage swing may be updated during channel equalization too.
266 if Success and not EQ_Done then
Nico Huber83693c82016-10-08 22:17:55 +0200267 Train_Set.Voltage_Swing :=
268 DP_Info.Max_Requested_VS (Status, Link);
269 if Train_Set.Voltage_Swing > Max_V_Swing (Port)
270 then
271 Train_Set.Voltage_Swing := Max_V_Swing (Port);
272 end if;
273 end if;
274
275 -- According to DP spec, only change preemphasis during channel
276 -- equalization. What to do if sink requests it during clock recovery?
Nico Huber234e7722017-08-27 02:07:31 +0200277 -- Linux always accepts new values from the sink, we too, now: There
278 -- are sinks in the wild that need this.
279 if Success and not EQ_Done then
Nico Huber83693c82016-10-08 22:17:55 +0200280 Train_Set.Pre_Emph :=
281 DP_Info.Max_Requested_Emph (Status, Link);
282 if Train_Set.Pre_Emph > Max_Pre_Emph (Port, Train_Set)
283 then
284 Train_Set.Pre_Emph := Max_Pre_Emph (Port, Train_Set);
285 end if;
286 end if;
287 end Sink_Adjust_Training;
288 pragma Warnings (GNATprove, On, "unused initial value of ""Port""*");
289
290 ----------------------------------------------------------------------------
291
292 procedure Train_DP
293 (Port : in T;
294 Link : in DP_Link;
295 Success : out Boolean)
296 is
297 use type DP_Info.DP_Voltage_Swing;
298 use type DP_Info.DP_Pre_Emph;
299 use type Word8;
300
301 DP : constant Aux_T := To_Aux (Port);
302
303 Retries : Natural;
304 Max_Retry : constant := 4;
305 CR_Done, EQ_Done : Boolean := False;
306
307 EQ_Pattern : constant DP_Info.Training_Pattern :=
308 (if TPS3_Supported and Link.Receiver_Caps.TPS3_Supported then
309 DP_Info.TP_3
310 else
311 DP_Info.TP_2);
312
313 Train_Set, Last_Train_Set : DP_Info.Train_Set;
314
315 function CR_Delay return Natural is
316 Result : Natural := 100; -- DP spec: 100us
317 begin
318 if Link.Bandwidth = DP_Bandwidth_5_4 and
319 Link.Receiver_Caps.Aux_RD_Interval /= 0
320 then
321 Result := Natural (Link.Receiver_Caps.Aux_RD_Interval) * 4_000;
322 end if;
323 return Result;
324 end CR_Delay;
325
326 function EQ_Delay return Natural is
327 Result : Natural := 400; -- DP spec: 400us
328 begin
Angel Ponsb789e862022-09-08 18:06:40 +0200329 if Link.Receiver_Caps.Aux_RD_Interval /= 0
Nico Huber83693c82016-10-08 22:17:55 +0200330 then
331 Result := Natural (Link.Receiver_Caps.Aux_RD_Interval) * 4_000;
332 end if;
333 return Result;
334 end EQ_Delay;
335 begin
336 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
337
338 Train_Set.Voltage_Swing := DP_Info.DP_Voltage_Swing'First;
339 Train_Set.Pre_Emph := DP_Info.DP_Pre_Emph'First;
340
341 Set_Pattern (Port, Link, DP_Info.TP_1);
342 Set_Signal_Levels (Port, Link, Train_Set);
343
344 pragma Warnings
345 (GNATprove, Off, """Success"" modified by call, but value overwritten*",
346 Reason => "Read first, then overwritten, looks like a false positive");
347 Sink_Init (DP, Link, Success);
348 pragma Warnings
349 (GNATprove, On, """Success"" modified by call, but value overwritten*");
350 if Success then
Nico Huber94fb9162019-05-10 13:44:11 +0200351 Sink_Set_Training_Pattern
352 (Port, Link, DP_Info.TP_1, Train_Set, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200353 end if;
354
355 if Success then
356 Retries := 0;
357 for Tries in 1 .. 32 loop
358 pragma Loop_Invariant (Retries <= Max_Retry);
359
360 Time.U_Delay (CR_Delay);
361
362 Last_Train_Set := Train_Set;
363 Sink_Adjust_Training
364 (Port, DP, Link, Train_Set, CR_Done, EQ_Done, Success);
365 exit when CR_Done or not Success;
366
367 if Train_Set.Voltage_Swing = Last_Train_Set.Voltage_Swing then
368 exit when Retries = Max_Retry;
369 Retries := Retries + 1;
370 else
371 exit when Last_Train_Set.Voltage_Swing = Max_V_Swing (Port);
372 Retries := 0;
373 end if;
374
375 Set_Signal_Levels (Port, Link, Train_Set);
376 Sink_Set_Signal_Levels (Port, DP, Link, Train_Set, Success);
377 exit when not Success;
378 end loop;
379 end if;
380
381 Success := Success and CR_Done;
382
383 if Success then
384 Set_Pattern (Port, Link, EQ_Pattern);
Nico Huber94fb9162019-05-10 13:44:11 +0200385 Sink_Set_Training_Pattern (Port, Link, EQ_Pattern, Train_Set, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200386 end if;
387
388 if Success then
Nico Huber41b18ca2017-08-27 02:03:04 +0200389 for Tries in 1 .. 6 loop
Nico Huber83693c82016-10-08 22:17:55 +0200390 Time.U_Delay (EQ_Delay);
391
Nico Huber83693c82016-10-08 22:17:55 +0200392 Sink_Adjust_Training
393 (Port, DP, Link, Train_Set, CR_Done, EQ_Done, Success);
394 exit when EQ_Done or not Success;
395
Nico Huber83693c82016-10-08 22:17:55 +0200396 Set_Signal_Levels (Port, Link, Train_Set);
397 Sink_Set_Signal_Levels (Port, DP, Link, Train_Set, Success);
398 exit when not Success;
399 end loop;
400 end if;
401
Nico Huberf80c3e42019-05-10 13:44:02 +0200402 -- Set_Pattern (TP_None) includes sending the Idle Pattern,
403 -- so tell sink first.
404 Sink_Set_Training_Pattern
Nico Huber94fb9162019-05-10 13:44:11 +0200405 (Port, Link, DP_Info.TP_None, Train_Set, Success);
Nico Huberf80c3e42019-05-10 13:44:02 +0200406 Set_Pattern (Port, Link, DP_Info.TP_None);
Nico Huber83693c82016-10-08 22:17:55 +0200407
Nico Huberf80c3e42019-05-10 13:44:02 +0200408 Success := Success and then EQ_Done;
409 if not Success then
Nico Huber83693c82016-10-08 22:17:55 +0200410 Off (Port);
411 end if;
412 end Train_DP;
413
414end HW.GFX.DP_Training;