Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1029 | serge | 1 | /* |
2 | * Copyright 2007 Luc Verhaegen |
||
3 | * Copyright 2007 Matthias Hopf |
||
4 | * Copyright 2007 Egbert Eich |
||
5 | * Copyright 2007 Advanced Micro Devices, Inc. |
||
6 | * |
||
7 | * Permission is hereby granted, free of charge, to any person obtaining a |
||
8 | * copy of this software and associated documentation files (the "Software"), |
||
9 | * to deal in the Software without restriction, including without limitation |
||
10 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||
11 | * and/or sell copies of the Software, and to permit persons to whom the |
||
12 | * Software is furnished to do so, subject to the following conditions: |
||
13 | * |
||
14 | * The above copyright notice and this permission notice shall be included in |
||
15 | * all copies or substantial portions of the Software. |
||
16 | * |
||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||
20 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
||
21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
||
22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
||
23 | * OTHER DEALINGS IN THE SOFTWARE. |
||
24 | */ |
||
25 | |||
26 | #ifdef HAVE_CONFIG_H |
||
27 | #include "config.h" |
||
28 | #endif |
||
29 | |||
30 | #include "xf86.h" |
||
31 | |||
32 | /* for usleep */ |
||
33 | #if HAVE_XF86_ANSIC_H |
||
34 | # include "xf86_ansic.h" |
||
35 | #else |
||
36 | # include |
||
37 | #endif |
||
38 | |||
39 | #include "rhd.h" |
||
40 | #include "rhd_crtc.h" |
||
41 | #include "rhd_pll.h" |
||
42 | #include "rhd_regs.h" |
||
43 | #include "rhd_atombios.h" |
||
44 | |||
45 | |||
46 | #define PLL_CALIBRATE_WAIT 0x100000 |
||
47 | |||
48 | /* |
||
49 | * Get gain, charge pump, loop filter and current bias. |
||
50 | * For R500, this is done in atombios by ASIC_RegistersInit |
||
51 | * Some data table in atom should've provided this information. |
||
52 | */ |
||
53 | |||
54 | struct PLL_Control { |
||
55 | CARD16 FeedbackDivider; /* 0xFFFF/-1 is the endmarker here */ |
||
56 | CARD32 Control; |
||
57 | }; |
||
58 | |||
59 | /* From hardcoded values. */ |
||
60 | static struct PLL_Control RV610PLLControl[] = |
||
61 | { |
||
62 | { 0x0049, 0x159F8704 }, |
||
63 | { 0x006C, 0x159B8704 }, |
||
64 | { 0xFFFF, 0x159EC704 } |
||
65 | }; |
||
66 | |||
67 | /* Some tables are provided by atombios, |
||
68 | * it's just that they are hidden away deliberately and not exposed */ |
||
69 | static struct PLL_Control RV670PLLControl[] = |
||
70 | { |
||
71 | { 0x004A, 0x159FC704 }, |
||
72 | { 0x0067, 0x159BC704 }, |
||
73 | { 0x00C4, 0x159EC704 }, |
||
74 | { 0x00F4, 0x1593A704 }, |
||
75 | { 0x0136, 0x1595A704 }, |
||
76 | { 0x01A4, 0x1596A704 }, |
||
77 | { 0x022C, 0x159CE504 }, |
||
78 | { 0xFFFF, 0x1591E404 } |
||
79 | }; |
||
80 | |||
81 | /* |
||
82 | * Used by PLLElectrical() for r5xx+ and by rv620/35 code. |
||
83 | */ |
||
84 | static CARD32 |
||
85 | PLLControlTableRetrieve(struct PLL_Control *Table, CARD16 FeedbackDivider) |
||
86 | { |
||
87 | int i; |
||
88 | |||
89 | for (i = 0; Table[i].FeedbackDivider < 0xFFFF ; i++) |
||
90 | if (Table[i].FeedbackDivider >= FeedbackDivider) |
||
91 | break; |
||
92 | |||
93 | return Table[i].Control; |
||
94 | } |
||
95 | |||
96 | /* |
||
97 | * Not used by rv620/35 code. |
||
98 | */ |
||
99 | static CARD32 |
||
100 | PLLElectrical(RHDPtr rhdPtr, CARD16 FeedbackDivider) |
||
101 | { |
||
102 | switch (rhdPtr->ChipSet) { |
||
103 | case RHD_RV515: |
||
104 | if (rhdPtr->PciDeviceID == 0x7146) |
||
105 | return 0x00120704; |
||
106 | else |
||
107 | return 0; |
||
108 | case RHD_RV535: |
||
109 | if (rhdPtr->PciDeviceID == 0x71C1) |
||
110 | return 0x00230704; |
||
111 | else |
||
112 | return 0; |
||
113 | case RHD_RS600: |
||
114 | case RHD_RS690: |
||
115 | case RHD_RS740: |
||
116 | /* depending on MiscInfo also 0x00120004 */ |
||
117 | return 0x00120704; |
||
118 | case RHD_R600: |
||
119 | return 0x01130704; |
||
120 | case RHD_RV610: |
||
121 | case RHD_RV630: |
||
122 | case RHD_M72: |
||
123 | case RHD_M74: |
||
124 | case RHD_M76: |
||
125 | return PLLControlTableRetrieve(RV610PLLControl, FeedbackDivider); |
||
126 | case RHD_RV670: |
||
127 | case RHD_R680: |
||
128 | return PLLControlTableRetrieve(RV670PLLControl, FeedbackDivider); |
||
129 | default: |
||
130 | return 0; |
||
131 | } |
||
132 | } |
||
133 | |||
134 | /* |
||
135 | * All R500s, RS6x0, R600, RV610 and RV630. |
||
136 | */ |
||
137 | |||
138 | /* |
||
139 | * |
||
140 | */ |
||
141 | static void |
||
142 | PLL1Calibrate(struct rhdPLL *PLL) |
||
143 | { |
||
144 | int i; |
||
145 | |||
146 | RHDFUNC(PLL); |
||
147 | |||
148 | RHDRegMask(PLL, P1PLL_CNTL, 1, 0x01); /* Reset */ |
||
149 | usleep(2); |
||
150 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x01); /* Set */ |
||
151 | for (i = 0; i < PLL_CALIBRATE_WAIT; i++) |
||
152 | if (((RHDRegRead(PLL, P1PLL_CNTL) >> 20) & 0x03) == 0x03) |
||
153 | break; |
||
154 | |||
155 | if (i == PLL_CALIBRATE_WAIT) { |
||
156 | if (RHDRegRead(PLL, P1PLL_CNTL) & 0x00100000) /* Calibration done? */ |
||
157 | xf86DrvMsg(PLL->scrnIndex, X_ERROR, |
||
158 | "%s: Calibration failed.\n", __func__); |
||
159 | if (RHDRegRead(PLL, P1PLL_CNTL) & 0x00200000) /* PLL locked? */ |
||
160 | xf86DrvMsg(PLL->scrnIndex, X_ERROR, |
||
161 | "%s: Locking failed.\n", __func__); |
||
162 | } else |
||
163 | RHDDebug(PLL->scrnIndex, "%s: lock in %d loops\n", __func__, i); |
||
164 | } |
||
165 | |||
166 | /* |
||
167 | * |
||
168 | */ |
||
169 | static void |
||
170 | PLL2Calibrate(struct rhdPLL *PLL) |
||
171 | { |
||
172 | int i; |
||
173 | |||
174 | RHDFUNC(PLL); |
||
175 | |||
176 | RHDRegMask(PLL, P2PLL_CNTL, 1, 0x01); /* Reset */ |
||
177 | usleep(2); |
||
178 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x01); /* Set */ |
||
179 | |||
180 | for (i = 0; i < PLL_CALIBRATE_WAIT; i++) |
||
181 | if (((RHDRegRead(PLL, P2PLL_CNTL) >> 20) & 0x03) == 0x03) |
||
182 | break; |
||
183 | |||
184 | if (i == PLL_CALIBRATE_WAIT) { |
||
185 | if (RHDRegRead(PLL, P2PLL_CNTL) & 0x00100000) /* Calibration done? */ |
||
186 | xf86DrvMsg(PLL->scrnIndex, X_ERROR, |
||
187 | "%s: Calibration failed.\n", __func__); |
||
188 | if (RHDRegRead(PLL, P2PLL_CNTL) & 0x00200000) /* PLL locked? */ |
||
189 | xf86DrvMsg(PLL->scrnIndex, X_ERROR, |
||
190 | "%s: Locking failed.\n", __func__); |
||
191 | } else |
||
192 | RHDDebug(PLL->scrnIndex, "%s: lock in %d loops\n", __func__, i); |
||
193 | } |
||
194 | |||
195 | /* |
||
196 | * |
||
197 | */ |
||
198 | static void |
||
199 | R500PLL1Power(struct rhdPLL *PLL, int Power) |
||
200 | { |
||
201 | RHDFUNC(PLL); |
||
202 | |||
203 | switch (Power) { |
||
204 | case RHD_POWER_ON: |
||
205 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x02); /* Powah */ |
||
206 | usleep(2); |
||
207 | |||
208 | PLL1Calibrate(PLL); |
||
209 | |||
210 | return; |
||
211 | case RHD_POWER_RESET: |
||
212 | RHDRegMask(PLL, P1PLL_CNTL, 0x01, 0x01); /* Reset */ |
||
213 | usleep(2); |
||
214 | |||
215 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x02); /* Powah */ |
||
216 | usleep(2); |
||
217 | |||
218 | return; |
||
219 | case RHD_POWER_SHUTDOWN: |
||
220 | default: |
||
221 | RHDRegMask(PLL, P1PLL_CNTL, 0x01, 0x01); /* Reset */ |
||
222 | usleep(2); |
||
223 | |||
224 | RHDRegMask(PLL, P1PLL_CNTL, 0x02, 0x02); /* Power down */ |
||
225 | usleep(200); |
||
226 | |||
227 | return; |
||
228 | } |
||
229 | } |
||
230 | |||
231 | /* |
||
232 | * |
||
233 | */ |
||
234 | static void |
||
235 | R500PLL2Power(struct rhdPLL *PLL, int Power) |
||
236 | { |
||
237 | RHDFUNC(PLL); |
||
238 | |||
239 | switch (Power) { |
||
240 | case RHD_POWER_ON: |
||
241 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x02); /* Powah */ |
||
242 | usleep(2); |
||
243 | |||
244 | PLL2Calibrate(PLL); |
||
245 | |||
246 | return; |
||
247 | case RHD_POWER_RESET: |
||
248 | RHDRegMask(PLL, P2PLL_CNTL, 0x01, 0x01); /* Reset */ |
||
249 | usleep(2); |
||
250 | |||
251 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x02); /* Powah */ |
||
252 | usleep(2); |
||
253 | |||
254 | return; |
||
255 | case RHD_POWER_SHUTDOWN: |
||
256 | default: |
||
257 | RHDRegMask(PLL, P2PLL_CNTL, 0x01, 0x01); /* Reset */ |
||
258 | usleep(2); |
||
259 | |||
260 | RHDRegMask(PLL, P2PLL_CNTL, 0x02, 0x02); /* Power down */ |
||
261 | usleep(200); |
||
262 | |||
263 | return; |
||
264 | } |
||
265 | } |
||
266 | |||
267 | /* |
||
268 | * |
||
269 | */ |
||
270 | static void |
||
271 | R500PLL1SetLow(struct rhdPLL *PLL, CARD32 RefDiv, CARD32 FBDiv, CARD32 PostDiv, |
||
272 | CARD32 Control) |
||
273 | { |
||
274 | RHDFUNC(PLL); |
||
275 | RHDRegWrite(PLL, EXT1_PPLL_REF_DIV_SRC, 0x01); /* XTAL */ |
||
276 | RHDRegWrite(PLL, EXT1_PPLL_POST_DIV_SRC, 0x00); /* source = reference */ |
||
277 | |||
278 | RHDRegWrite(PLL, EXT1_PPLL_UPDATE_LOCK, 0x01); /* lock */ |
||
279 | |||
280 | RHDRegWrite(PLL, EXT1_PPLL_REF_DIV, RefDiv); |
||
281 | RHDRegWrite(PLL, EXT1_PPLL_FB_DIV, FBDiv); |
||
282 | RHDRegWrite(PLL, EXT1_PPLL_POST_DIV, PostDiv); |
||
283 | RHDRegWrite(PLL, EXT1_PPLL_CNTL, Control); |
||
284 | |||
285 | RHDRegMask(PLL, EXT1_PPLL_UPDATE_CNTL, 0x00010000, 0x00010000); /* no autoreset */ |
||
286 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x04); /* don't bypass calibration */ |
||
287 | |||
288 | /* We need to reset the anti glitch logic */ |
||
289 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x00000002); /* power up */ |
||
290 | |||
291 | /* reset anti glitch logic */ |
||
292 | RHDRegMask(PLL, P1PLL_CNTL, 0x00002000, 0x00002000); |
||
293 | usleep(2); |
||
294 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x00002000); |
||
295 | |||
296 | /* powerdown and reset */ |
||
297 | RHDRegMask(PLL, P1PLL_CNTL, 0x00000003, 0x00000003); |
||
298 | usleep(2); |
||
299 | |||
300 | RHDRegWrite(PLL, EXT1_PPLL_UPDATE_LOCK, 0); /* unlock */ |
||
301 | RHDRegMask(PLL, EXT1_PPLL_UPDATE_CNTL, 0, 0x01); /* we're done updating! */ |
||
302 | |||
303 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x02); /* Powah */ |
||
304 | usleep(2); |
||
305 | |||
306 | PLL1Calibrate(PLL); |
||
307 | |||
308 | RHDRegWrite(PLL, EXT1_PPLL_POST_DIV_SRC, 0x01); /* source is PLL itself */ |
||
309 | } |
||
310 | |||
311 | /* |
||
312 | * |
||
313 | */ |
||
314 | static void |
||
315 | R500PLL2SetLow(struct rhdPLL *PLL, CARD32 RefDiv, CARD32 FBDiv, CARD32 PostDiv, |
||
316 | CARD32 Control) |
||
317 | { |
||
318 | RHDRegWrite(PLL, EXT2_PPLL_REF_DIV_SRC, 0x01); /* XTAL */ |
||
319 | RHDRegWrite(PLL, EXT2_PPLL_POST_DIV_SRC, 0x00); /* source = reference */ |
||
320 | |||
321 | RHDRegWrite(PLL, EXT2_PPLL_UPDATE_LOCK, 0x01); /* lock */ |
||
322 | |||
323 | RHDRegWrite(PLL, EXT2_PPLL_REF_DIV, RefDiv); |
||
324 | RHDRegWrite(PLL, EXT2_PPLL_FB_DIV, FBDiv); |
||
325 | RHDRegWrite(PLL, EXT2_PPLL_POST_DIV, PostDiv); |
||
326 | RHDRegWrite(PLL, EXT2_PPLL_CNTL, Control); |
||
327 | |||
328 | RHDRegMask(PLL, EXT2_PPLL_UPDATE_CNTL, 0x00010000, 0x00010000); /* no autoreset */ |
||
329 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x04); /* don't bypass calibration */ |
||
330 | |||
331 | /* We need to reset the anti glitch logic */ |
||
332 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x00000002); /* power up */ |
||
333 | |||
334 | /* reset anti glitch logic */ |
||
335 | RHDRegMask(PLL, P2PLL_CNTL, 0x00002000, 0x00002000); |
||
336 | usleep(2); |
||
337 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x00002000); |
||
338 | |||
339 | /* powerdown and reset */ |
||
340 | RHDRegMask(PLL, P2PLL_CNTL, 0x00000003, 0x00000003); |
||
341 | usleep(2); |
||
342 | |||
343 | RHDRegWrite(PLL, EXT2_PPLL_UPDATE_LOCK, 0); /* unlock */ |
||
344 | RHDRegMask(PLL, EXT2_PPLL_UPDATE_CNTL, 0, 0x01); /* we're done updating! */ |
||
345 | |||
346 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x02); /* Powah */ |
||
347 | usleep(2); |
||
348 | |||
349 | PLL2Calibrate(PLL); |
||
350 | |||
351 | RHDRegWrite(PLL, EXT2_PPLL_POST_DIV_SRC, 0x01); /* source is PLL itself */ |
||
352 | } |
||
353 | |||
354 | /* |
||
355 | * The CRTC ownership of each PLL is multiplexed on the PLL blocks, and the |
||
356 | * ownership can only be switched when the currently referenced PLL is active. |
||
357 | * This makes handling a slight bit more complex. |
||
358 | */ |
||
359 | static void |
||
360 | R500PLLCRTCGrab(struct rhdPLL *PLL, Bool Crtc2) |
||
361 | { |
||
362 | CARD32 Stored; |
||
363 | Bool PLL2IsCurrent; |
||
364 | |||
365 | if (!Crtc2) { |
||
366 | PLL2IsCurrent = RHDRegRead(PLL, PCLK_CRTC1_CNTL) & 0x00010000; |
||
367 | |||
368 | if (PLL->Id == PLL_ID_PLL1) |
||
369 | RHDRegMask(PLL, PCLK_CRTC1_CNTL, 0, 0x00010000); |
||
370 | else |
||
371 | RHDRegMask(PLL, PCLK_CRTC1_CNTL, 0x00010000, 0x00010000); |
||
372 | } else { |
||
373 | PLL2IsCurrent = RHDRegRead(PLL, PCLK_CRTC2_CNTL) & 0x00010000; |
||
374 | |||
375 | if (PLL->Id == PLL_ID_PLL1) |
||
376 | RHDRegMask(PLL, PCLK_CRTC2_CNTL, 0, 0x00010000); |
||
377 | else |
||
378 | RHDRegMask(PLL, PCLK_CRTC2_CNTL, 0x00010000, 0x00010000); |
||
379 | } |
||
380 | |||
381 | /* if the current pll is not active, then poke it just enough to flip |
||
382 | * owners */ |
||
383 | if (!PLL2IsCurrent) { |
||
384 | Stored = RHDRegRead(PLL, P1PLL_CNTL); |
||
385 | |||
386 | if (Stored & 0x03) { |
||
387 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x03); |
||
388 | usleep(10); |
||
389 | RHDRegMask(PLL, P1PLL_CNTL, Stored, 0x03); |
||
390 | } |
||
391 | } else { |
||
392 | Stored = RHDRegRead(PLL, P2PLL_CNTL); |
||
393 | |||
394 | if (Stored & 0x03) { |
||
395 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x03); |
||
396 | usleep(10); |
||
397 | RHDRegMask(PLL, P2PLL_CNTL, Stored, 0x03); |
||
398 | } |
||
399 | } |
||
400 | } |
||
401 | |||
402 | /* |
||
403 | * |
||
404 | */ |
||
405 | static void |
||
406 | R500PLL1Set(struct rhdPLL *PLL, int PixelClock, CARD16 ReferenceDivider, |
||
407 | CARD16 FeedbackDivider, CARD8 PostDivider) |
||
408 | { |
||
409 | RHDPtr rhdPtr = RHDPTRI(PLL); |
||
410 | CARD32 RefDiv, FBDiv, PostDiv, Control; |
||
411 | |||
412 | RHDFUNC(PLL); |
||
413 | |||
414 | RefDiv = ReferenceDivider; |
||
415 | |||
416 | FBDiv = FeedbackDivider << 16; |
||
417 | |||
418 | if (rhdPtr->ChipSet > RHD_R600) { /* set up Feedbackdivider slip */ |
||
419 | if (FeedbackDivider <= 0x24) |
||
420 | FBDiv |= 0x00000030; |
||
421 | else if (FeedbackDivider <= 0x3F) |
||
422 | FBDiv |= 0x00000020; |
||
423 | } else if (rhdPtr->ChipSet >= RHD_RS600) /* RS600, RS690, R600 */ |
||
424 | FBDiv |= 0x00000030; |
||
425 | else |
||
426 | FBDiv |= RHDRegRead(PLL, EXT1_PPLL_FB_DIV) & 0x00000030; |
||
427 | |||
428 | PostDiv = RHDRegRead(PLL, EXT1_PPLL_POST_DIV) & ~0x0000007F; |
||
429 | PostDiv |= PostDivider & 0x0000007F; |
||
430 | |||
431 | Control = PLLElectrical(rhdPtr, FeedbackDivider); |
||
432 | if (!Control) |
||
433 | Control = RHDRegRead(PLL, EXT1_PPLL_CNTL); |
||
434 | |||
435 | /* Disable Spread Spectrum */ |
||
436 | RHDRegMask(PLL, P1PLL_INT_SS_CNTL, 0, 0x00000001); |
||
437 | |||
438 | R500PLL1SetLow(PLL, RefDiv, FBDiv, PostDiv, Control); |
||
439 | |||
440 | if (rhdPtr->Crtc[0]->PLL == PLL) |
||
441 | R500PLLCRTCGrab(PLL, FALSE); |
||
442 | if (rhdPtr->Crtc[1]->PLL == PLL) |
||
443 | R500PLLCRTCGrab(PLL, TRUE); |
||
444 | } |
||
445 | |||
446 | /* |
||
447 | * |
||
448 | */ |
||
449 | static void |
||
450 | R500PLL2Set(struct rhdPLL *PLL, int PixelClock, CARD16 ReferenceDivider, |
||
451 | CARD16 FeedbackDivider, CARD8 PostDivider) |
||
452 | { |
||
453 | RHDPtr rhdPtr = RHDPTRI(PLL); |
||
454 | CARD32 RefDiv, FBDiv, PostDiv, Control; |
||
455 | |||
456 | RHDFUNC(PLL); |
||
457 | |||
458 | RefDiv = ReferenceDivider; |
||
459 | |||
460 | FBDiv = FeedbackDivider << 16; |
||
461 | |||
462 | if (rhdPtr->ChipSet > RHD_R600) { /* set up Feedbackdivider slip */ |
||
463 | if (FeedbackDivider <= 0x24) |
||
464 | FBDiv |= 0x00000030; |
||
465 | else if (FeedbackDivider <= 0x3F) |
||
466 | FBDiv |= 0x00000020; |
||
467 | } else if (rhdPtr->ChipSet >= RHD_RS600) /* RS600, RS690, R600 */ |
||
468 | FBDiv |= 0x00000030; |
||
469 | else |
||
470 | FBDiv |= RHDRegRead(PLL, EXT2_PPLL_FB_DIV) & 0x00000030; |
||
471 | |||
472 | PostDiv = RHDRegRead(PLL, EXT2_PPLL_POST_DIV) & ~0x0000007F; |
||
473 | PostDiv |= PostDivider & 0x0000007F; |
||
474 | |||
475 | Control = PLLElectrical(rhdPtr, FeedbackDivider); |
||
476 | if (!Control) |
||
477 | Control = RHDRegRead(PLL, EXT2_PPLL_CNTL); |
||
478 | |||
479 | /* Disable Spread Spectrum */ |
||
480 | RHDRegMask(PLL, P2PLL_INT_SS_CNTL, 0, 0x00000001); |
||
481 | |||
482 | R500PLL2SetLow(PLL, RefDiv, FBDiv, PostDiv, Control); |
||
483 | |||
484 | if (rhdPtr->Crtc[0]->PLL == PLL) |
||
485 | R500PLLCRTCGrab(PLL, FALSE); |
||
486 | if (rhdPtr->Crtc[1]->PLL == PLL) |
||
487 | R500PLLCRTCGrab(PLL, TRUE); |
||
488 | } |
||
489 | |||
490 | /* |
||
491 | * |
||
492 | */ |
||
493 | static void |
||
494 | R500PLL1Save(struct rhdPLL *PLL) |
||
495 | { |
||
496 | RHDFUNC(PLL); |
||
497 | |||
498 | PLL->StoreActive = !(RHDRegRead(PLL, P1PLL_CNTL) & 0x03); |
||
499 | PLL->StoreRefDiv = RHDRegRead(PLL, EXT1_PPLL_REF_DIV); |
||
500 | PLL->StoreFBDiv = RHDRegRead(PLL, EXT1_PPLL_FB_DIV); |
||
501 | PLL->StorePostDiv = RHDRegRead(PLL, EXT1_PPLL_POST_DIV); |
||
502 | PLL->StoreControl = RHDRegRead(PLL, EXT1_PPLL_CNTL); |
||
503 | PLL->StoreSpreadSpectrum = RHDRegRead(PLL, P1PLL_INT_SS_CNTL); |
||
504 | PLL->StoreCrtc1Owner = !(RHDRegRead(PLL, PCLK_CRTC1_CNTL) & 0x00010000); |
||
505 | PLL->StoreCrtc2Owner = !(RHDRegRead(PLL, PCLK_CRTC2_CNTL) & 0x00010000); |
||
506 | |||
507 | PLL->Stored = TRUE; |
||
508 | } |
||
509 | |||
510 | /* |
||
511 | * |
||
512 | */ |
||
513 | static void |
||
514 | R500PLL2Save(struct rhdPLL *PLL) |
||
515 | { |
||
516 | RHDFUNC(PLL); |
||
517 | |||
518 | PLL->StoreActive = !(RHDRegRead(PLL, P2PLL_CNTL) & 0x03); |
||
519 | PLL->StoreRefDiv = RHDRegRead(PLL, EXT2_PPLL_REF_DIV); |
||
520 | PLL->StoreFBDiv = RHDRegRead(PLL, EXT2_PPLL_FB_DIV); |
||
521 | PLL->StorePostDiv = RHDRegRead(PLL, EXT2_PPLL_POST_DIV); |
||
522 | PLL->StoreControl = RHDRegRead(PLL, EXT2_PPLL_CNTL); |
||
523 | PLL->StoreSpreadSpectrum = RHDRegRead(PLL, P2PLL_INT_SS_CNTL); |
||
524 | PLL->StoreCrtc1Owner = RHDRegRead(PLL, PCLK_CRTC1_CNTL) & 0x00010000; |
||
525 | PLL->StoreCrtc2Owner = RHDRegRead(PLL, PCLK_CRTC2_CNTL) & 0x00010000; |
||
526 | |||
527 | PLL->Stored = TRUE; |
||
528 | } |
||
529 | |||
530 | /* |
||
531 | * |
||
532 | */ |
||
533 | static void |
||
534 | R500PLL1Restore(struct rhdPLL *PLL) |
||
535 | { |
||
536 | RHDFUNC(PLL); |
||
537 | |||
538 | if (!PLL->Stored) { |
||
539 | xf86DrvMsg(PLL->scrnIndex, X_ERROR, "%s: %s: trying to restore " |
||
540 | "uninitialized values.\n", __func__, PLL->Name); |
||
541 | return; |
||
542 | } |
||
543 | |||
544 | if (PLL->StoreActive) { |
||
545 | R500PLL1SetLow(PLL, PLL->StoreRefDiv, PLL->StoreFBDiv, |
||
546 | PLL->StorePostDiv, PLL->StoreControl); |
||
547 | |||
548 | /* HotFix: always keep spread spectrum disabled on restore */ |
||
549 | if (0 && RHDPTRI(PLL)->ChipSet != RHD_M54) |
||
550 | RHDRegMask(PLL, P1PLL_INT_SS_CNTL, |
||
551 | PLL->StoreSpreadSpectrum, 0x00000001); |
||
552 | } else { |
||
553 | PLL->Power(PLL, RHD_POWER_SHUTDOWN); |
||
554 | |||
555 | /* lame attempt at at least restoring the old values */ |
||
556 | RHDRegWrite(PLL, EXT1_PPLL_REF_DIV, PLL->StoreRefDiv); |
||
557 | RHDRegWrite(PLL, EXT1_PPLL_FB_DIV, PLL->StoreFBDiv); |
||
558 | RHDRegWrite(PLL, EXT1_PPLL_POST_DIV, PLL->StorePostDiv); |
||
559 | RHDRegWrite(PLL, EXT1_PPLL_CNTL, PLL->StoreControl); |
||
560 | RHDRegWrite(PLL, P1PLL_INT_SS_CNTL, PLL->StoreSpreadSpectrum); |
||
561 | } |
||
562 | |||
563 | if (PLL->StoreCrtc1Owner) |
||
564 | R500PLLCRTCGrab(PLL, FALSE); |
||
565 | if (PLL->StoreCrtc2Owner) |
||
566 | R500PLLCRTCGrab(PLL, TRUE); |
||
567 | } |
||
568 | |||
569 | /* |
||
570 | * |
||
571 | */ |
||
572 | static void |
||
573 | R500PLL2Restore(struct rhdPLL *PLL) |
||
574 | { |
||
575 | RHDFUNC(PLL); |
||
576 | |||
577 | if (!PLL->Stored) { |
||
578 | xf86DrvMsg(PLL->scrnIndex, X_ERROR, "%s: %s: trying to restore " |
||
579 | "uninitialized values.\n", __func__, PLL->Name); |
||
580 | return; |
||
581 | } |
||
582 | |||
583 | if (PLL->StoreActive) { |
||
584 | R500PLL2SetLow(PLL, PLL->StoreRefDiv, PLL->StoreFBDiv, |
||
585 | PLL->StorePostDiv, PLL->StoreControl); |
||
586 | |||
587 | if (RHDPTRI(PLL)->ChipSet != RHD_M54) |
||
588 | RHDRegMask(PLL, P2PLL_INT_SS_CNTL, |
||
589 | PLL->StoreSpreadSpectrum, 0x00000001); |
||
590 | } else { |
||
591 | PLL->Power(PLL, RHD_POWER_SHUTDOWN); |
||
592 | |||
593 | /* lame attempt at at least restoring the old values */ |
||
594 | RHDRegWrite(PLL, EXT2_PPLL_REF_DIV, PLL->StoreRefDiv); |
||
595 | RHDRegWrite(PLL, EXT2_PPLL_FB_DIV, PLL->StoreFBDiv); |
||
596 | RHDRegWrite(PLL, EXT2_PPLL_POST_DIV, PLL->StorePostDiv); |
||
597 | RHDRegWrite(PLL, EXT2_PPLL_CNTL, PLL->StoreControl); |
||
598 | RHDRegWrite(PLL, P2PLL_INT_SS_CNTL, PLL->StoreSpreadSpectrum); |
||
599 | } |
||
600 | |||
601 | if (PLL->StoreCrtc1Owner) |
||
602 | R500PLLCRTCGrab(PLL, FALSE); |
||
603 | if (PLL->StoreCrtc2Owner) |
||
604 | R500PLLCRTCGrab(PLL, TRUE); |
||
605 | } |
||
606 | |||
607 | /* |
||
608 | * RV620 and up |
||
609 | */ |
||
610 | |||
611 | /* |
||
612 | * |
||
613 | */ |
||
614 | #define RV620_DCCGCLK_RESET 0 |
||
615 | #define RV620_DCCGCLK_GRAB 1 |
||
616 | #define RV620_DCCGCLK_RELEASE 2 |
||
617 | |||
618 | /* |
||
619 | * I still have no idea what DCCG stands for and why it needs to hook off some |
||
620 | * pixelclock... |
||
621 | */ |
||
622 | static void |
||
623 | RV620DCCGCLKSet(struct rhdPLL *PLL, int set) |
||
624 | { |
||
625 | CARD32 tmp; |
||
626 | |||
627 | RHDFUNC(PLL); |
||
628 | |||
629 | switch(set) { |
||
630 | case RV620_DCCGCLK_GRAB: |
||
631 | if (PLL->Id == PLL_ID_PLL1) |
||
632 | RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 0, 0x00000003); |
||
633 | else if (PLL->Id == PLL_ID_PLL2) |
||
634 | RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 1, 0x00000003); |
||
635 | else |
||
636 | RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003); |
||
637 | break; |
||
638 | case RV620_DCCGCLK_RELEASE: |
||
639 | tmp = RHDRegRead(PLL, DCCG_DISP_CLK_SRCSEL) & 0x03; |
||
640 | |||
641 | if ((PLL->Id == PLL_ID_PLL1) && (tmp == 0)) { |
||
642 | /* set to other PLL or external */ |
||
643 | tmp = RHDRegRead(PLL, P2PLL_CNTL); |
||
644 | if (!(tmp & 0x03) && /* powered and not in reset */ |
||
645 | ((tmp & 0x00300000) == 0x00300000)) /* calibrated and locked */ |
||
646 | RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 1, 0x00000003); |
||
647 | else |
||
648 | RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003); |
||
649 | } else if ((PLL->Id == PLL_ID_PLL2) && (tmp == 1)) { |
||
650 | /* set to other PLL or external */ |
||
651 | tmp = RHDRegRead(PLL, P1PLL_CNTL); |
||
652 | if (!(tmp & 0x03) && /* powered and not in reset */ |
||
653 | ((tmp & 0x00300000) == 0x00300000)) /* calibrated and locked */ |
||
654 | RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 0, 0x00000003); |
||
655 | else |
||
656 | RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003); |
||
657 | |||
658 | } /* no other action needs to be taken */ |
||
659 | break; |
||
660 | case RV620_DCCGCLK_RESET: |
||
661 | tmp = RHDRegRead(PLL, DCCG_DISP_CLK_SRCSEL) & 0x03; |
||
662 | |||
663 | if (((PLL->Id == PLL_ID_PLL1) && (tmp == 0)) || |
||
664 | ((PLL->Id == PLL_ID_PLL2) && (tmp == 1))) |
||
665 | RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003); |
||
666 | break; |
||
667 | default: |
||
668 | break; |
||
669 | } |
||
670 | } |
||
671 | |||
672 | /* |
||
673 | * |
||
674 | */ |
||
675 | static Bool |
||
676 | RV620DCCGCLKAvailable(struct rhdPLL *PLL) |
||
677 | { |
||
678 | CARD32 Dccg = RHDRegRead(PLL, DCCG_DISP_CLK_SRCSEL) & 0x03; |
||
679 | |||
680 | RHDFUNC(PLL); |
||
681 | |||
682 | if (Dccg & 0x02) |
||
683 | return TRUE; |
||
684 | |||
685 | if ((PLL->Id == PLL_ID_PLL1) && (Dccg == 0)) |
||
686 | return TRUE; |
||
687 | if ((PLL->Id == PLL_ID_PLL2) && (Dccg == 1)) |
||
688 | return TRUE; |
||
689 | |||
690 | return FALSE; |
||
691 | } |
||
692 | |||
693 | /* |
||
694 | * |
||
695 | */ |
||
696 | static void |
||
697 | RV620PLL1Power(struct rhdPLL *PLL, int Power) |
||
698 | { |
||
699 | RHDFUNC(PLL); |
||
700 | |||
701 | switch (Power) { |
||
702 | case RHD_POWER_ON: |
||
703 | { |
||
704 | Bool HasDccg = RV620DCCGCLKAvailable(PLL); |
||
705 | |||
706 | if (HasDccg) |
||
707 | RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RESET); |
||
708 | |||
709 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x02); /* Powah */ |
||
710 | usleep(2); |
||
711 | |||
712 | PLL1Calibrate(PLL); |
||
713 | |||
714 | if (HasDccg) |
||
715 | RV620DCCGCLKSet(PLL, RV620_DCCGCLK_GRAB); |
||
716 | return; |
||
717 | } |
||
718 | case RHD_POWER_RESET: |
||
719 | RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RELEASE); |
||
720 | |||
721 | RHDRegMask(PLL, P1PLL_CNTL, 0x01, 0x01); /* Reset */ |
||
722 | usleep(2); |
||
723 | |||
724 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x02); /* Powah */ |
||
725 | usleep(2); |
||
726 | |||
727 | return; |
||
728 | case RHD_POWER_SHUTDOWN: |
||
729 | default: |
||
730 | RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RELEASE); |
||
731 | |||
732 | RHDRegMask(PLL, P1PLL_CNTL, 0x01, 0x01); /* Reset */ |
||
733 | usleep(2); |
||
734 | |||
735 | RHDRegMask(PLL, P1PLL_CNTL, 0x02, 0x02); /* Power down */ |
||
736 | usleep(200); |
||
737 | |||
738 | return; |
||
739 | } |
||
740 | } |
||
741 | |||
742 | /* |
||
743 | * |
||
744 | */ |
||
745 | static void |
||
746 | RV620PLL2Power(struct rhdPLL *PLL, int Power) |
||
747 | { |
||
748 | RHDFUNC(PLL); |
||
749 | |||
750 | switch (Power) { |
||
751 | case RHD_POWER_ON: |
||
752 | { |
||
753 | Bool HasDccg = RV620DCCGCLKAvailable(PLL); |
||
754 | |||
755 | if (HasDccg) |
||
756 | RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RESET); |
||
757 | |||
758 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x02); /* Powah */ |
||
759 | usleep(2); |
||
760 | |||
761 | PLL2Calibrate(PLL); |
||
762 | |||
763 | if (HasDccg) |
||
764 | RV620DCCGCLKSet(PLL, RV620_DCCGCLK_GRAB); |
||
765 | return; |
||
766 | } |
||
767 | case RHD_POWER_RESET: |
||
768 | RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RELEASE); |
||
769 | |||
770 | RHDRegMask(PLL, P2PLL_CNTL, 0x01, 0x01); /* Reset */ |
||
771 | usleep(2); |
||
772 | |||
773 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x02); /* Powah */ |
||
774 | usleep(2); |
||
775 | |||
776 | return; |
||
777 | case RHD_POWER_SHUTDOWN: |
||
778 | default: |
||
779 | RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RELEASE); |
||
780 | |||
781 | RHDRegMask(PLL, P2PLL_CNTL, 0x01, 0x01); /* Reset */ |
||
782 | usleep(2); |
||
783 | |||
784 | RHDRegMask(PLL, P2PLL_CNTL, 0x02, 0x02); /* Power down */ |
||
785 | usleep(200); |
||
786 | |||
787 | return; |
||
788 | } |
||
789 | } |
||
790 | |||
791 | /* |
||
792 | * |
||
793 | */ |
||
794 | static void |
||
795 | RV620PLL1SetLow(struct rhdPLL *PLL, CARD32 RefDiv, CARD32 FBDiv, CARD32 PostDiv, |
||
796 | CARD8 ScalerDiv, CARD8 SymPostDiv, CARD32 Control) |
||
797 | { |
||
798 | RHDFUNC(PLL); |
||
799 | |||
800 | /* switch to external */ |
||
801 | RHDRegWrite(PLL, EXT1_PPLL_POST_DIV_SRC, 0); |
||
802 | RHDRegMask(PLL, P1PLL_DISP_CLK_CNTL, 0x00000200, 0x00000300); |
||
803 | RHDRegMask(PLL, EXT1_SYM_PPLL_POST_DIV, 0, 0x00000100); |
||
804 | |||
805 | RHDRegMask(PLL, P1PLL_CNTL, 0x00000001, 0x00000001); /* reset */ |
||
806 | usleep(2); |
||
807 | RHDRegMask(PLL, P1PLL_CNTL, 0x00000002, 0x00000002); /* power down */ |
||
808 | usleep(10); |
||
809 | RHDRegMask(PLL, P1PLL_CNTL, 0x00002000, 0x00002000); /* reset anti-glitch */ |
||
810 | |||
811 | RHDRegWrite(PLL, EXT1_PPLL_CNTL, Control); |
||
812 | |||
813 | RHDRegMask(PLL, P1PLL_DISP_CLK_CNTL, ScalerDiv, 0x0000003F); |
||
814 | |||
815 | RHDRegWrite(PLL, EXT1_PPLL_UPDATE_LOCK, 1); /* lock */ |
||
816 | |||
817 | RHDRegWrite(PLL, EXT1_PPLL_POST_DIV_SRC, 0x00000001); |
||
818 | |||
819 | RHDRegWrite(PLL, EXT1_PPLL_REF_DIV, RefDiv); |
||
820 | RHDRegWrite(PLL, EXT1_PPLL_FB_DIV, FBDiv); |
||
821 | RHDRegMask(PLL, EXT1_PPLL_POST_DIV, PostDiv, 0x0000007F); |
||
822 | RHDRegMask(PLL, EXT1_SYM_PPLL_POST_DIV, SymPostDiv, 0x0000007F); |
||
823 | |||
824 | usleep(10); |
||
825 | RHDRegWrite(PLL, EXT1_PPLL_UPDATE_LOCK, 0); /* unlock */ |
||
826 | |||
827 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x00000002); /* power up */ |
||
828 | usleep(10); |
||
829 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x00002000); /* undo reset anti-glitch */ |
||
830 | |||
831 | PLL1Calibrate(PLL); |
||
832 | |||
833 | /* switch back to the pll */ |
||
834 | RHDRegMask(PLL, P1PLL_DISP_CLK_CNTL, 0, 0x00000300); |
||
835 | RHDRegMask(PLL, EXT1_SYM_PPLL_POST_DIV, 0x00000100, 0x00000100); |
||
836 | RHDRegWrite(PLL, EXT1_PPLL_POST_DIV_SRC, 0x00000001); |
||
837 | |||
838 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x80000000); /* new and undocumented */ |
||
839 | } |
||
840 | |||
841 | /* |
||
842 | * |
||
843 | */ |
||
844 | static void |
||
845 | RV620PLL2SetLow(struct rhdPLL *PLL, CARD32 RefDiv, CARD32 FBDiv, CARD32 PostDiv, |
||
846 | CARD8 ScalerDiv, CARD8 SymPostDiv, CARD32 Control) |
||
847 | { |
||
848 | RHDFUNC(PLL); |
||
849 | |||
850 | /* switch to external */ |
||
851 | RHDRegWrite(PLL, EXT2_PPLL_POST_DIV_SRC, 0); |
||
852 | RHDRegMask(PLL, P2PLL_DISP_CLK_CNTL, 0x00000200, 0x00000300); |
||
853 | RHDRegMask(PLL, EXT2_SYM_PPLL_POST_DIV, 0, 0x00000100); |
||
854 | |||
855 | RHDRegMask(PLL, P2PLL_CNTL, 0x00000001, 0x00000001); /* reset */ |
||
856 | usleep(2); |
||
857 | RHDRegMask(PLL, P2PLL_CNTL, 0x00000002, 0x00000002); /* power down */ |
||
858 | usleep(10); |
||
859 | RHDRegMask(PLL, P2PLL_CNTL, 0x00002000, 0x00002000); /* reset anti-glitch */ |
||
860 | |||
861 | RHDRegWrite(PLL, EXT2_PPLL_CNTL, Control); |
||
862 | |||
863 | RHDRegMask(PLL, P2PLL_DISP_CLK_CNTL, ScalerDiv, 0x0000003F); |
||
864 | |||
865 | RHDRegWrite(PLL, EXT2_PPLL_UPDATE_LOCK, 1); /* lock */ |
||
866 | |||
867 | RHDRegWrite(PLL, EXT2_PPLL_POST_DIV_SRC, 0x00000001); |
||
868 | |||
869 | RHDRegWrite(PLL, EXT2_PPLL_REF_DIV, RefDiv); |
||
870 | RHDRegWrite(PLL, EXT2_PPLL_FB_DIV, FBDiv); |
||
871 | RHDRegMask(PLL, EXT2_PPLL_POST_DIV, PostDiv, 0x0000007F); |
||
872 | RHDRegMask(PLL, EXT2_SYM_PPLL_POST_DIV, SymPostDiv, 0x0000007F); |
||
873 | |||
874 | usleep(10); |
||
875 | RHDRegWrite(PLL, EXT2_PPLL_UPDATE_LOCK, 0); /* unlock */ |
||
876 | |||
877 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x00000002); /* power up */ |
||
878 | usleep(10); |
||
879 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x00002000); /* undo reset anti-glitch */ |
||
880 | |||
881 | PLL2Calibrate(PLL); |
||
882 | |||
883 | /* switch back to the pll */ |
||
884 | RHDRegMask(PLL, P2PLL_DISP_CLK_CNTL, 0, 0x00000300); |
||
885 | RHDRegMask(PLL, EXT2_SYM_PPLL_POST_DIV, 0x00000100, 0x00000100); |
||
886 | RHDRegWrite(PLL, EXT2_PPLL_POST_DIV_SRC, 0x00000001); |
||
887 | |||
888 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x80000000); /* new and undocumented */ |
||
889 | } |
||
890 | |||
891 | /* |
||
892 | * |
||
893 | */ |
||
894 | static void |
||
895 | RV620PLL1Set(struct rhdPLL *PLL, int PixelClock, CARD16 ReferenceDivider, |
||
896 | CARD16 FeedbackDivider, CARD8 PostDivider) |
||
897 | { |
||
898 | RHDPtr rhdPtr = RHDPTRI(PLL); |
||
899 | Bool HasDccg = RV620DCCGCLKAvailable(PLL); |
||
900 | CARD32 RefDiv, FBDiv, PostDiv, Control; |
||
901 | CARD8 ScalerDiv, SymPostDiv; |
||
902 | |||
903 | RHDFUNC(PLL); |
||
904 | |||
905 | if (HasDccg) |
||
906 | RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RESET); |
||
907 | |||
908 | /* Disable Spread Spectrum */ |
||
909 | RHDRegMask(PLL, P1PLL_INT_SS_CNTL, 0, 0x00000001); |
||
910 | |||
911 | RefDiv = ReferenceDivider; |
||
912 | |||
913 | FBDiv = RHDRegRead(PLL, EXT1_PPLL_FB_DIV) & ~0x07FF003F; |
||
914 | FBDiv |= ((FeedbackDivider << 16) | 0x0030) & 0x07FF003F; |
||
915 | |||
916 | PostDiv = RHDRegRead(PLL, EXT1_PPLL_POST_DIV) & ~0x0000007F; |
||
917 | PostDiv |= PostDivider & 0x0000007F; |
||
918 | |||
919 | /* introduce flags for this, like on unichrome */ |
||
920 | ScalerDiv = 2; /* scaler post divider, 4 for UPDP */ |
||
921 | |||
922 | SymPostDiv = PostDivider & 0x0000007F; |
||
923 | |||
924 | Control = PLLControlTableRetrieve(RV670PLLControl, FeedbackDivider); |
||
925 | |||
926 | RV620PLL1SetLow(PLL, RefDiv, FBDiv, PostDiv, ScalerDiv, SymPostDiv, |
||
927 | Control); |
||
928 | |||
929 | if (rhdPtr->Crtc[0]->PLL == PLL) |
||
930 | R500PLLCRTCGrab(PLL, FALSE); |
||
931 | if (rhdPtr->Crtc[1]->PLL == PLL) |
||
932 | R500PLLCRTCGrab(PLL, TRUE); |
||
933 | |||
934 | if (HasDccg) |
||
935 | RV620DCCGCLKSet(PLL, RV620_DCCGCLK_GRAB); |
||
936 | } |
||
937 | |||
938 | /* |
||
939 | * |
||
940 | */ |
||
941 | static void |
||
942 | RV620PLL2Set(struct rhdPLL *PLL, int PixelClock, CARD16 ReferenceDivider, |
||
943 | CARD16 FeedbackDivider, CARD8 PostDivider) |
||
944 | { |
||
945 | RHDPtr rhdPtr = RHDPTRI(PLL); |
||
946 | Bool HasDccg = RV620DCCGCLKAvailable(PLL); |
||
947 | CARD32 RefDiv, FBDiv, PostDiv, Control; |
||
948 | CARD8 ScalerDiv, SymPostDiv; |
||
949 | |||
950 | RHDFUNC(PLL); |
||
951 | |||
952 | if (HasDccg) |
||
953 | RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RESET); |
||
954 | |||
955 | /* Disable Spread Spectrum */ |
||
956 | RHDRegMask(PLL, P2PLL_INT_SS_CNTL, 0, 0x00000001); |
||
957 | |||
958 | RefDiv = ReferenceDivider; |
||
959 | |||
960 | FBDiv = RHDRegRead(PLL, EXT2_PPLL_FB_DIV) & ~0x07FF003F; |
||
961 | FBDiv |= ((FeedbackDivider << 16) | 0x0030) & 0x07FF003F; |
||
962 | |||
963 | PostDiv = RHDRegRead(PLL, EXT2_PPLL_POST_DIV) & ~0x0000007F; |
||
964 | PostDiv |= PostDivider & 0x0000007F; |
||
965 | |||
966 | /* introduce flags for this, like on unichrome */ |
||
967 | ScalerDiv = 2; /* scaler post divider, 4 for UPDP */ |
||
968 | |||
969 | SymPostDiv = PostDivider & 0x0000007F; |
||
970 | |||
971 | Control = PLLControlTableRetrieve(RV670PLLControl, FeedbackDivider); |
||
972 | |||
973 | RV620PLL2SetLow(PLL, RefDiv, FBDiv, PostDiv, ScalerDiv, SymPostDiv, |
||
974 | Control); |
||
975 | |||
976 | if (rhdPtr->Crtc[0]->PLL == PLL) |
||
977 | R500PLLCRTCGrab(PLL, FALSE); |
||
978 | if (rhdPtr->Crtc[1]->PLL == PLL) |
||
979 | R500PLLCRTCGrab(PLL, TRUE); |
||
980 | |||
981 | if (HasDccg) |
||
982 | RV620DCCGCLKSet(PLL, RV620_DCCGCLK_GRAB); |
||
983 | } |
||
984 | |||
985 | /* |
||
986 | * |
||
987 | */ |
||
988 | static void |
||
989 | RV620PLL1Save(struct rhdPLL *PLL) |
||
990 | { |
||
991 | RHDFUNC(PLL); |
||
992 | |||
993 | PLL->StoreActive = !(RHDRegRead(PLL, P1PLL_CNTL) & 0x03); |
||
994 | PLL->StoreRefDiv = RHDRegRead(PLL, EXT1_PPLL_REF_DIV); |
||
995 | PLL->StoreFBDiv = RHDRegRead(PLL, EXT1_PPLL_FB_DIV); |
||
996 | PLL->StorePostDiv = RHDRegRead(PLL, EXT1_PPLL_POST_DIV); |
||
997 | PLL->StorePostDivSrc = RHDRegRead(PLL, EXT1_PPLL_POST_DIV_SRC); |
||
998 | PLL->StoreControl = RHDRegRead(PLL, EXT1_PPLL_CNTL); |
||
999 | PLL->StoreSpreadSpectrum = RHDRegRead(PLL, P1PLL_INT_SS_CNTL); |
||
1000 | |||
1001 | PLL->StoreGlitchReset = RHDRegRead(PLL, P1PLL_CNTL) & 0x00002000; |
||
1002 | |||
1003 | PLL->StoreScalerPostDiv = RHDRegRead(PLL, P1PLL_DISP_CLK_CNTL) & 0x003F; |
||
1004 | PLL->StoreSymPostDiv = RHDRegRead(PLL, EXT1_SYM_PPLL_POST_DIV) & 0x007F; |
||
1005 | |||
1006 | PLL->StoreCrtc1Owner = !(RHDRegRead(PLL, PCLK_CRTC1_CNTL) & 0x00010000); |
||
1007 | PLL->StoreCrtc2Owner = !(RHDRegRead(PLL, PCLK_CRTC2_CNTL) & 0x00010000); |
||
1008 | |||
1009 | PLL->StoreDCCGCLKOwner = RV620DCCGCLKAvailable(PLL); |
||
1010 | if (PLL->StoreDCCGCLKOwner) |
||
1011 | PLL->StoreDCCGCLK = RHDRegRead(PLL, DCCG_DISP_CLK_SRCSEL); |
||
1012 | else |
||
1013 | PLL->StoreDCCGCLK = 0; |
||
1014 | |||
1015 | PLL->Stored = TRUE; |
||
1016 | } |
||
1017 | |||
1018 | /* |
||
1019 | * |
||
1020 | */ |
||
1021 | static void |
||
1022 | RV620PLL2Save(struct rhdPLL *PLL) |
||
1023 | { |
||
1024 | RHDFUNC(PLL); |
||
1025 | |||
1026 | PLL->StoreActive = !(RHDRegRead(PLL, P2PLL_CNTL) & 0x03); |
||
1027 | PLL->StoreRefDiv = RHDRegRead(PLL, EXT2_PPLL_REF_DIV); |
||
1028 | PLL->StoreFBDiv = RHDRegRead(PLL, EXT2_PPLL_FB_DIV); |
||
1029 | PLL->StorePostDiv = RHDRegRead(PLL, EXT2_PPLL_POST_DIV); |
||
1030 | PLL->StorePostDivSrc = RHDRegRead(PLL, EXT2_PPLL_POST_DIV_SRC); |
||
1031 | PLL->StoreControl = RHDRegRead(PLL, EXT2_PPLL_CNTL); |
||
1032 | PLL->StoreSpreadSpectrum = RHDRegRead(PLL, P2PLL_INT_SS_CNTL); |
||
1033 | |||
1034 | PLL->StoreGlitchReset = RHDRegRead(PLL, P2PLL_CNTL) & 0x00002000; |
||
1035 | |||
1036 | PLL->StoreScalerPostDiv = RHDRegRead(PLL, P2PLL_DISP_CLK_CNTL) & 0x003F; |
||
1037 | PLL->StoreSymPostDiv = RHDRegRead(PLL, EXT2_SYM_PPLL_POST_DIV) & 0x007F; |
||
1038 | |||
1039 | PLL->StoreCrtc1Owner = RHDRegRead(PLL, PCLK_CRTC1_CNTL) & 0x00010000; |
||
1040 | PLL->StoreCrtc2Owner = RHDRegRead(PLL, PCLK_CRTC2_CNTL) & 0x00010000; |
||
1041 | |||
1042 | PLL->StoreDCCGCLKOwner = RV620DCCGCLKAvailable(PLL); |
||
1043 | if (PLL->StoreDCCGCLKOwner) |
||
1044 | PLL->StoreDCCGCLK = RHDRegRead(PLL, DCCG_DISP_CLK_SRCSEL); |
||
1045 | else |
||
1046 | PLL->StoreDCCGCLK = 0; |
||
1047 | |||
1048 | PLL->Stored = TRUE; |
||
1049 | } |
||
1050 | |||
1051 | /* |
||
1052 | * Notice how we handle the DCCG ownership here. There is a difference between |
||
1053 | * currently holding the DCCG and what was held when in the VT. With the |
||
1054 | * solution here we no longer hardlock, but we do have the danger of keeping |
||
1055 | * the DCCG in external mode for too long a time, if both PLL restores are |
||
1056 | * too far apart. This is currently not an issue as VT restoration goes over |
||
1057 | * the whole device in one go anyway; no partial restoration going on |
||
1058 | */ |
||
1059 | static void |
||
1060 | RV620PLL1Restore(struct rhdPLL *PLL) |
||
1061 | { |
||
1062 | RHDFUNC(PLL); |
||
1063 | |||
1064 | if (RV620DCCGCLKAvailable(PLL)) |
||
1065 | RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 0x03, 0x00000003); |
||
1066 | |||
1067 | if (PLL->StoreActive) { |
||
1068 | RV620PLL1SetLow(PLL, PLL->StoreRefDiv, PLL->StoreFBDiv, |
||
1069 | PLL->StorePostDiv, PLL->StoreScalerPostDiv, |
||
1070 | PLL->StoreSymPostDiv, PLL->StoreControl); |
||
1071 | RHDRegMask(PLL, P1PLL_INT_SS_CNTL, |
||
1072 | PLL->StoreSpreadSpectrum, 0x00000001); |
||
1073 | |||
1074 | if (PLL->StoreDCCGCLKOwner) |
||
1075 | RHDRegWrite(PLL, DCCG_DISP_CLK_SRCSEL, PLL->StoreDCCGCLK); |
||
1076 | |||
1077 | } else { |
||
1078 | PLL->Power(PLL, RHD_POWER_SHUTDOWN); |
||
1079 | |||
1080 | /* lame attempt at at least restoring the old values */ |
||
1081 | RHDRegWrite(PLL, EXT1_PPLL_REF_DIV, PLL->StoreRefDiv); |
||
1082 | RHDRegWrite(PLL, EXT1_PPLL_FB_DIV, PLL->StoreFBDiv); |
||
1083 | RHDRegWrite(PLL, EXT1_PPLL_POST_DIV, PLL->StorePostDiv); |
||
1084 | RHDRegWrite(PLL, EXT1_PPLL_POST_DIV_SRC, PLL->StorePostDivSrc); |
||
1085 | RHDRegWrite(PLL, EXT1_PPLL_CNTL, PLL->StoreControl); |
||
1086 | RHDRegMask(PLL, P1PLL_DISP_CLK_CNTL, PLL->StoreScalerPostDiv, 0x003F); |
||
1087 | RHDRegMask(PLL, EXT1_SYM_PPLL_POST_DIV, PLL->StoreSymPostDiv, 0x007F); |
||
1088 | RHDRegWrite(PLL, P1PLL_INT_SS_CNTL, PLL->StoreSpreadSpectrum); |
||
1089 | |||
1090 | if (PLL->StoreGlitchReset) |
||
1091 | RHDRegMask(PLL, P1PLL_CNTL, 0x00002000, 0x00002000); |
||
1092 | else |
||
1093 | RHDRegMask(PLL, P1PLL_CNTL, 0, 0x00002000); |
||
1094 | } |
||
1095 | |||
1096 | if (PLL->StoreCrtc1Owner) |
||
1097 | R500PLLCRTCGrab(PLL, FALSE); |
||
1098 | if (PLL->StoreCrtc2Owner) |
||
1099 | R500PLLCRTCGrab(PLL, TRUE); |
||
1100 | |||
1101 | if (PLL->StoreDCCGCLKOwner) |
||
1102 | RHDRegWrite(PLL, DCCG_DISP_CLK_SRCSEL, PLL->StoreDCCGCLK); |
||
1103 | } |
||
1104 | |||
1105 | /* |
||
1106 | * |
||
1107 | */ |
||
1108 | static void |
||
1109 | RV620PLL2Restore(struct rhdPLL *PLL) |
||
1110 | { |
||
1111 | RHDFUNC(PLL); |
||
1112 | |||
1113 | if (RV620DCCGCLKAvailable(PLL)) |
||
1114 | RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 0x03, 0x00000003); |
||
1115 | |||
1116 | if (PLL->StoreActive) { |
||
1117 | RV620PLL2SetLow(PLL, PLL->StoreRefDiv, PLL->StoreFBDiv, |
||
1118 | PLL->StorePostDiv, PLL->StoreScalerPostDiv, |
||
1119 | PLL->StoreSymPostDiv, PLL->StoreControl); |
||
1120 | RHDRegMask(PLL, P2PLL_INT_SS_CNTL, |
||
1121 | PLL->StoreSpreadSpectrum, 0x00000001); |
||
1122 | } else { |
||
1123 | PLL->Power(PLL, RHD_POWER_SHUTDOWN); |
||
1124 | |||
1125 | /* lame attempt at at least restoring the old values */ |
||
1126 | RHDRegWrite(PLL, EXT2_PPLL_REF_DIV, PLL->StoreRefDiv); |
||
1127 | RHDRegWrite(PLL, EXT2_PPLL_FB_DIV, PLL->StoreFBDiv); |
||
1128 | RHDRegWrite(PLL, EXT2_PPLL_POST_DIV, PLL->StorePostDiv); |
||
1129 | RHDRegWrite(PLL, EXT2_PPLL_POST_DIV_SRC, PLL->StorePostDivSrc); |
||
1130 | RHDRegWrite(PLL, EXT2_PPLL_CNTL, PLL->StoreControl); |
||
1131 | RHDRegMask(PLL, P2PLL_DISP_CLK_CNTL, PLL->StoreScalerPostDiv, 0x003F); |
||
1132 | RHDRegMask(PLL, EXT2_SYM_PPLL_POST_DIV, PLL->StoreSymPostDiv, 0x007F); |
||
1133 | RHDRegWrite(PLL, P2PLL_INT_SS_CNTL, PLL->StoreSpreadSpectrum); |
||
1134 | |||
1135 | if (PLL->StoreGlitchReset) |
||
1136 | RHDRegMask(PLL, P2PLL_CNTL, 0x00002000, 0x00002000); |
||
1137 | else |
||
1138 | RHDRegMask(PLL, P2PLL_CNTL, 0, 0x00002000); |
||
1139 | } |
||
1140 | |||
1141 | if (PLL->StoreCrtc1Owner) |
||
1142 | R500PLLCRTCGrab(PLL, FALSE); |
||
1143 | if (PLL->StoreCrtc2Owner) |
||
1144 | R500PLLCRTCGrab(PLL, TRUE); |
||
1145 | |||
1146 | if (PLL->StoreDCCGCLKOwner) |
||
1147 | RHDRegWrite(PLL, DCCG_DISP_CLK_SRCSEL, PLL->StoreDCCGCLK); |
||
1148 | } |
||
1149 | |||
1150 | /* Some defaults for when we don't have this info */ |
||
1151 | /* XTAL is visible on the cards */ |
||
1152 | #define RHD_PLL_REFERENCE_DEFAULT 27000 |
||
1153 | /* these required quite some testing */ |
||
1154 | #define RHD_R500_PLL_INTERNAL_MIN_DEFAULT 648000 |
||
1155 | #define RHD_RV620_PLL_INTERNAL_MIN_DEFAULT 702000 |
||
1156 | /* Lowest value seen so far */ |
||
1157 | #define RHD_PLL_INTERNAL_MAX_DEFAULT 1100000 |
||
1158 | #define RHD_PLL_MIN_DEFAULT 16000 /* guess */ |
||
1159 | #define RHD_PLL_MAX_DEFAULT 400000 /* 400Mhz modes... hrm */ |
||
1160 | |||
1161 | enum pllComp { |
||
1162 | PLL_NONE, |
||
1163 | PLL_MIN, |
||
1164 | PLL_MAX |
||
1165 | }; |
||
1166 | |||
1167 | /* |
||
1168 | * |
||
1169 | */ |
||
1170 | #ifdef ATOM_BIOS |
||
1171 | static Bool |
||
1172 | getPLLValuesFromAtomBIOS(RHDPtr rhdPtr, |
||
1173 | AtomBiosRequestID func, char *msg, CARD32 *val, enum pllComp comp) |
||
1174 | { |
||
1175 | AtomBiosArgRec arg; |
||
1176 | AtomBiosResult ret; |
||
1177 | |||
1178 | if (rhdPtr->atomBIOS) { |
||
1179 | ret = RHDAtomBiosFunc(rhdPtr->scrnIndex, rhdPtr->atomBIOS, |
||
1180 | func, &arg); |
||
1181 | if (ret == ATOM_SUCCESS) { |
||
1182 | if (arg.val) { |
||
1183 | switch (comp) { |
||
1184 | case PLL_MAX: |
||
1185 | if (arg.val < *val) |
||
1186 | xf86DrvMsg(rhdPtr->scrnIndex, X_WARNING, |
||
1187 | "Lower %s detected than the default: %lu %lu.\n" |
||
1188 | "Please contact the authors ASAP.\n", msg, |
||
1189 | (unsigned long)*val, (unsigned long)arg.val * 10); |
||
1190 | break; |
||
1191 | case PLL_MIN: |
||
1192 | if (arg.val > *val) |
||
1193 | xf86DrvMsg(rhdPtr->scrnIndex, X_WARNING, |
||
1194 | "Higher %s detected than the default: %lu %lu.\n" |
||
1195 | "Please contact the authors ASAP.\n", msg, |
||
1196 | (unsigned long)*val, (unsigned long)arg.val * 10); |
||
1197 | break; |
||
1198 | default: |
||
1199 | break; |
||
1200 | } |
||
1201 | *val = arg.val; |
||
1202 | } |
||
1203 | } |
||
1204 | return TRUE; |
||
1205 | } else |
||
1206 | xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "Failed to retrieve the %s" |
||
1207 | " clock from ATOM.\n",msg); |
||
1208 | return FALSE; |
||
1209 | } |
||
1210 | #endif |
||
1211 | |||
1212 | /* |
||
1213 | * |
||
1214 | */ |
||
1215 | void |
||
1216 | RHDSetupLimits(RHDPtr rhdPtr, CARD32 *RefClock, |
||
1217 | CARD32 *IntMin, CARD32 *IntMax, |
||
1218 | CARD32 *PixMin, CARD32 *PixMax) |
||
1219 | { |
||
1220 | /* Retrieve the internal PLL frequency limits*/ |
||
1221 | *RefClock = RHD_PLL_REFERENCE_DEFAULT; |
||
1222 | if (rhdPtr->ChipSet < RHD_RV620) |
||
1223 | *IntMin = RHD_R500_PLL_INTERNAL_MIN_DEFAULT; |
||
1224 | else |
||
1225 | *IntMin = RHD_RV620_PLL_INTERNAL_MIN_DEFAULT; |
||
1226 | |||
1227 | *IntMax = RHD_PLL_INTERNAL_MAX_DEFAULT; |
||
1228 | |||
1229 | /* keep the defaults */ |
||
1230 | *PixMin = RHD_PLL_MIN_DEFAULT; |
||
1231 | *PixMax = RHD_PLL_MAX_DEFAULT; |
||
1232 | |||
1233 | #ifdef ATOM_BIOS |
||
1234 | getPLLValuesFromAtomBIOS(rhdPtr, GET_MIN_PIXEL_CLOCK_PLL_OUTPUT, "minimum PLL output", |
||
1235 | IntMin, PLL_MIN); |
||
1236 | getPLLValuesFromAtomBIOS(rhdPtr, GET_MAX_PIXEL_CLOCK_PLL_OUTPUT, "maximum PLL output", |
||
1237 | IntMax, PLL_MAX); |
||
1238 | getPLLValuesFromAtomBIOS(rhdPtr, GET_MAX_PIXEL_CLK, "Pixel Clock", |
||
1239 | PixMax, PLL_MAX); |
||
1240 | getPLLValuesFromAtomBIOS(rhdPtr, GET_REF_CLOCK, "reference clock", |
||
1241 | RefClock, PLL_NONE); |
||
1242 | if (*IntMax == 0) { |
||
1243 | if (rhdPtr->ChipSet < RHD_RV620) |
||
1244 | *IntMax = RHD_R500_PLL_INTERNAL_MIN_DEFAULT; |
||
1245 | else |
||
1246 | *IntMax = RHD_RV620_PLL_INTERNAL_MIN_DEFAULT; |
||
1247 | |||
1248 | xf86DrvMsg(rhdPtr->scrnIndex, X_WARNING, "AtomBIOS reports maximum VCO freq 0. " |
||
1249 | "Using %lu instead\n",(unsigned long)*IntMax); |
||
1250 | } |
||
1251 | #endif |
||
1252 | } |
||
1253 | |||
1254 | /* |
||
1255 | * |
||
1256 | */ |
||
1257 | Bool |
||
1258 | RHDPLLsInit(RHDPtr rhdPtr) |
||
1259 | { |
||
1260 | struct rhdPLL *PLL; |
||
1261 | CARD32 RefClock, IntMin, IntMax, PixMin, PixMax; |
||
1262 | |||
1263 | RHDFUNC(rhdPtr); |
||
1264 | |||
1265 | if (RHDUseAtom(rhdPtr, NULL, atomUsagePLL)) |
||
1266 | return FALSE; |
||
1267 | |||
1268 | RHDSetupLimits(rhdPtr, &RefClock, &IntMin, &IntMax, &PixMin, &PixMax); |
||
1269 | |||
1270 | /* PLL1 */ |
||
1271 | PLL = (struct rhdPLL *) xnfcalloc(sizeof(struct rhdPLL), 1); |
||
1272 | |||
1273 | PLL->scrnIndex = rhdPtr->scrnIndex; |
||
1274 | PLL->Name = PLL_NAME_PLL1; |
||
1275 | PLL->Id = PLL_ID_PLL1; |
||
1276 | |||
1277 | PLL->RefClock = RefClock; |
||
1278 | PLL->IntMin = IntMin; |
||
1279 | PLL->IntMax = IntMax; |
||
1280 | PLL->PixMin = PixMin; |
||
1281 | PLL->PixMax = PixMax; |
||
1282 | |||
1283 | PLL->Valid = NULL; |
||
1284 | if (rhdPtr->ChipSet < RHD_RV620) { |
||
1285 | PLL->Set = R500PLL1Set; |
||
1286 | PLL->Power = R500PLL1Power; |
||
1287 | PLL->Save = R500PLL1Save; |
||
1288 | PLL->Restore = R500PLL1Restore; |
||
1289 | } else { |
||
1290 | PLL->Set = RV620PLL1Set; |
||
1291 | PLL->Power = RV620PLL1Power; |
||
1292 | PLL->Save = RV620PLL1Save; |
||
1293 | PLL->Restore = RV620PLL1Restore; |
||
1294 | } |
||
1295 | |||
1296 | rhdPtr->PLLs[0] = PLL; |
||
1297 | |||
1298 | /* PLL2 */ |
||
1299 | PLL = (struct rhdPLL *) xnfcalloc(sizeof(struct rhdPLL), 1); |
||
1300 | |||
1301 | PLL->scrnIndex = rhdPtr->scrnIndex; |
||
1302 | PLL->Name = PLL_NAME_PLL2; |
||
1303 | PLL->Id = PLL_ID_PLL2; |
||
1304 | |||
1305 | PLL->RefClock = RefClock; |
||
1306 | PLL->IntMin = IntMin; |
||
1307 | PLL->IntMax = IntMax; |
||
1308 | PLL->PixMin = PixMin; |
||
1309 | PLL->PixMax = PixMax; |
||
1310 | |||
1311 | PLL->Valid = NULL; |
||
1312 | if (rhdPtr->ChipSet < RHD_RV620) { |
||
1313 | PLL->Set = R500PLL2Set; |
||
1314 | PLL->Power = R500PLL2Power; |
||
1315 | PLL->Save = R500PLL2Save; |
||
1316 | PLL->Restore = R500PLL2Restore; |
||
1317 | } else { |
||
1318 | PLL->Set = RV620PLL2Set; |
||
1319 | PLL->Power = RV620PLL2Power; |
||
1320 | PLL->Save = RV620PLL2Save; |
||
1321 | PLL->Restore = RV620PLL2Restore; |
||
1322 | } |
||
1323 | |||
1324 | rhdPtr->PLLs[1] = PLL; |
||
1325 | |||
1326 | return TRUE; |
||
1327 | } |
||
1328 | |||
1329 | /* |
||
1330 | * |
||
1331 | */ |
||
1332 | ModeStatus |
||
1333 | RHDPLLValid(struct rhdPLL *PLL, CARD32 Clock) |
||
1334 | { |
||
1335 | RHDFUNC(PLL); |
||
1336 | |||
1337 | if (Clock < PLL->PixMin) |
||
1338 | return MODE_CLOCK_LOW; |
||
1339 | if (Clock > PLL->PixMax) |
||
1340 | return MODE_CLOCK_HIGH; |
||
1341 | |||
1342 | if (PLL->Valid) |
||
1343 | return PLL->Valid(PLL, Clock); |
||
1344 | else |
||
1345 | return MODE_OK; |
||
1346 | } |
||
1347 | |||
1348 | |||
1349 | /* |
||
1350 | * Calculate the PLL parameters for a given dotclock. |
||
1351 | * |
||
1352 | * This calculation uses a linear approximation of an experimentally found |
||
1353 | * curve that delimits reference versus feedback dividers on rv610. This curve |
||
1354 | * can be shifted towards higher feedback divider through increasing the gain |
||
1355 | * control, but the effect of this is rather limited. |
||
1356 | * |
||
1357 | * Since this upper limit still provides a wide enough range with enough |
||
1358 | * granularity, we use it for all r5xx and r6xx devices. |
||
1359 | */ |
||
1360 | static Bool |
||
1361 | PLLCalculate(struct rhdPLL *PLL, CARD32 PixelClock, |
||
1362 | CARD16 *RefDivider, CARD16 *FBDivider, CARD8 *PostDivider) |
||
1363 | { |
||
1364 | /* limited by the number of bits available */ |
||
1365 | #define FB_DIV_LIMIT 2048 |
||
1366 | #define REF_DIV_LIMIT 1024 |
||
1367 | #define POST_DIV_LIMIT 128 |
||
1368 | |||
1369 | CARD32 FBDiv, RefDiv, PostDiv, BestDiff = 0xFFFFFFFF; |
||
1370 | float Ratio; |
||
1371 | |||
1372 | Ratio = ((float) PixelClock) / ((float) PLL->RefClock); |
||
1373 | |||
1374 | for (PostDiv = 2; PostDiv < POST_DIV_LIMIT; PostDiv++) { |
||
1375 | CARD32 VCOOut = PixelClock * PostDiv; |
||
1376 | |||
1377 | /* we are conservative and avoid the limits */ |
||
1378 | if (VCOOut <= PLL->IntMin) |
||
1379 | continue; |
||
1380 | if (VCOOut >= PLL->IntMax) |
||
1381 | break; |
||
1382 | |||
1383 | for (RefDiv = 1; RefDiv <= REF_DIV_LIMIT; RefDiv++) { |
||
1384 | CARD32 Diff; |
||
1385 | |||
1386 | FBDiv = (CARD32) ((Ratio * PostDiv * RefDiv) + 0.5); |
||
1387 | |||
1388 | if (FBDiv >= FB_DIV_LIMIT) |
||
1389 | break; |
||
1390 | if (FBDiv > (500 + (13 * RefDiv))) /* rv6x0 limit */ |
||
1391 | break; |
||
1392 | |||
1393 | Diff = abs( PixelClock - (FBDiv * PLL->RefClock) / (PostDiv * RefDiv) ); |
||
1394 | |||
1395 | if (Diff < BestDiff) { |
||
1396 | *FBDivider = FBDiv; |
||
1397 | *RefDivider = RefDiv; |
||
1398 | *PostDivider = PostDiv; |
||
1399 | BestDiff = Diff; |
||
1400 | } |
||
1401 | |||
1402 | if (BestDiff == 0) |
||
1403 | break; |
||
1404 | } |
||
1405 | if (BestDiff == 0) |
||
1406 | break; |
||
1407 | } |
||
1408 | |||
1409 | if (BestDiff != 0xFFFFFFFF) { |
||
1410 | RHDDebug(PLL->scrnIndex, "PLL Calculation: %dkHz = " |
||
1411 | "(((%i / 0x%X) * 0x%X) / 0x%X) (%dkHz off)\n", |
||
1412 | (int) PixelClock, (unsigned int) PLL->RefClock, *RefDivider, |
||
1413 | *FBDivider, *PostDivider, (int) BestDiff); |
||
1414 | return TRUE; |
||
1415 | } else { /* Should never happen */ |
||
1416 | xf86DrvMsg(PLL->scrnIndex, X_ERROR, |
||
1417 | "%s: Failed to get a valid PLL setting for %dkHz\n", |
||
1418 | __func__, (int) PixelClock); |
||
1419 | return FALSE; |
||
1420 | } |
||
1421 | } |
||
1422 | |||
1423 | /* |
||
1424 | * |
||
1425 | */ |
||
1426 | void |
||
1427 | RHDPLLSet(struct rhdPLL *PLL, CARD32 Clock) |
||
1428 | { |
||
1429 | CARD16 RefDivider = 0, FBDivider = 0; |
||
1430 | CARD8 PostDivider = 0; |
||
1431 | |||
1432 | RHDDebug(PLL->scrnIndex, "%s: Setting %s to %dkHz\n", __func__, |
||
1433 | PLL->Name, Clock); |
||
1434 | |||
1435 | if (PLLCalculate(PLL, Clock, &RefDivider, &FBDivider, &PostDivider)) { |
||
1436 | PLL->Set(PLL, Clock, RefDivider, FBDivider, PostDivider); |
||
1437 | |||
1438 | PLL->CurrentClock = Clock; |
||
1439 | PLL->Active = TRUE; |
||
1440 | } else |
||
1441 | xf86DrvMsg(PLL->scrnIndex, X_WARNING, |
||
1442 | "%s: Not altering any settings.\n", __func__); |
||
1443 | } |
||
1444 | |||
1445 | /* |
||
1446 | * |
||
1447 | */ |
||
1448 | void |
||
1449 | RHDPLLPower(struct rhdPLL *PLL, int Power) |
||
1450 | { |
||
1451 | RHDFUNC(PLL); |
||
1452 | |||
1453 | if (PLL->Power) |
||
1454 | PLL->Power(PLL, Power); |
||
1455 | } |
||
1456 | |||
1457 | /* |
||
1458 | * |
||
1459 | */ |
||
1460 | void |
||
1461 | RHDPLLsPowerAll(RHDPtr rhdPtr, int Power) |
||
1462 | { |
||
1463 | struct rhdPLL *PLL; |
||
1464 | |||
1465 | RHDFUNC(rhdPtr); |
||
1466 | |||
1467 | PLL = rhdPtr->PLLs[0]; |
||
1468 | if (PLL->Power) |
||
1469 | PLL->Power(PLL, Power); |
||
1470 | |||
1471 | PLL = rhdPtr->PLLs[1]; |
||
1472 | if (PLL->Power) |
||
1473 | PLL->Power(PLL, Power); |
||
1474 | } |
||
1475 | |||
1476 | /* |
||
1477 | * |
||
1478 | */ |
||
1479 | void |
||
1480 | RHDPLLsShutdownInactive(RHDPtr rhdPtr) |
||
1481 | { |
||
1482 | struct rhdPLL *PLL; |
||
1483 | |||
1484 | RHDFUNC(rhdPtr); |
||
1485 | |||
1486 | PLL = rhdPtr->PLLs[0]; |
||
1487 | if (PLL->Power && !PLL->Active) |
||
1488 | PLL->Power(PLL, RHD_POWER_SHUTDOWN); |
||
1489 | |||
1490 | PLL = rhdPtr->PLLs[1]; |
||
1491 | if (PLL->Power && !PLL->Active) |
||
1492 | PLL->Power(PLL, RHD_POWER_SHUTDOWN); |
||
1493 | } |
||
1494 | |||
1495 | /* |
||
1496 | * |
||
1497 | */ |
||
1498 | void |
||
1499 | RHDPLLsSave(RHDPtr rhdPtr) |
||
1500 | { |
||
1501 | struct rhdPLL *PLL; |
||
1502 | |||
1503 | RHDFUNC(rhdPtr); |
||
1504 | |||
1505 | PLL = rhdPtr->PLLs[0]; |
||
1506 | if (PLL->Save) |
||
1507 | PLL->Save(PLL); |
||
1508 | |||
1509 | PLL = rhdPtr->PLLs[1]; |
||
1510 | if (PLL->Save) |
||
1511 | PLL->Save(PLL); |
||
1512 | } |
||
1513 | |||
1514 | /* |
||
1515 | * |
||
1516 | */ |
||
1517 | void |
||
1518 | RHDPLLsRestore(RHDPtr rhdPtr) |
||
1519 | { |
||
1520 | struct rhdPLL *PLL; |
||
1521 | |||
1522 | RHDFUNC(rhdPtr); |
||
1523 | |||
1524 | PLL = rhdPtr->PLLs[0]; |
||
1525 | if (PLL->Restore) |
||
1526 | PLL->Restore(PLL); |
||
1527 | |||
1528 | PLL = rhdPtr->PLLs[1]; |
||
1529 | if (PLL->Restore) |
||
1530 | PLL->Restore(PLL); |
||
1531 | } |
||
1532 | |||
1533 | /* |
||
1534 | * |
||
1535 | */ |
||
1536 | void |
||
1537 | RHDPLLsDestroy(RHDPtr rhdPtr) |
||
1538 | { |
||
1539 | RHDFUNC(rhdPtr); |
||
1540 | |||
1541 | if (rhdPtr->PLLs[0] && rhdPtr->PLLs[0]->Private) |
||
1542 | xfree(rhdPtr->PLLs[0]->Private); |
||
1543 | xfree(rhdPtr->PLLs[0]); |
||
1544 | if (rhdPtr->PLLs[1] && rhdPtr->PLLs[1]->Private) |
||
1545 | xfree(rhdPtr->PLLs[1]->Private); |
||
1546 | xfree(rhdPtr->PLLs[1]); |
||
1547 | }>=>=>>>>>>>>><>><>=>=>><>=>=>><>>>> |