3 * ATI Mach64 CT/VT/GT/LT Support
7 #include <linux/delay.h>
9 #include <video/mach64.h>
14 static int aty_valid_pll_ct (const struct fb_info
*info
, u32 vclk_per
, struct pll_ct
*pll
);
15 static int aty_dsp_gt (const struct fb_info
*info
, u32 bpp
, struct pll_ct
*pll
);
16 static int aty_var_to_pll_ct(const struct fb_info
*info
, u32 vclk_per
, u32 bpp
, union aty_pll
*pll
);
17 static u32
aty_pll_to_var_ct(const struct fb_info
*info
, const union aty_pll
*pll
);
19 u8
aty_ld_pll_ct(int offset
, const struct atyfb_par
*par
)
24 aty_st_8(CLOCK_CNTL_ADDR
, (offset
<< 2) & PLL_ADDR
, par
);
25 /* read the register value */
26 res
= aty_ld_8(CLOCK_CNTL_DATA
, par
);
30 void aty_st_pll_ct(int offset
, u8 val
, const struct atyfb_par
*par
)
33 aty_st_8(CLOCK_CNTL_ADDR
, ((offset
<< 2) & PLL_ADDR
) | PLL_WR_EN
, par
);
34 /* write the register value */
35 aty_st_8(CLOCK_CNTL_DATA
, val
& PLL_DATA
, par
);
36 aty_st_8(CLOCK_CNTL_ADDR
, ((offset
<< 2) & PLL_ADDR
) & ~PLL_WR_EN
, par
);
41 * <daniel.mantione@freepascal.org>
44 * ATI Mach64 CT clock synthesis description.
46 * All clocks on the Mach64 can be calculated using the same principle:
49 * CLK = ----------------------
50 * PLL_REF_DIV * POST_DIV
52 * XTALIN is a fixed speed clock. Common speeds are 14.31 MHz and 29.50 MHz.
53 * PLL_REF_DIV can be set by the user, but is the same for all clocks.
54 * FB_DIV can be set by the user for each clock individually, it should be set
55 * between 128 and 255, the chip will generate a bad clock signal for too low
57 * x depends on the type of clock; usually it is 2, but for the MCLK it can also
59 * POST_DIV can be set by the user for each clock individually, Possible values
60 * are 1,2,4,8 and for some clocks other values are available too.
61 * CLK is of course the clock speed that is generated.
63 * The Mach64 has these clocks:
65 * MCLK The clock rate of the chip
66 * XCLK The clock rate of the on-chip memory
67 * VCLK0 First pixel clock of first CRT controller
68 * VCLK1 Second pixel clock of first CRT controller
69 * VCLK2 Third pixel clock of first CRT controller
70 * VCLK3 Fourth pixel clock of first CRT controller
71 * VCLK Selected pixel clock, one of VCLK0, VCLK1, VCLK2, VCLK3
72 * V2CLK Pixel clock of the second CRT controller.
73 * SCLK Multi-purpose clock
75 * - MCLK and XCLK use the same FB_DIV
76 * - VCLK0 .. VCLK3 use the same FB_DIV
77 * - V2CLK is needed when the second CRTC is used (can be used for dualhead);
78 * i.e. CRT monitor connected to laptop has different resolution than built
80 * - SCLK is not available on all cards; it is know to exist on the Rage LT-PRO,
81 * Rage XL and Rage Mobility. It is know not to exist on the Mach64 VT.
82 * - V2CLK is not available on all cards, most likely only the Rage LT-PRO,
83 * the Rage XL and the Rage Mobility
85 * SCLK can be used to:
86 * - Clock the chip instead of MCLK
87 * - Replace XTALIN with a user defined frequency
88 * - Generate the pixel clock for the LCD monitor (instead of VCLK)
92 * It can be quite hard to calculate XCLK and MCLK if they don't run at the
93 * same frequency. Luckily, until now all cards that need asynchrone clock
94 * speeds seem to have SCLK.
95 * So this driver uses SCLK to clock the chip and XCLK to clock the memory.
98 /* ------------------------------------------------------------------------- */
101 * PLL programming (Mach64 CT family)
104 * This procedure sets the display fifo. The display fifo is a buffer that
105 * contains data read from the video memory that waits to be processed by
106 * the CRT controller.
108 * On the more modern Mach64 variants, the chip doesn't calculate the
109 * interval after which the display fifo has to be reloaded from memory
110 * automatically, the driver has to do it instead.
113 #define Maximum_DSP_PRECISION 7
114 static u8 postdividers
[] = {1,2,4,8,3};
116 static int aty_dsp_gt(const struct fb_info
*info
, u32 bpp
, struct pll_ct
*pll
)
118 u32 dsp_off
, dsp_on
, dsp_xclks
;
119 u32 multiplier
, divider
, ras_multiplier
, ras_divider
, tmp
;
123 multiplier
= ((u32
)pll
->mclk_fb_div
) * pll
->vclk_post_div_real
;
124 divider
= ((u32
)pll
->vclk_fb_div
) * pll
->xclk_ref_div
;
126 ras_multiplier
= pll
->xclkmaxrasdelay
;
130 divider
= divider
* (bpp
>> 2);
132 vshift
= (6 - 2) - pll
->xclk_post_div
; /* FIFO is 64 bits wide in accelerator mode ... */
135 vshift
--; /* ... but only 32 bits in VGA mode. */
137 #ifdef CONFIG_FB_ATY_GENERIC_LCD
138 if (pll
->xres
!= 0) {
139 struct atyfb_par
*par
= (struct atyfb_par
*) info
->par
;
141 multiplier
= multiplier
* par
->lcd_width
;
142 divider
= divider
* pll
->xres
& ~7;
144 ras_multiplier
= ras_multiplier
* par
->lcd_width
;
145 ras_divider
= ras_divider
* pll
->xres
& ~7;
148 /* If we don't do this, 32 bits for multiplier & divider won't be
149 enough in certain situations! */
150 while (((multiplier
| divider
) & 1) == 0) {
151 multiplier
= multiplier
>> 1;
152 divider
= divider
>> 1;
155 /* Determine DSP precision first */
156 tmp
= ((multiplier
* pll
->fifo_size
) << vshift
) / divider
;
158 for (dsp_precision
= -5; tmp
; dsp_precision
++)
160 if (dsp_precision
< 0)
162 else if (dsp_precision
> Maximum_DSP_PRECISION
)
163 dsp_precision
= Maximum_DSP_PRECISION
;
165 xshift
= 6 - dsp_precision
;
168 /* Move on to dsp_off */
169 dsp_off
= ((multiplier
* (pll
->fifo_size
- 1)) << vshift
) / divider
-
170 (1 << (vshift
- xshift
));
173 dsp_on = ((multiplier * 20 << vshift) + divider) / divider;
176 dsp_on
= ((multiplier
<< vshift
) + divider
) / divider
;
177 tmp
= ((ras_multiplier
<< xshift
) + ras_divider
) / ras_divider
;
180 dsp_on
= dsp_on
+ (tmp
* 2) + (pll
->xclkpagefaultdelay
<< xshift
);
183 /* Calculate rounding factor and apply it to dsp_on */
184 tmp
= ((1 << (Maximum_DSP_PRECISION
- dsp_precision
)) - 1) >> 1;
185 dsp_on
= ((dsp_on
+ tmp
) / (tmp
+ 1)) * (tmp
+ 1);
187 if (dsp_on
>= ((dsp_off
/ (tmp
+ 1)) * (tmp
+ 1))) {
188 dsp_on
= dsp_off
- (multiplier
<< vshift
) / divider
;
189 dsp_on
= (dsp_on
/ (tmp
+ 1)) * (tmp
+ 1);
192 /* Last but not least: dsp_xclks */
193 dsp_xclks
= ((multiplier
<< (vshift
+ 5)) + divider
) / divider
;
195 /* Get register values. */
196 pll
->dsp_on_off
= (dsp_on
<< 16) + dsp_off
;
197 pll
->dsp_config
= (dsp_precision
<< 20) | (pll
->dsp_loop_latency
<< 16) | dsp_xclks
;
199 printk("atyfb(%s): dsp_config 0x%08x, dsp_on_off 0x%08x\n",
200 __FUNCTION__
, pll
->dsp_config
, pll
->dsp_on_off
);
205 static int aty_valid_pll_ct(const struct fb_info
*info
, u32 vclk_per
, struct pll_ct
*pll
)
208 struct atyfb_par
*par
= (struct atyfb_par
*) info
->par
;
213 /* FIXME: use the VTB/GTB /{3,6,12} post dividers if they're better suited */
214 q
= par
->ref_clk_per
* pll
->pll_ref_div
* 4 / vclk_per
;
215 if (q
< 16*8 || q
> 255*8) {
216 printk(KERN_CRIT
"atyfb: vclk out of range\n");
219 pll
->vclk_post_div
= (q
< 128*8);
220 pll
->vclk_post_div
+= (q
< 64*8);
221 pll
->vclk_post_div
+= (q
< 32*8);
223 pll
->vclk_post_div_real
= postdividers
[pll
->vclk_post_div
];
224 // pll->vclk_post_div <<= 6;
225 pll
->vclk_fb_div
= q
* pll
->vclk_post_div_real
/ 8;
227 pllvclk
= (1000000 * 2 * pll
->vclk_fb_div
) /
228 (par
->ref_clk_per
* pll
->pll_ref_div
);
229 printk("atyfb(%s): pllvclk=%d MHz, vclk=%d MHz\n",
230 __FUNCTION__
, pllvclk
, pllvclk
/ pll
->vclk_post_div_real
);
232 pll
->pll_vclk_cntl
= 0x03; /* VCLK = PLL_VCLK/VCLKx_POST */
236 static int aty_var_to_pll_ct(const struct fb_info
*info
, u32 vclk_per
, u32 bpp
, union aty_pll
*pll
)
238 struct atyfb_par
*par
= (struct atyfb_par
*) info
->par
;
241 if ((err
= aty_valid_pll_ct(info
, vclk_per
, &pll
->ct
)))
243 if (M64_HAS(GTB_DSP
) && (err
= aty_dsp_gt(info
, bpp
, &pll
->ct
)))
245 /*aty_calc_pll_ct(info, &pll->ct);*/
249 static u32
aty_pll_to_var_ct(const struct fb_info
*info
, const union aty_pll
*pll
)
251 struct atyfb_par
*par
= (struct atyfb_par
*) info
->par
;
253 ret
= par
->ref_clk_per
* pll
->ct
.pll_ref_div
* pll
->ct
.vclk_post_div_real
/ pll
->ct
.vclk_fb_div
/ 2;
254 #ifdef CONFIG_FB_ATY_GENERIC_LCD
255 if(pll
->ct
.xres
> 0) {
256 ret
*= par
->lcd_width
;
261 printk("atyfb(%s): calculated 0x%08X(%i)\n", __FUNCTION__
, ret
, ret
);
266 void aty_set_pll_ct(const struct fb_info
*info
, const union aty_pll
*pll
)
268 struct atyfb_par
*par
= (struct atyfb_par
*) info
->par
;
269 u32 crtc_gen_cntl
, lcd_gen_cntrl
;
274 printk("atyfb(%s): about to program:\n"
275 "pll_ext_cntl=0x%02x pll_gen_cntl=0x%02x pll_vclk_cntl=0x%02x\n",
277 pll
->ct
.pll_ext_cntl
, pll
->ct
.pll_gen_cntl
, pll
->ct
.pll_vclk_cntl
);
279 printk("atyfb(%s): setting clock %lu for FeedBackDivider %i, ReferenceDivider %i, PostDivider %i(%i)\n",
281 par
->clk_wr_offset
, pll
->ct
.vclk_fb_div
,
282 pll
->ct
.pll_ref_div
, pll
->ct
.vclk_post_div
, pll
->ct
.vclk_post_div_real
);
284 #ifdef CONFIG_FB_ATY_GENERIC_LCD
285 if (par
->lcd_table
!= 0) {
287 lcd_gen_cntrl
= aty_ld_lcd(LCD_GEN_CNTL
, par
);
288 aty_st_lcd(LCD_GEN_CNTL
, lcd_gen_cntrl
& ~LCD_ON
, par
);
291 aty_st_8(CLOCK_CNTL
, par
->clk_wr_offset
| CLOCK_STROBE
, par
);
293 /* Temporarily switch to accelerator mode */
294 crtc_gen_cntl
= aty_ld_le32(CRTC_GEN_CNTL
, par
);
295 if (!(crtc_gen_cntl
& CRTC_EXT_DISP_EN
))
296 aty_st_le32(CRTC_GEN_CNTL
, crtc_gen_cntl
| CRTC_EXT_DISP_EN
, par
);
298 /* Reset VCLK generator */
299 aty_st_pll_ct(PLL_VCLK_CNTL
, pll
->ct
.pll_vclk_cntl
, par
);
301 /* Set post-divider */
302 tmp2
= par
->clk_wr_offset
<< 1;
303 tmp
= aty_ld_pll_ct(VCLK_POST_DIV
, par
);
304 tmp
&= ~(0x03U
<< tmp2
);
305 tmp
|= ((pll
->ct
.vclk_post_div
& 0x03U
) << tmp2
);
306 aty_st_pll_ct(VCLK_POST_DIV
, tmp
, par
);
308 /* Set extended post-divider */
309 tmp
= aty_ld_pll_ct(PLL_EXT_CNTL
, par
);
310 tmp
&= ~(0x10U
<< par
->clk_wr_offset
);
312 tmp
|= pll
->ct
.pll_ext_cntl
;
313 aty_st_pll_ct(PLL_EXT_CNTL
, tmp
, par
);
315 /* Set feedback divider */
316 tmp
= VCLK0_FB_DIV
+ par
->clk_wr_offset
;
317 aty_st_pll_ct(tmp
, (pll
->ct
.vclk_fb_div
& 0xFFU
), par
);
319 aty_st_pll_ct(PLL_GEN_CNTL
, (pll
->ct
.pll_gen_cntl
& (~(PLL_OVERRIDE
| PLL_MCLK_RST
))) | OSC_EN
, par
);
321 /* End VCLK generator reset */
322 aty_st_pll_ct(PLL_VCLK_CNTL
, pll
->ct
.pll_vclk_cntl
& ~(PLL_VCLK_RST
), par
);
325 aty_st_pll_ct(PLL_GEN_CNTL
, pll
->ct
.pll_gen_cntl
, par
);
326 aty_st_pll_ct(PLL_VCLK_CNTL
, pll
->ct
.pll_vclk_cntl
, par
);
329 /* Restore mode register */
330 if (!(crtc_gen_cntl
& CRTC_EXT_DISP_EN
))
331 aty_st_le32(CRTC_GEN_CNTL
, crtc_gen_cntl
, par
);
333 if (M64_HAS(GTB_DSP
)) {
338 else if (par
->ram_type
>= SDRAM
)
342 aty_st_pll_ct(DLL_CNTL
, dll_cntl
, par
);
343 aty_st_pll_ct(VFC_CNTL
, 0x1b, par
);
344 aty_st_le32(DSP_CONFIG
, pll
->ct
.dsp_config
, par
);
345 aty_st_le32(DSP_ON_OFF
, pll
->ct
.dsp_on_off
, par
);
348 aty_st_pll_ct(DLL_CNTL
, dll_cntl
, par
);
350 aty_st_pll_ct(DLL_CNTL
, dll_cntl
| 0x40, par
);
352 aty_st_pll_ct(DLL_CNTL
, dll_cntl
& ~0x40, par
);
354 #ifdef CONFIG_FB_ATY_GENERIC_LCD
355 if (par
->lcd_table
!= 0) {
357 aty_st_lcd(LCD_GEN_CNTL
, lcd_gen_cntrl
, par
);
362 static void __init
aty_get_pll_ct(const struct fb_info
*info
,
365 struct atyfb_par
*par
= (struct atyfb_par
*) info
->par
;
368 clock
= aty_ld_8(CLOCK_CNTL
, par
) & 0x03U
;
370 pll
->ct
.vclk_post_div
= (aty_ld_pll_ct(VCLK_POST_DIV
, par
) >> tmp
) & 0x03U
;
372 pll
->ct
.pll_ext_cntl
= aty_ld_pll_ct(PLL_EXT_CNTL
, par
) & 0x0FU
;
373 pll
->ct
.vclk_fb_div
= aty_ld_pll_ct(VCLK0_FB_DIV
+ clock
, par
) & 0xFFU
;
374 pll
->ct
.pll_ref_div
= aty_ld_pll_ct(PLL_REF_DIV
, par
);
375 pll
->ct
.mclk_fb_div
= aty_ld_pll_ct(MCLK_FB_DIV
, par
);
377 pll
->ct
.pll_gen_cntl
= aty_ld_pll_ct(PLL_GEN_CNTL
, par
);
378 pll
->ct
.pll_vclk_cntl
= aty_ld_pll_ct(PLL_VCLK_CNTL
, par
);
380 if (M64_HAS(GTB_DSP
)) {
381 pll
->ct
.dsp_config
= aty_ld_le32(DSP_CONFIG
, par
);
382 pll
->ct
.dsp_on_off
= aty_ld_le32(DSP_ON_OFF
, par
);
386 static int __init
aty_init_pll_ct(const struct fb_info
*info
,
389 struct atyfb_par
*par
= (struct atyfb_par
*) info
->par
;
390 u8 mpost_div
, xpost_div
, sclk_post_div_real
, sclk_fb_div
, spll_cntl2
;
391 u32 q
, i
, memcntl
, trp
;
392 u32 dsp_config
, dsp_on_off
, vga_dsp_config
, vga_dsp_on_off
;
394 int pllmclk
, pllsclk
;
396 pll
->ct
.pll_ext_cntl
= aty_ld_pll_ct(PLL_EXT_CNTL
, par
);
397 pll
->ct
.xclk_post_div
= pll
->ct
.pll_ext_cntl
& 0x07;
398 pll
->ct
.xclk_ref_div
= 1;
399 switch (pll
->ct
.xclk_post_div
) {
400 case 0: case 1: case 2: case 3:
404 pll
->ct
.xclk_ref_div
= 3;
405 pll
->ct
.xclk_post_div
= 0;
409 printk(KERN_CRIT
"atyfb: Unsupported xclk source: %d.\n", pll
->ct
.xclk_post_div
);
412 pll
->ct
.mclk_fb_mult
= 2;
413 if(pll
->ct
.pll_ext_cntl
& PLL_MFB_TIMES_4_2B
) {
414 pll
->ct
.mclk_fb_mult
= 4;
415 pll
->ct
.xclk_post_div
-= 1;
419 printk("atyfb(%s): mclk_fb_mult=%d, xclk_post_div=%d\n",
420 __FUNCTION__
, pll
->ct
.mclk_fb_mult
, pll
->ct
.xclk_post_div
);
423 memcntl
= aty_ld_le32(MEM_CNTL
, par
);
424 trp
= (memcntl
& 0x300) >> 8;
426 pll
->ct
.xclkpagefaultdelay
= ((memcntl
& 0xc00) >> 10) + ((memcntl
& 0x1000) >> 12) + trp
+ 2;
427 pll
->ct
.xclkmaxrasdelay
= ((memcntl
& 0x70000) >> 16) + trp
+ 2;
429 if (M64_HAS(FIFO_32
)) {
430 pll
->ct
.fifo_size
= 32;
432 pll
->ct
.fifo_size
= 24;
433 pll
->ct
.xclkpagefaultdelay
+= 2;
434 pll
->ct
.xclkmaxrasdelay
+= 3;
437 switch (par
->ram_type
) {
439 if (info
->fix
.smem_len
<=ONE_MB
) {
440 pll
->ct
.dsp_loop_latency
= 10;
442 pll
->ct
.dsp_loop_latency
= 8;
443 pll
->ct
.xclkpagefaultdelay
+= 2;
448 if (info
->fix
.smem_len
<=ONE_MB
) {
449 pll
->ct
.dsp_loop_latency
= 9;
451 pll
->ct
.dsp_loop_latency
= 8;
452 pll
->ct
.xclkpagefaultdelay
+= 1;
456 if (info
->fix
.smem_len
<=ONE_MB
) {
457 pll
->ct
.dsp_loop_latency
= 11;
459 pll
->ct
.dsp_loop_latency
= 10;
460 pll
->ct
.xclkpagefaultdelay
+= 1;
464 pll
->ct
.dsp_loop_latency
= 8;
465 pll
->ct
.xclkpagefaultdelay
+= 3;
468 pll
->ct
.dsp_loop_latency
= 11;
469 pll
->ct
.xclkpagefaultdelay
+= 3;
473 if (pll
->ct
.xclkmaxrasdelay
<= pll
->ct
.xclkpagefaultdelay
)
474 pll
->ct
.xclkmaxrasdelay
= pll
->ct
.xclkpagefaultdelay
+ 1;
476 /* Allow BIOS to override */
477 dsp_config
= aty_ld_le32(DSP_CONFIG
, par
);
478 dsp_on_off
= aty_ld_le32(DSP_ON_OFF
, par
);
479 vga_dsp_config
= aty_ld_le32(VGA_DSP_CONFIG
, par
);
480 vga_dsp_on_off
= aty_ld_le32(VGA_DSP_ON_OFF
, par
);
483 pll
->ct
.dsp_loop_latency
= (dsp_config
& DSP_LOOP_LATENCY
) >> 16;
485 FIXME
: is it relevant
for us
?
486 if ((!dsp_on_off
&& !M64_HAS(RESET_3D
)) ||
487 ((dsp_on_off
== vga_dsp_on_off
) &&
488 (!dsp_config
|| !((dsp_config
^ vga_dsp_config
) & DSP_XCLKS_PER_QW
)))) {
489 vga_dsp_on_off
&= VGA_DSP_OFF
;
490 vga_dsp_config
&= VGA_DSP_XCLKS_PER_QW
;
491 if (ATIDivide(vga_dsp_on_off
, vga_dsp_config
, 5, 1) > 24)
492 pll
->ct
.fifo_size
= 32;
494 pll
->ct
.fifo_size
= 24;
497 /* Exit if the user does not want us to tamper with the clock
498 rates of her chip. */
499 if (par
->mclk_per
== 0) {
500 u8 mclk_fb_div
, pll_ext_cntl
;
501 pll
->ct
.pll_ref_div
= aty_ld_pll_ct(PLL_REF_DIV
, par
);
502 pll_ext_cntl
= aty_ld_pll_ct(PLL_EXT_CNTL
, par
);
503 pll
->ct
.xclk_post_div_real
= postdividers
[pll_ext_cntl
& 0x07];
504 mclk_fb_div
= aty_ld_pll_ct(MCLK_FB_DIV
, par
);
505 if (pll_ext_cntl
& PLL_MFB_TIMES_4_2B
)
507 pll
->ct
.mclk_fb_div
= mclk_fb_div
;
511 pll
->ct
.pll_ref_div
= par
->pll_per
* 2 * 255 / par
->ref_clk_per
;
513 /* FIXME: use the VTB/GTB /3 post divider if it's better suited */
514 q
= par
->ref_clk_per
* pll
->ct
.pll_ref_div
* 8 /
515 (pll
->ct
.mclk_fb_mult
* par
->xclk_per
);
517 if (q
< 16*8 || q
> 255*8) {
518 printk(KERN_CRIT
"atxfb: xclk out of range\n");
521 xpost_div
= (q
< 128*8);
522 xpost_div
+= (q
< 64*8);
523 xpost_div
+= (q
< 32*8);
525 pll
->ct
.xclk_post_div_real
= postdividers
[xpost_div
];
526 pll
->ct
.mclk_fb_div
= q
* pll
->ct
.xclk_post_div_real
/ 8;
529 pllmclk
= (1000000 * pll
->ct
.mclk_fb_mult
* pll
->ct
.mclk_fb_div
) /
530 (par
->ref_clk_per
* pll
->ct
.pll_ref_div
);
531 printk("atyfb(%s): pllmclk=%d MHz, xclk=%d MHz\n",
532 __FUNCTION__
, pllmclk
, pllmclk
/ pll
->ct
.xclk_post_div_real
);
535 if (M64_HAS(SDRAM_MAGIC_PLL
) && (par
->ram_type
>= SDRAM
))
536 pll
->ct
.pll_gen_cntl
= OSC_EN
;
538 pll
->ct
.pll_gen_cntl
= OSC_EN
| DLL_PWDN
/* | FORCE_DCLK_TRI_STATE */;
540 if (M64_HAS(MAGIC_POSTDIV
))
541 pll
->ct
.pll_ext_cntl
= 0;
543 pll
->ct
.pll_ext_cntl
= xpost_div
;
545 if (pll
->ct
.mclk_fb_mult
== 4)
546 pll
->ct
.pll_ext_cntl
|= PLL_MFB_TIMES_4_2B
;
548 if (par
->mclk_per
== par
->xclk_per
) {
549 pll
->ct
.pll_gen_cntl
|= (xpost_div
<< 4); /* mclk == xclk */
552 * The chip clock is not equal to the memory clock.
553 * Therefore we will use sclk to clock the chip.
555 pll
->ct
.pll_gen_cntl
|= (6 << 4); /* mclk == sclk */
557 q
= par
->ref_clk_per
* pll
->ct
.pll_ref_div
* 4 / par
->mclk_per
;
558 if (q
< 16*8 || q
> 255*8) {
559 printk(KERN_CRIT
"atyfb: mclk out of range\n");
562 mpost_div
= (q
< 128*8);
563 mpost_div
+= (q
< 64*8);
564 mpost_div
+= (q
< 32*8);
566 sclk_post_div_real
= postdividers
[mpost_div
];
567 sclk_fb_div
= q
* sclk_post_div_real
/ 8;
568 spll_cntl2
= mpost_div
<< 4;
570 pllsclk
= (1000000 * 2 * sclk_fb_div
) /
571 (par
->ref_clk_per
* pll
->ct
.pll_ref_div
);
572 printk("atyfb(%s): use sclk, pllsclk=%d MHz, sclk=mclk=%d MHz\n",
573 __FUNCTION__
, pllsclk
, pllsclk
/ sclk_post_div_real
);
576 * This disables the sclk, crashes the computer as reported:
577 * aty_st_pll_ct(SPLL_CNTL2, 3, info);
579 * So it seems the sclk must be enabled before it is used;
580 * so PLL_GEN_CNTL must be programmed *after* the sclk.
582 aty_st_pll_ct(SCLK_FB_DIV
, sclk_fb_div
, par
);
583 aty_st_pll_ct(SPLL_CNTL2
, spll_cntl2
, par
);
585 * The sclk has been started. However, I believe the first clock
586 * ticks it generates are not very stable. Hope this primitive loop
587 * helps for Rage Mobilities that sometimes crash when
588 * we switch to sclk. (Daniel Mantione, 13-05-2003)
590 for (i
=0;i
<=0x1ffff;i
++);
593 aty_st_pll_ct(PLL_REF_DIV
, pll
->ct
.pll_ref_div
, par
);
594 aty_st_pll_ct(PLL_GEN_CNTL
, pll
->ct
.pll_gen_cntl
, par
);
595 aty_st_pll_ct(MCLK_FB_DIV
, pll
->ct
.mclk_fb_div
, par
);
596 aty_st_pll_ct(PLL_EXT_CNTL
, pll
->ct
.pll_ext_cntl
, par
);
597 /* Disable the extra precision pixel clock controls since we do not use them. */
598 aty_st_pll_ct(EXT_VPLL_CNTL
, aty_ld_pll_ct(EXT_VPLL_CNTL
, par
) &
599 ~(EXT_VPLL_EN
| EXT_VPLL_VGA_EN
| EXT_VPLL_INSYNC
), par
);
604 static int dummy(void)
609 const struct aty_dac_ops aty_dac_ct
= {
610 .set_dac
= (void *) dummy
,
613 const struct aty_pll_ops aty_pll_ct
= {
614 .var_to_pll
= aty_var_to_pll_ct
,
615 .pll_to_var
= aty_pll_to_var_ct
,
616 .set_pll
= aty_set_pll_ct
,
617 .get_pll
= aty_get_pll_ct
,
618 .init_pll
= aty_init_pll_ct