518 lines · c
1/*2 *3 * Hardware accelerated Matrox PCI cards - G450/G550 PLL control.4 *5 * (c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz>6 *7 * Portions Copyright (c) 2001 Matrox Graphics Inc.8 *9 * Version: 1.64 2002/06/1010 *11 * This file is subject to the terms and conditions of the GNU General Public12 * License. See the file COPYING in the main directory of this archive for13 * more details.14 *15 */16 17#include "g450_pll.h"18#include "matroxfb_DAC1064.h"19 20static inline unsigned int g450_vco2f(unsigned char p, unsigned int fvco) {21 return (p & 0x40) ? fvco : fvco >> ((p & 3) + 1);22}23 24static inline unsigned int g450_f2vco(unsigned char p, unsigned int fin) {25 return (p & 0x40) ? fin : fin << ((p & 3) + 1);26}27 28static unsigned int g450_mnp2vco(const struct matrox_fb_info *minfo,29 unsigned int mnp)30{31 unsigned int m, n;32 33 m = ((mnp >> 16) & 0x0FF) + 1;34 n = ((mnp >> 7) & 0x1FE) + 4;35 return (minfo->features.pll.ref_freq * n + (m >> 1)) / m;36}37 38unsigned int g450_mnp2f(const struct matrox_fb_info *minfo, unsigned int mnp)39{40 return g450_vco2f(mnp, g450_mnp2vco(minfo, mnp));41}42 43static inline unsigned int pll_freq_delta(unsigned int f1, unsigned int f2) {44 if (f2 < f1) {45 f2 = f1 - f2;46 } else {47 f2 = f2 - f1;48 }49 return f2;50}51 52#define NO_MORE_MNP 0x01FFFFFF53#define G450_MNP_FREQBITS (0xFFFFFF43) /* do not mask high byte so we'll catch NO_MORE_MNP */54 55static unsigned int g450_nextpll(const struct matrox_fb_info *minfo,56 const struct matrox_pll_limits *pi,57 unsigned int *fvco, unsigned int mnp)58{59 unsigned int m, n, p;60 unsigned int tvco = *fvco;61 62 m = (mnp >> 16) & 0xFF;63 p = mnp & 0xFF;64 65 do {66 if (m == 0 || m == 0xFF) {67 if (m == 0) {68 if (p & 0x40) {69 return NO_MORE_MNP;70 }71 if (p & 3) {72 p--;73 } else {74 p = 0x40;75 }76 tvco >>= 1;77 if (tvco < pi->vcomin) {78 return NO_MORE_MNP;79 }80 *fvco = tvco;81 }82 83 p &= 0x43;84 if (tvco < 550000) {85/* p |= 0x00; */86 } else if (tvco < 700000) {87 p |= 0x08;88 } else if (tvco < 1000000) {89 p |= 0x10;90 } else if (tvco < 1150000) {91 p |= 0x18;92 } else {93 p |= 0x20;94 }95 m = 9;96 } else {97 m--;98 }99 n = ((tvco * (m+1) + minfo->features.pll.ref_freq) / (minfo->features.pll.ref_freq * 2)) - 2;100 } while (n < 0x03 || n > 0x7A);101 return (m << 16) | (n << 8) | p;102}103 104static unsigned int g450_firstpll(const struct matrox_fb_info *minfo,105 const struct matrox_pll_limits *pi,106 unsigned int *vco, unsigned int fout)107{108 unsigned int p;109 unsigned int vcomax;110 111 vcomax = pi->vcomax;112 if (fout > (vcomax / 2)) {113 if (fout > vcomax) {114 *vco = vcomax;115 } else {116 *vco = fout;117 }118 p = 0x40;119 } else {120 unsigned int tvco;121 122 p = 3;123 tvco = g450_f2vco(p, fout);124 while (p && (tvco > vcomax)) {125 p--;126 tvco >>= 1;127 }128 if (tvco < pi->vcomin) {129 tvco = pi->vcomin;130 }131 *vco = tvco;132 }133 return g450_nextpll(minfo, pi, vco, 0xFF0000 | p);134}135 136static inline unsigned int g450_setpll(const struct matrox_fb_info *minfo,137 unsigned int mnp, unsigned int pll)138{139 switch (pll) {140 case M_PIXEL_PLL_A:141 matroxfb_DAC_out(minfo, M1064_XPIXPLLAM, mnp >> 16);142 matroxfb_DAC_out(minfo, M1064_XPIXPLLAN, mnp >> 8);143 matroxfb_DAC_out(minfo, M1064_XPIXPLLAP, mnp);144 return M1064_XPIXPLLSTAT;145 146 case M_PIXEL_PLL_B:147 matroxfb_DAC_out(minfo, M1064_XPIXPLLBM, mnp >> 16);148 matroxfb_DAC_out(minfo, M1064_XPIXPLLBN, mnp >> 8);149 matroxfb_DAC_out(minfo, M1064_XPIXPLLBP, mnp);150 return M1064_XPIXPLLSTAT;151 152 case M_PIXEL_PLL_C:153 matroxfb_DAC_out(minfo, M1064_XPIXPLLCM, mnp >> 16);154 matroxfb_DAC_out(minfo, M1064_XPIXPLLCN, mnp >> 8);155 matroxfb_DAC_out(minfo, M1064_XPIXPLLCP, mnp);156 return M1064_XPIXPLLSTAT;157 158 case M_SYSTEM_PLL:159 matroxfb_DAC_out(minfo, DAC1064_XSYSPLLM, mnp >> 16);160 matroxfb_DAC_out(minfo, DAC1064_XSYSPLLN, mnp >> 8);161 matroxfb_DAC_out(minfo, DAC1064_XSYSPLLP, mnp);162 return DAC1064_XSYSPLLSTAT;163 164 case M_VIDEO_PLL:165 matroxfb_DAC_out(minfo, M1064_XVIDPLLM, mnp >> 16);166 matroxfb_DAC_out(minfo, M1064_XVIDPLLN, mnp >> 8);167 matroxfb_DAC_out(minfo, M1064_XVIDPLLP, mnp);168 return M1064_XVIDPLLSTAT;169 }170 return 0;171}172 173static inline unsigned int g450_cmppll(const struct matrox_fb_info *minfo,174 unsigned int mnp, unsigned int pll)175{176 unsigned char m = mnp >> 16;177 unsigned char n = mnp >> 8;178 unsigned char p = mnp;179 180 switch (pll) {181 case M_PIXEL_PLL_A:182 return (matroxfb_DAC_in(minfo, M1064_XPIXPLLAM) != m ||183 matroxfb_DAC_in(minfo, M1064_XPIXPLLAN) != n ||184 matroxfb_DAC_in(minfo, M1064_XPIXPLLAP) != p);185 186 case M_PIXEL_PLL_B:187 return (matroxfb_DAC_in(minfo, M1064_XPIXPLLBM) != m ||188 matroxfb_DAC_in(minfo, M1064_XPIXPLLBN) != n ||189 matroxfb_DAC_in(minfo, M1064_XPIXPLLBP) != p);190 191 case M_PIXEL_PLL_C:192 return (matroxfb_DAC_in(minfo, M1064_XPIXPLLCM) != m ||193 matroxfb_DAC_in(minfo, M1064_XPIXPLLCN) != n ||194 matroxfb_DAC_in(minfo, M1064_XPIXPLLCP) != p);195 196 case M_SYSTEM_PLL:197 return (matroxfb_DAC_in(minfo, DAC1064_XSYSPLLM) != m ||198 matroxfb_DAC_in(minfo, DAC1064_XSYSPLLN) != n ||199 matroxfb_DAC_in(minfo, DAC1064_XSYSPLLP) != p);200 201 case M_VIDEO_PLL:202 return (matroxfb_DAC_in(minfo, M1064_XVIDPLLM) != m ||203 matroxfb_DAC_in(minfo, M1064_XVIDPLLN) != n ||204 matroxfb_DAC_in(minfo, M1064_XVIDPLLP) != p);205 }206 return 1;207}208 209static inline int g450_isplllocked(const struct matrox_fb_info *minfo,210 unsigned int regidx)211{212 unsigned int j;213 214 for (j = 0; j < 1000; j++) {215 if (matroxfb_DAC_in(minfo, regidx) & 0x40) {216 unsigned int r = 0;217 int i;218 219 for (i = 0; i < 100; i++) {220 r += matroxfb_DAC_in(minfo, regidx) & 0x40;221 }222 return r >= (90 * 0x40);223 }224 /* udelay(1)... but DAC_in is much slower... */225 }226 return 0;227}228 229static int g450_testpll(const struct matrox_fb_info *minfo, unsigned int mnp,230 unsigned int pll)231{232 return g450_isplllocked(minfo, g450_setpll(minfo, mnp, pll));233}234 235static void updatehwstate_clk(struct matrox_hw_state* hw, unsigned int mnp, unsigned int pll) {236 switch (pll) {237 case M_SYSTEM_PLL:238 hw->DACclk[3] = mnp >> 16;239 hw->DACclk[4] = mnp >> 8;240 hw->DACclk[5] = mnp;241 break;242 }243}244 245void matroxfb_g450_setpll_cond(struct matrox_fb_info *minfo, unsigned int mnp,246 unsigned int pll)247{248 if (g450_cmppll(minfo, mnp, pll)) {249 g450_setpll(minfo, mnp, pll);250 }251}252 253static inline unsigned int g450_findworkingpll(struct matrox_fb_info *minfo,254 unsigned int pll,255 unsigned int *mnparray,256 unsigned int mnpcount)257{258 unsigned int found = 0;259 unsigned int idx;260 unsigned int mnpfound = mnparray[0];261 262 for (idx = 0; idx < mnpcount; idx++) {263 unsigned int sarray[3];264 unsigned int *sptr;265 {266 unsigned int mnp;267 268 sptr = sarray;269 mnp = mnparray[idx];270 if (mnp & 0x38) {271 *sptr++ = mnp - 8;272 }273 if ((mnp & 0x38) != 0x38) {274 *sptr++ = mnp + 8;275 }276 *sptr = mnp;277 }278 while (sptr >= sarray) {279 unsigned int mnp = *sptr--;280 281 if (g450_testpll(minfo, mnp - 0x0300, pll) &&282 g450_testpll(minfo, mnp + 0x0300, pll) &&283 g450_testpll(minfo, mnp - 0x0200, pll) &&284 g450_testpll(minfo, mnp + 0x0200, pll) &&285 g450_testpll(minfo, mnp - 0x0100, pll) &&286 g450_testpll(minfo, mnp + 0x0100, pll)) {287 if (g450_testpll(minfo, mnp, pll)) {288 return mnp;289 }290 } else if (!found && g450_testpll(minfo, mnp, pll)) {291 mnpfound = mnp;292 found = 1;293 }294 }295 }296 g450_setpll(minfo, mnpfound, pll);297 return mnpfound;298}299 300static void g450_addcache(struct matrox_pll_cache* ci, unsigned int mnp_key, unsigned int mnp_value) {301 if (++ci->valid > ARRAY_SIZE(ci->data)) {302 ci->valid = ARRAY_SIZE(ci->data);303 }304 memmove(ci->data + 1, ci->data, (ci->valid - 1) * sizeof(*ci->data));305 ci->data[0].mnp_key = mnp_key & G450_MNP_FREQBITS;306 ci->data[0].mnp_value = mnp_value;307}308 309static int g450_checkcache(struct matrox_fb_info *minfo,310 struct matrox_pll_cache *ci, unsigned int mnp_key)311{312 unsigned int i;313 314 mnp_key &= G450_MNP_FREQBITS;315 for (i = 0; i < ci->valid; i++) {316 if (ci->data[i].mnp_key == mnp_key) {317 unsigned int mnp;318 319 mnp = ci->data[i].mnp_value;320 if (i) {321 memmove(ci->data + 1, ci->data, i * sizeof(*ci->data));322 ci->data[0].mnp_key = mnp_key;323 ci->data[0].mnp_value = mnp;324 }325 return mnp;326 }327 }328 return NO_MORE_MNP;329}330 331static int __g450_setclk(struct matrox_fb_info *minfo, unsigned int fout,332 unsigned int pll, unsigned int *mnparray,333 unsigned int *deltaarray)334{335 unsigned int mnpcount;336 const struct matrox_pll_limits* pi;337 struct matrox_pll_cache* ci;338 339 switch (pll) {340 case M_PIXEL_PLL_A:341 case M_PIXEL_PLL_B:342 case M_PIXEL_PLL_C:343 {344 u_int8_t tmp, xpwrctrl;345 unsigned long flags;346 347 matroxfb_DAC_lock_irqsave(flags);348 349 xpwrctrl = matroxfb_DAC_in(minfo, M1064_XPWRCTRL);350 matroxfb_DAC_out(minfo, M1064_XPWRCTRL, xpwrctrl & ~M1064_XPWRCTRL_PANELPDN);351 mga_outb(M_SEQ_INDEX, M_SEQ1);352 mga_outb(M_SEQ_DATA, mga_inb(M_SEQ_DATA) | M_SEQ1_SCROFF);353 tmp = matroxfb_DAC_in(minfo, M1064_XPIXCLKCTRL);354 tmp |= M1064_XPIXCLKCTRL_DIS;355 if (!(tmp & M1064_XPIXCLKCTRL_PLL_UP)) {356 tmp |= M1064_XPIXCLKCTRL_PLL_UP;357 }358 matroxfb_DAC_out(minfo, M1064_XPIXCLKCTRL, tmp);359 /* DVI PLL preferred for frequencies up to360 panel link max, standard PLL otherwise */361 if (fout >= minfo->max_pixel_clock_panellink)362 tmp = 0;363 else tmp =364 M1064_XDVICLKCTRL_DVIDATAPATHSEL |365 M1064_XDVICLKCTRL_C1DVICLKSEL |366 M1064_XDVICLKCTRL_C1DVICLKEN |367 M1064_XDVICLKCTRL_DVILOOPCTL |368 M1064_XDVICLKCTRL_P1LOOPBWDTCTL;369 /* Setting this breaks PC systems so don't do it */370 /* matroxfb_DAC_out(minfo, M1064_XDVICLKCTRL, tmp); */371 matroxfb_DAC_out(minfo, M1064_XPWRCTRL,372 xpwrctrl);373 374 matroxfb_DAC_unlock_irqrestore(flags);375 }376 {377 u_int8_t misc;378 379 misc = mga_inb(M_MISC_REG_READ) & ~0x0C;380 switch (pll) {381 case M_PIXEL_PLL_A:382 break;383 case M_PIXEL_PLL_B:384 misc |= 0x04;385 break;386 default:387 misc |= 0x0C;388 break;389 }390 mga_outb(M_MISC_REG, misc);391 }392 pi = &minfo->limits.pixel;393 ci = &minfo->cache.pixel;394 break;395 case M_SYSTEM_PLL:396 {397 u_int32_t opt;398 399 pci_read_config_dword(minfo->pcidev, PCI_OPTION_REG, &opt);400 if (!(opt & 0x20)) {401 pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, opt | 0x20);402 }403 }404 pi = &minfo->limits.system;405 ci = &minfo->cache.system;406 break;407 case M_VIDEO_PLL:408 {409 u_int8_t tmp;410 unsigned int mnp;411 unsigned long flags;412 413 matroxfb_DAC_lock_irqsave(flags);414 tmp = matroxfb_DAC_in(minfo, M1064_XPWRCTRL);415 if (!(tmp & 2)) {416 matroxfb_DAC_out(minfo, M1064_XPWRCTRL, tmp | 2);417 }418 419 mnp = matroxfb_DAC_in(minfo, M1064_XPIXPLLCM) << 16;420 mnp |= matroxfb_DAC_in(minfo, M1064_XPIXPLLCN) << 8;421 matroxfb_DAC_unlock_irqrestore(flags);422 }423 pi = &minfo->limits.video;424 ci = &minfo->cache.video;425 break;426 default:427 return -EINVAL;428 }429 430 mnpcount = 0;431 {432 unsigned int mnp;433 unsigned int xvco;434 435 for (mnp = g450_firstpll(minfo, pi, &xvco, fout); mnp != NO_MORE_MNP; mnp = g450_nextpll(minfo, pi, &xvco, mnp)) {436 unsigned int idx;437 unsigned int vco;438 unsigned int delta;439 440 vco = g450_mnp2vco(minfo, mnp);441 delta = pll_freq_delta(fout, g450_vco2f(mnp, vco));442 for (idx = mnpcount; idx > 0; idx--) {443 /* == is important; due to nextpll algorithm we get444 sorted equally good frequencies from lower VCO 445 frequency to higher - with <= lowest wins, while446 with < highest one wins */447 if (delta <= deltaarray[idx-1]) {448 /* all else being equal except VCO,449 * choose VCO not near (within 1/16th or so) VCOmin450 * (freqs near VCOmin aren't as stable)451 */452 if (delta == deltaarray[idx-1]453 && vco != g450_mnp2vco(minfo, mnparray[idx-1])454 && vco < (pi->vcomin * 17 / 16)) {455 break;456 }457 mnparray[idx] = mnparray[idx-1];458 deltaarray[idx] = deltaarray[idx-1];459 } else {460 break;461 }462 }463 mnparray[idx] = mnp;464 deltaarray[idx] = delta;465 mnpcount++;466 }467 }468 /* VideoPLL and PixelPLL matched: do nothing... In all other cases we should get at least one frequency */469 if (!mnpcount) {470 return -EBUSY;471 }472 {473 unsigned long flags;474 unsigned int mnp;475 476 matroxfb_DAC_lock_irqsave(flags);477 mnp = g450_checkcache(minfo, ci, mnparray[0]);478 if (mnp != NO_MORE_MNP) {479 matroxfb_g450_setpll_cond(minfo, mnp, pll);480 } else {481 mnp = g450_findworkingpll(minfo, pll, mnparray, mnpcount);482 g450_addcache(ci, mnparray[0], mnp);483 }484 updatehwstate_clk(&minfo->hw, mnp, pll);485 matroxfb_DAC_unlock_irqrestore(flags);486 return mnp;487 }488}489 490/* It must be greater than number of possible PLL values.491 * Currently there is 5(p) * 10(m) = 50 possible values. */492#define MNP_TABLE_SIZE 64493 494int matroxfb_g450_setclk(struct matrox_fb_info *minfo, unsigned int fout,495 unsigned int pll)496{497 unsigned int* arr;498 499 arr = kmalloc(sizeof(*arr) * MNP_TABLE_SIZE * 2, GFP_KERNEL);500 if (arr) {501 int r;502 503 r = __g450_setclk(minfo, fout, pll, arr, arr + MNP_TABLE_SIZE);504 kfree(arr);505 return r;506 }507 return -ENOMEM;508}509 510EXPORT_SYMBOL(matroxfb_g450_setclk);511EXPORT_SYMBOL(g450_mnp2f);512EXPORT_SYMBOL(matroxfb_g450_setpll_cond);513 514MODULE_AUTHOR("(c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");515MODULE_DESCRIPTION("Matrox G450/G550 PLL driver");516 517MODULE_LICENSE("GPL");518