ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / video / aty / mach64_ct.c
1
2 /*
3  *  ATI Mach64 CT/VT/GT/LT Support
4  */
5
6 #include <linux/fb.h>
7
8 #include <asm/io.h>
9
10 #include <video/mach64.h>
11 #include "atyfb.h"
12
13
14 /* FIXME: remove the FAIL definition */
15 #define FAIL(x) do { printk(x "\n"); return -EINVAL; } while (0)
16
17 static void aty_st_pll(int offset, u8 val, const struct atyfb_par *par);
18 static int aty_valid_pll_ct(const struct fb_info *info, u32 vclk_per,
19                             struct pll_ct *pll);
20 static int aty_dsp_gt(const struct fb_info *info, u8 bpp,
21                       struct pll_ct *pll);
22 static int aty_var_to_pll_ct(const struct fb_info *info, u32 vclk_per,
23                              u8 bpp, union aty_pll *pll);
24 static u32 aty_pll_ct_to_var(const struct fb_info *info,
25                              const union aty_pll *pll);
26
27
28
29 static void aty_st_pll(int offset, u8 val, const struct atyfb_par *par)
30 {
31         /* write addr byte */
32         aty_st_8(CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN, par);
33         /* write the register value */
34         aty_st_8(CLOCK_CNTL + 2, val, par);
35         aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN, par);
36 }
37
38
39 /* ------------------------------------------------------------------------- */
40
41     /*
42      *  PLL programming (Mach64 CT family)
43      */
44
45 static int aty_dsp_gt(const struct fb_info *info, u8 bpp,
46                       struct pll_ct *pll)
47 {
48         struct atyfb_par *par = (struct atyfb_par *) info->par;
49         u32 dsp_xclks_per_row, dsp_loop_latency, dsp_precision, dsp_off,
50             dsp_on;
51         u32 xclks_per_row, fifo_off, fifo_on, y, fifo_size, page_size;
52
53         /* xclocks_per_row<<11 */
54         xclks_per_row =
55             (pll->mclk_fb_div * pll->vclk_post_div_real * 64 << 11) /
56             (pll->vclk_fb_div * pll->mclk_post_div_real * bpp);
57         if (xclks_per_row < (1 << 11))
58                 FAIL("Dotclock to high");
59         if (M64_HAS(FIFO_24)) {
60                 fifo_size = 24;
61                 dsp_loop_latency = 0;
62         } else {
63                 fifo_size = 32;
64                 dsp_loop_latency = 2;
65         }
66         dsp_precision = 0;
67         y = (xclks_per_row * fifo_size) >> 11;
68         while (y) {
69                 y >>= 1;
70                 dsp_precision++;
71         }
72         dsp_precision -= 5;
73         /* fifo_off<<6 */
74         fifo_off = ((xclks_per_row * (fifo_size - 1)) >> 5) + (3 << 6);
75
76         if (info->fix.smem_len > 1 * 1024 * 1024) {
77                 if (par->ram_type >= SDRAM) {
78                         /* >1 MB SDRAM */
79                         dsp_loop_latency += 8;
80                         page_size = 8;
81                 } else {
82                         /* >1 MB DRAM */
83                         dsp_loop_latency += 6;
84                         page_size = 9;
85                 }
86         } else {
87                 if (par->ram_type >= SDRAM) {
88                         /* <2 MB SDRAM */
89                         dsp_loop_latency += 9;
90                         page_size = 10;
91                 } else {
92                         /* <2 MB DRAM */
93                         dsp_loop_latency += 8;
94                         page_size = 10;
95                 }
96         }
97         /* fifo_on<<6 */
98         if (xclks_per_row >= (page_size << 11))
99                 fifo_on =
100                     ((2 * page_size + 1) << 6) + (xclks_per_row >> 5);
101         else
102                 fifo_on = (3 * page_size + 2) << 6;
103
104         dsp_xclks_per_row = xclks_per_row >> dsp_precision;
105         dsp_on = fifo_on >> dsp_precision;
106         dsp_off = fifo_off >> dsp_precision;
107
108         pll->dsp_config = (dsp_xclks_per_row & 0x3fff) |
109             ((dsp_loop_latency & 0xf) << 16) | ((dsp_precision & 7) << 20);
110         pll->dsp_on_off = (dsp_on & 0x7ff) | ((dsp_off & 0x7ff) << 16);
111         return 0;
112 }
113
114 static int aty_valid_pll_ct(const struct fb_info *info, u32 vclk_per,
115                             struct pll_ct *pll)
116 {
117         struct atyfb_par *par = (struct atyfb_par *) info->par;
118         u32 q, x;               /* x is a workaround for sparc64-linux-gcc */
119         x = x;                  /* x is a workaround for sparc64-linux-gcc */
120
121         pll->pll_ref_div = par->pll_per * 2 * 255 / par->ref_clk_per;
122
123         /* FIXME: use the VTB/GTB /3 post divider if it's better suited */
124         q = par->ref_clk_per * pll->pll_ref_div * 4 / par->mclk_per;    /* actually 8*q */
125         if (q < 16 * 8 || q > 255 * 8)
126                 FAIL("mclk out of range");
127         else if (q < 32 * 8)
128                 pll->mclk_post_div_real = 8;
129         else if (q < 64 * 8)
130                 pll->mclk_post_div_real = 4;
131         else if (q < 128 * 8)
132                 pll->mclk_post_div_real = 2;
133         else
134                 pll->mclk_post_div_real = 1;
135         pll->mclk_fb_div = q * pll->mclk_post_div_real / 8;
136
137         /* FIXME: use the VTB/GTB /{3,6,12} post dividers if they're better suited */
138         q = par->ref_clk_per * pll->pll_ref_div * 4 / vclk_per; /* actually 8*q */
139         if (q < 16 * 8 || q > 255 * 8)
140                 FAIL("vclk out of range");
141         else if (q < 32 * 8)
142                 pll->vclk_post_div_real = 8;
143         else if (q < 64 * 8)
144                 pll->vclk_post_div_real = 4;
145         else if (q < 128 * 8)
146                 pll->vclk_post_div_real = 2;
147         else
148                 pll->vclk_post_div_real = 1;
149         pll->vclk_fb_div = q * pll->vclk_post_div_real / 8;
150         return 0;
151 }
152
153 void aty_calc_pll_ct(const struct fb_info *info, struct pll_ct *pll)
154 {
155         struct atyfb_par *par = (struct atyfb_par *) info->par;
156         u8 mpostdiv = 0;
157         u8 vpostdiv = 0;
158
159         if (M64_HAS(SDRAM_MAGIC_PLL) && (par->ram_type >= SDRAM))
160                 pll->pll_gen_cntl = 0x04;
161         else
162                 pll->pll_gen_cntl = 0x84;
163
164         switch (pll->mclk_post_div_real) {
165         case 1:
166                 mpostdiv = 0;
167                 break;
168         case 2:
169                 mpostdiv = 1;
170                 break;
171         case 3:
172                 mpostdiv = 4;
173                 break;
174         case 4:
175                 mpostdiv = 2;
176                 break;
177         case 8:
178                 mpostdiv = 3;
179                 break;
180         }
181         pll->pll_gen_cntl |= mpostdiv << 4;     /* mclk */
182
183         if (M64_HAS(MAGIC_POSTDIV))
184                 pll->pll_ext_cntl = 0;
185         else
186                 pll->pll_ext_cntl = mpostdiv;   /* xclk == mclk */
187
188         switch (pll->vclk_post_div_real) {
189         case 2:
190                 vpostdiv = 1;
191                 break;
192         case 3:
193                 pll->pll_ext_cntl |= 0x10;
194         case 1:
195                 vpostdiv = 0;
196                 break;
197         case 6:
198                 pll->pll_ext_cntl |= 0x10;
199         case 4:
200                 vpostdiv = 2;
201                 break;
202         case 12:
203                 pll->pll_ext_cntl |= 0x10;
204         case 8:
205                 vpostdiv = 3;
206                 break;
207         }
208
209         pll->pll_vclk_cntl = 0x03;      /* VCLK = PLL_VCLK/VCLKx_POST */
210         pll->vclk_post_div = vpostdiv;
211 }
212
213 static int aty_var_to_pll_ct(const struct fb_info *info, u32 vclk_per,
214                              u8 bpp, union aty_pll *pll)
215 {
216         struct atyfb_par *par = (struct atyfb_par *) info->par;
217         int err;
218
219         if ((err = aty_valid_pll_ct(info, vclk_per, &pll->ct)))
220                 return err;
221         if (M64_HAS(GTB_DSP) && (err = aty_dsp_gt(info, bpp, &pll->ct)))
222                 return err;
223         aty_calc_pll_ct(info, &pll->ct);
224         return 0;
225 }
226
227 static u32 aty_pll_ct_to_var(const struct fb_info *info,
228                              const union aty_pll *pll)
229 {
230         struct atyfb_par *par = (struct atyfb_par *) info->par;
231
232         u32 ref_clk_per = par->ref_clk_per;
233         u8 pll_ref_div = pll->ct.pll_ref_div;
234         u8 vclk_fb_div = pll->ct.vclk_fb_div;
235         u8 vclk_post_div = pll->ct.vclk_post_div_real;
236
237         return ref_clk_per * pll_ref_div * vclk_post_div / vclk_fb_div / 2;
238 }
239
240 void aty_set_pll_ct(const struct fb_info *info,
241                     const union aty_pll *pll)
242 {
243         struct atyfb_par *par = (struct atyfb_par *) info->par;
244
245         aty_st_pll(PLL_REF_DIV, pll->ct.pll_ref_div, par);
246         aty_st_pll(PLL_GEN_CNTL, pll->ct.pll_gen_cntl, par);
247         aty_st_pll(MCLK_FB_DIV, pll->ct.mclk_fb_div, par);
248         aty_st_pll(PLL_VCLK_CNTL, pll->ct.pll_vclk_cntl, par);
249         aty_st_pll(VCLK_POST_DIV, pll->ct.vclk_post_div, par);
250         aty_st_pll(VCLK0_FB_DIV, pll->ct.vclk_fb_div, par);
251         aty_st_pll(PLL_EXT_CNTL, pll->ct.pll_ext_cntl, par);
252
253         if (M64_HAS(GTB_DSP)) {
254                 if (M64_HAS(XL_DLL))
255                         aty_st_pll(DLL_CNTL, 0x80, par);
256                 else if (par->ram_type >= SDRAM)
257                         aty_st_pll(DLL_CNTL, 0xa6, par);
258                 else
259                         aty_st_pll(DLL_CNTL, 0xa0, par);
260                 aty_st_pll(VFC_CNTL, 0x1b, par);
261                 aty_st_le32(DSP_CONFIG, pll->ct.dsp_config, par);
262                 aty_st_le32(DSP_ON_OFF, pll->ct.dsp_on_off, par);
263         }
264 }
265
266 static int dummy(void)
267 {
268         return 0;
269 }
270
271 const struct aty_dac_ops aty_dac_ct = {
272         .set_dac        = (void *) dummy,
273 };
274
275 const struct aty_pll_ops aty_pll_ct = {
276         .var_to_pll     = aty_var_to_pll_ct,
277         .pll_to_var     = aty_pll_ct_to_var,
278         .set_pll        = aty_set_pll_ct,
279 };