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