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