blob: b5614284db50497127b5b23d76c2160121b46b8d [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
2-- Copyright (C) 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 HW.GFX.GMA.Registers;
16
17package body HW.GFX.GMA.PLLs.DPLL is
18
19 -- NOTE: Order of DPLLs is twisted => always use named associations!
20
21 type Regs is array (Configurable_DPLLs) of Registers.Registers_Index;
22
23 DPLL_CTL : constant Regs := Regs'
24 (DPLL1 => Registers.LCPLL2_CTL,
25 DPLL2 => Registers.WRPLL_CTL_1,
26 DPLL3 => Registers.WRPLL_CTL_2);
27 DPLL_CTL_PLL_ENABLE : constant := 1 * 2 ** 31;
28
29 ----------------------------------------------------------------------------
30
31 DPLL_CFGR1 : constant Regs := Regs'
32 (DPLL1 => Registers.DPLL1_CFGR1,
33 DPLL2 => Registers.DPLL2_CFGR1,
34 DPLL3 => Registers.DPLL3_CFGR1);
35 DPLL_CFGR1_FREQUENCY_ENABLE : constant := 1 * 2 ** 31;
36 DPLL_CFGR1_DCO_FRACTION_SHIFT : constant := 9;
37 DPLL_CFGR1_DCO_FRACTION_MASK : constant := 16#7fff# * 2 ** 9;
38 DPLL_CFGR1_DCO_INTEGER_MASK : constant := 16#01ff# * 2 ** 0;
39
40 DPLL_CFGR2 : constant Regs := Regs'
41 (DPLL1 => Registers.DPLL1_CFGR2,
42 DPLL2 => Registers.DPLL2_CFGR2,
43 DPLL3 => Registers.DPLL3_CFGR2);
44 DPLL_CFGR2_QDIV_RATIO_SHIFT : constant := 8;
45 DPLL_CFGR2_QDIV_RATIO_MASK : constant := 255 * 2 ** 8;
46 DPLL_CFGR2_QDIV_MODE : constant := 1 * 2 ** 7;
47 DPLL_CFGR2_KDIV_SHIFT : constant := 5;
48 DPLL_CFGR2_KDIV_MASK : constant := 3 * 2 ** 5;
49 DPLL_CFGR2_PDIV_SHIFT : constant := 2;
50 DPLL_CFGR2_PDIV_MASK : constant := 7 * 2 ** 2;
51 DPLL_CFGR2_CENTRAL_FREQ_MASK : constant := 3 * 2 ** 0;
52 DPLL_CFGR2_CENTRAL_FREQ_9600MHZ : constant := 0 * 2 ** 0;
53 DPLL_CFGR2_CENTRAL_FREQ_9000MHZ : constant := 1 * 2 ** 0;
54 DPLL_CFGR2_CENTRAL_FREQ_8400MHZ : constant := 3 * 2 ** 0;
55
56 ----------------------------------------------------------------------------
57
58 HDMI_MODE : constant := 1 * 2 ** 5;
59 SSC : constant := 1 * 2 ** 4;
60 LINK_RATE_MASK : constant := 7 * 2 ** 1;
61 LINK_RATE_2700MHZ : constant := 0 * 2 ** 1;
62 LINK_RATE_1350MHZ : constant := 1 * 2 ** 1;
63 LINK_RATE_810MHZ : constant := 2 * 2 ** 1;
64 LINK_RATE_1620MHZ : constant := 3 * 2 ** 1;
65 LINK_RATE_1080MHZ : constant := 4 * 2 ** 1;
66 LINK_RATE_2160MHZ : constant := 5 * 2 ** 1;
67 OVERRIDE : constant := 1 * 2 ** 0;
68
69 LOCK : constant := 1 * 2 ** 0;
70
71 type Shifts is array (Configurable_DPLLs) of Natural;
72 DPLL_CTRL1_SHIFT : constant Shifts :=
73 (DPLL1 => 6, DPLL2 => 12, DPLL3 => 18);
74 DPLL_STATUS_SHIFT : constant Shifts :=
75 (DPLL1 => 8, DPLL2 => 16, DPLL3 => 24);
76
77 function LINK_RATE (Link_Rate : DP_Bandwidth) return Word32 is
78 begin
79 return (case Link_Rate is
80 when DP_Bandwidth_5_4 => LINK_RATE_2700MHZ,
81 when DP_Bandwidth_2_7 => LINK_RATE_1350MHZ,
82 when DP_Bandwidth_1_62 => LINK_RATE_810MHZ);
83 end LINK_RATE;
84
85 function DPLL_CTRL1_DPLLx
86 (Value : Word32;
87 PLL : Configurable_DPLLs)
88 return Word32 is
89 begin
90 return Shift_Left (Value, DPLL_CTRL1_SHIFT (PLL));
91 end DPLL_CTRL1_DPLLx;
92
93 function DPLL_STATUS_DPLLx_LOCK (PLL : Configurable_DPLLs) return Word32 is
94 begin
95 return Shift_Left (LOCK, DPLL_STATUS_SHIFT (PLL));
96 end DPLL_STATUS_DPLLx_LOCK;
97
98 ----------------------------------------------------------------------------
99
100 subtype PDiv_Range is Pos64 range 1 .. 7;
101 subtype QDiv_Range is Pos64 range 1 .. 255;
102 subtype KDiv_Range is Pos64 range 1 .. 5;
103
104 type Central_Frequency is (CF_INVALID, CF_9600MHZ, CF_9000MHZ, CF_8400MHZ);
105 subtype Valid_Central_Freq is
106 Central_Frequency range CF_9600MHZ .. CF_8400MHZ;
107
108 type CF_Pos is array (Valid_Central_Freq) of Pos64;
109 CF_Pos64 : constant CF_Pos := CF_Pos'
110 (CF_9600MHZ => 9_600_000_000,
111 CF_9000MHZ => 9_000_000_000,
112 CF_8400MHZ => 8_400_000_000);
113
114 subtype DCO_Frequency is
115 Pos64 range 1 .. CF_Pos64 (CF_9600MHZ) + CF_Pos64 (CF_9600MHZ) / 100;
116
117 function DPLL_CFGR1_DCO_FRACTION (DCO_Freq : DCO_Frequency) return Word32
118 with
119 Pre => True
120 is
121 begin
122 return Shift_Left
123 (Word32 ((DCO_Freq * 2 ** 15) / 24_000_000) and 16#7fff#,
124 DPLL_CFGR1_DCO_FRACTION_SHIFT);
125 end DPLL_CFGR1_DCO_FRACTION;
126
127 function DPLL_CFGR1_DCO_INTEGER (DCO_Freq : DCO_Frequency) return Word32
128 with
129 Pre => True
130 is
131 begin
132 return Word32 (DCO_Freq / 24_000_000);
133 end DPLL_CFGR1_DCO_INTEGER;
134
135 function DPLL_CFGR2_PDIV (PDiv : PDiv_Range) return Word32 is
136 begin
137 return Shift_Left
138 ((case PDiv is
139 when 1 => 0,
140 when 2 => 1,
141 when 3 => 2,
142 when 7 => 4,
143 when others => 4),
144 DPLL_CFGR2_PDIV_SHIFT);
145 end DPLL_CFGR2_PDIV;
146
147 function DPLL_CFGR2_QDIV (QDiv : QDiv_Range) return Word32 is
148 begin
149 return Shift_Left (Word32 (QDiv), DPLL_CFGR2_QDIV_RATIO_SHIFT) or
150 (if QDiv /= 1 then DPLL_CFGR2_QDIV_MODE else 0);
151 end DPLL_CFGR2_QDIV;
152
153 function DPLL_CFGR2_KDIV (KDiv : KDiv_Range) return Word32 is
154 begin
155 return Shift_Left
156 ((case KDiv is
157 when 5 => 0,
158 when 2 => 1,
159 when 3 => 2,
160 when 1 => 3,
161 when others => 0),
162 DPLL_CFGR2_KDIV_SHIFT);
163 end DPLL_CFGR2_KDIV;
164
165 function DPLL_CFGR2_CENTRAL_FREQ (CF : Valid_Central_Freq) return Word32 is
166 begin
167 return (case CF is
168 when CF_9600MHZ => DPLL_CFGR2_CENTRAL_FREQ_9600MHZ,
169 when CF_9000MHZ => DPLL_CFGR2_CENTRAL_FREQ_9000MHZ,
170 when CF_8400MHZ => DPLL_CFGR2_CENTRAL_FREQ_8400MHZ);
171 end DPLL_CFGR2_CENTRAL_FREQ;
172
173 ----------------------------------------------------------------------------
174
175 procedure Calculate_DPLL
176 (Dotclock : in Frequency_Type;
177 Central_Freq : out Central_Frequency;
178 DCO_Freq : out DCO_Frequency;
179 PDiv : out PDiv_Range;
180 QDiv : out QDiv_Range;
181 KDiv : out KDiv_Range)
182 with
183 Pre => True
184 is
185 Max_Pos_Deviation : constant := 1;
186 Max_Neg_Deviation : constant := 6;
187
188 subtype Div_Range is Pos64 range 1 .. 98;
189 subtype Candidate_Index is Positive range 1 .. 36;
190 type Candidate_Array is array (Candidate_Index) of Div_Range;
191 type Candidate_List is record
192 Divs : Candidate_Array;
193 Count : Candidate_Index;
194 end record;
195 type Parity_Type is (Even, Odd);
196 type Candidates_Type is array (Parity_Type) of Candidate_List;
197
198 Candidates : constant Candidates_Type := Candidates_Type'
199 (Even => Candidate_List'
200 (Divs => Candidate_Array'
201 (4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 30, 32, 36, 40, 42, 44,
202 48, 52, 54, 56, 60, 64, 66, 68, 70, 72, 76, 78, 80, 84, 88, 90,
203 92, 96, 98),
204 Count => 36),
205 Odd => Candidate_List'
206 (Divs => Candidate_Array'(3, 5, 7, 9, 15, 21, 35, others => 1),
207 Count => 7));
208
209 Temp_Freq,
210 Allowed_Deviation : Pos64;
211 Deviation : Int64;
212 Temp_Central : DCO_Frequency;
213 Min_Deviation : Int64 := Int64'Last;
214 Div : Div_Range := Div_Range'Last;
215 begin
216 Central_Freq := CF_INVALID;
217 DCO_Freq := 1;
218 PDiv := 1;
219 QDiv := 1;
220 KDiv := 1;
221
222 for Parity in Parity_Type loop
223 for CF in Valid_Central_Freq loop
224 Temp_Central := CF_Pos64 (CF);
225 for I in Candidate_Index range 1 .. Candidates (Parity).Count loop
226 Temp_Freq := Candidates (Parity).Divs (I) * 5 * Dotclock;
227 if Temp_Freq > Temp_Central then
228 Deviation := Temp_Freq - Temp_Central;
229 Allowed_Deviation := (Max_Pos_Deviation * Temp_Central) / 100;
230 else
231 Deviation := Temp_Central - Temp_Freq;
232 Allowed_Deviation := (Max_Neg_Deviation * Temp_Central) / 100;
233 end if;
234 if Deviation < Min_Deviation and
235 Deviation < Allowed_Deviation
236 then
237 Min_Deviation := Deviation;
238 Central_Freq := CF;
239 DCO_Freq := Temp_Freq;
240 Div := Candidates (Parity).Divs (I);
241 end if;
242 end loop;
243 end loop;
244 exit when Central_Freq /= CF_INVALID;
245 end loop;
246
247 if Central_Freq /= CF_INVALID then
248 if Div mod 2 = 0 then
249 pragma Assert (Div /= 1);
250 pragma Assert (Div > 1);
251 Div := Div / 2;
252 if Div = 1 or Div = 3 or Div = 5 then
253 -- 2, 6 and 10
254 PDiv := 2;
255 QDiv := 1;
256 KDiv := Div;
257 elsif Div mod 2 = 0 then
258 -- divisible by 4
259 PDiv := 2;
260 QDiv := Div / 2;
261 KDiv := 2;
262 elsif Div mod 3 = 0 then
263 -- divisible by 6
264 PDiv := 3;
265 QDiv := Div / 3;
266 KDiv := 2;
267 elsif Div mod 7 = 0 then
268 -- divisible by 14
269 PDiv := 7;
270 QDiv := Div / 7;
271 KDiv := 2;
272 end if;
273 elsif Div = 7 or Div = 21 or Div = 35 then
274 -- 7, 21 and 35
275 PDiv := 7;
276 QDiv := 1;
277 KDiv := Div / 7;
278 elsif Div = 3 or Div = 9 or Div = 15 then
279 -- 3, 9 and 15
280 PDiv := 3;
281 QDiv := 1;
282 KDiv := Div / 3;
283 elsif Div = 5 then
284 -- 5
285 PDiv := 5;
286 QDiv := 1;
287 KDiv := 1;
288 end if;
289 end if;
290 end Calculate_DPLL;
291
292 ----------------------------------------------------------------------------
293
294 procedure On
295 (PLL : in Configurable_DPLLs;
296 Port_Cfg : in Port_Config;
297 Success : out Boolean)
298 is
299 Central_Freq : Central_Frequency;
300 DCO_Freq : DCO_Frequency;
301 PDiv : PDiv_Range;
302 QDiv : QDiv_Range;
303 KDiv : KDiv_Range;
304 begin
305 if Port_Cfg.Display = DP then
306 Registers.Unset_And_Set_Mask
307 (Register => Registers.DPLL_CTRL1,
308 Mask_Unset => DPLL_CTRL1_DPLLx (HDMI_MODE, PLL) or
309 DPLL_CTRL1_DPLLx (SSC, PLL) or
310 DPLL_CTRL1_DPLLx (LINK_RATE_MASK, PLL),
311 Mask_Set => DPLL_CTRL1_DPLLx (LINK_RATE
312 (Port_Cfg.DP.Bandwidth), PLL) or
313 DPLL_CTRL1_DPLLx (OVERRIDE, PLL));
314 Registers.Posting_Read (Registers.DPLL_CTRL1);
315 Success := True;
316 else
317 Calculate_DPLL
318 (Port_Cfg.Mode.Dotclock, Central_Freq, DCO_Freq, PDiv, QDiv, KDiv);
319 Success := Central_Freq /= CF_INVALID;
320 if Success then
321 Registers.Unset_And_Set_Mask
322 (Register => Registers.DPLL_CTRL1,
323 Mask_Unset => DPLL_CTRL1_DPLLx (SSC, PLL),
324 Mask_Set => DPLL_CTRL1_DPLLx (HDMI_MODE, PLL) or
325 DPLL_CTRL1_DPLLx (OVERRIDE, PLL));
326 Registers.Write
327 (Register => DPLL_CFGR1 (PLL),
328 Value => DPLL_CFGR1_FREQUENCY_ENABLE or
329 DPLL_CFGR1_DCO_FRACTION (DCO_Freq) or
330 DPLL_CFGR1_DCO_INTEGER (DCO_Freq));
331 Registers.Write
332 (Register => DPLL_CFGR2 (PLL),
333 Value => DPLL_CFGR2_QDIV (QDiv) or
334 DPLL_CFGR2_KDIV (KDiv) or
335 DPLL_CFGR2_PDIV (PDiv) or
336 DPLL_CFGR2_CENTRAL_FREQ (Central_Freq));
337 Registers.Posting_Read (Registers.DPLL_CTRL1);
338 Registers.Posting_Read (DPLL_CFGR1 (PLL));
339 Registers.Posting_Read (DPLL_CFGR2 (PLL));
340 end if;
341 end if;
342
343 if Success then
344 Registers.Write
345 (Register => DPLL_CTL (PLL),
346 Value => DPLL_CTL_PLL_ENABLE);
347 Registers.Wait_Set_Mask
348 (Register => Registers.DPLL_STATUS,
349 Mask => DPLL_STATUS_DPLLx_LOCK (PLL));
350 end if;
351 end On;
352
353 procedure Off (PLL : Configurable_DPLLs) is
354 begin
355 Registers.Unset_Mask (DPLL_CTL (PLL), DPLL_CTL_PLL_ENABLE);
356 end Off;
357
358end HW.GFX.GMA.PLLs.DPLL;