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