3 * ATI Mach64 CT/VT/GT/LT Support
10 #include <video/mach64.h>
14 /* FIXME: remove the FAIL definition */
15 #define FAIL(x) do { printk(x "\n"); return -EINVAL; } while (0)
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,
20 static int aty_dsp_gt(const struct fb_info *info, u8 bpp,
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);
29 static void aty_st_pll(int offset, u8 val, const struct atyfb_par *par)
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);
39 /* ------------------------------------------------------------------------- */
42 * PLL programming (Mach64 CT family)
45 static int aty_dsp_gt(const struct fb_info *info, u8 bpp,
48 struct atyfb_par *par = (struct atyfb_par *) info->par;
49 u32 dsp_xclks_per_row, dsp_loop_latency, dsp_precision, dsp_off,
51 u32 xclks_per_row, fifo_off, fifo_on, y, fifo_size, page_size;
53 /* xclocks_per_row<<11 */
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)) {
67 y = (xclks_per_row * fifo_size) >> 11;
74 fifo_off = ((xclks_per_row * (fifo_size - 1)) >> 5) + (3 << 6);
76 if (info->fix.smem_len > 1 * 1024 * 1024) {
77 if (par->ram_type >= SDRAM) {
79 dsp_loop_latency += 8;
83 dsp_loop_latency += 6;
87 if (par->ram_type >= SDRAM) {
89 dsp_loop_latency += 9;
93 dsp_loop_latency += 8;
98 if (xclks_per_row >= (page_size << 11))
100 ((2 * page_size + 1) << 6) + (xclks_per_row >> 5);
102 fifo_on = (3 * page_size + 2) << 6;
104 dsp_xclks_per_row = xclks_per_row >> dsp_precision;
105 dsp_on = fifo_on >> dsp_precision;
106 dsp_off = fifo_off >> dsp_precision;
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);
114 static int aty_valid_pll_ct(const struct fb_info *info, u32 vclk_per,
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 */
121 pll->pll_ref_div = par->pll_per * 2 * 255 / par->ref_clk_per;
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");
128 pll->mclk_post_div_real = 8;
130 pll->mclk_post_div_real = 4;
131 else if (q < 128 * 8)
132 pll->mclk_post_div_real = 2;
134 pll->mclk_post_div_real = 1;
135 pll->mclk_fb_div = q * pll->mclk_post_div_real / 8;
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");
142 pll->vclk_post_div_real = 8;
144 pll->vclk_post_div_real = 4;
145 else if (q < 128 * 8)
146 pll->vclk_post_div_real = 2;
148 pll->vclk_post_div_real = 1;
149 pll->vclk_fb_div = q * pll->vclk_post_div_real / 8;
153 void aty_calc_pll_ct(const struct fb_info *info, struct pll_ct *pll)
155 struct atyfb_par *par = (struct atyfb_par *) info->par;
159 if (M64_HAS(SDRAM_MAGIC_PLL) && (par->ram_type >= SDRAM))
160 pll->pll_gen_cntl = 0x04;
162 pll->pll_gen_cntl = 0x84;
164 switch (pll->mclk_post_div_real) {
181 pll->pll_gen_cntl |= mpostdiv << 4; /* mclk */
183 if (M64_HAS(MAGIC_POSTDIV))
184 pll->pll_ext_cntl = 0;
186 pll->pll_ext_cntl = mpostdiv; /* xclk == mclk */
188 switch (pll->vclk_post_div_real) {
193 pll->pll_ext_cntl |= 0x10;
198 pll->pll_ext_cntl |= 0x10;
203 pll->pll_ext_cntl |= 0x10;
209 pll->pll_vclk_cntl = 0x03; /* VCLK = PLL_VCLK/VCLKx_POST */
210 pll->vclk_post_div = vpostdiv;
213 static int aty_var_to_pll_ct(const struct fb_info *info, u32 vclk_per,
214 u8 bpp, union aty_pll *pll)
216 struct atyfb_par *par = (struct atyfb_par *) info->par;
219 if ((err = aty_valid_pll_ct(info, vclk_per, &pll->ct)))
221 if (M64_HAS(GTB_DSP) && (err = aty_dsp_gt(info, bpp, &pll->ct)))
223 aty_calc_pll_ct(info, &pll->ct);
227 static u32 aty_pll_ct_to_var(const struct fb_info *info,
228 const union aty_pll *pll)
230 struct atyfb_par *par = (struct atyfb_par *) info->par;
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;
237 return ref_clk_per * pll_ref_div * vclk_post_div / vclk_fb_div / 2;
240 void aty_set_pll_ct(const struct fb_info *info,
241 const union aty_pll *pll)
243 struct atyfb_par *par = (struct atyfb_par *) info->par;
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);
253 if (M64_HAS(GTB_DSP)) {
255 aty_st_pll(DLL_CNTL, 0x80, par);
256 else if (par->ram_type >= SDRAM)
257 aty_st_pll(DLL_CNTL, 0xa6, par);
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);
266 static int dummy(void)
271 const struct aty_dac_ops aty_dac_ct = {
272 .set_dac = (void *) dummy,
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,