18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * Hardware accelerated Matrox PCI cards - G450/G550 PLL control. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Portions Copyright (c) 2001 Matrox Graphics Inc. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Version: 1.64 2002/06/10 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 128c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 138c2ecf20Sopenharmony_ci * more details. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "g450_pll.h" 188c2ecf20Sopenharmony_ci#include "matroxfb_DAC1064.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic inline unsigned int g450_vco2f(unsigned char p, unsigned int fvco) { 218c2ecf20Sopenharmony_ci return (p & 0x40) ? fvco : fvco >> ((p & 3) + 1); 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic inline unsigned int g450_f2vco(unsigned char p, unsigned int fin) { 258c2ecf20Sopenharmony_ci return (p & 0x40) ? fin : fin << ((p & 3) + 1); 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic unsigned int g450_mnp2vco(const struct matrox_fb_info *minfo, 298c2ecf20Sopenharmony_ci unsigned int mnp) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci unsigned int m, n; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci m = ((mnp >> 16) & 0x0FF) + 1; 348c2ecf20Sopenharmony_ci n = ((mnp >> 7) & 0x1FE) + 4; 358c2ecf20Sopenharmony_ci return (minfo->features.pll.ref_freq * n + (m >> 1)) / m; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ciunsigned int g450_mnp2f(const struct matrox_fb_info *minfo, unsigned int mnp) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return g450_vco2f(mnp, g450_mnp2vco(minfo, mnp)); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic inline unsigned int pll_freq_delta(unsigned int f1, unsigned int f2) { 448c2ecf20Sopenharmony_ci if (f2 < f1) { 458c2ecf20Sopenharmony_ci f2 = f1 - f2; 468c2ecf20Sopenharmony_ci } else { 478c2ecf20Sopenharmony_ci f2 = f2 - f1; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci return f2; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define NO_MORE_MNP 0x01FFFFFF 538c2ecf20Sopenharmony_ci#define G450_MNP_FREQBITS (0xFFFFFF43) /* do not mask high byte so we'll catch NO_MORE_MNP */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic unsigned int g450_nextpll(const struct matrox_fb_info *minfo, 568c2ecf20Sopenharmony_ci const struct matrox_pll_limits *pi, 578c2ecf20Sopenharmony_ci unsigned int *fvco, unsigned int mnp) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci unsigned int m, n, p; 608c2ecf20Sopenharmony_ci unsigned int tvco = *fvco; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci m = (mnp >> 16) & 0xFF; 638c2ecf20Sopenharmony_ci p = mnp & 0xFF; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci do { 668c2ecf20Sopenharmony_ci if (m == 0 || m == 0xFF) { 678c2ecf20Sopenharmony_ci if (m == 0) { 688c2ecf20Sopenharmony_ci if (p & 0x40) { 698c2ecf20Sopenharmony_ci return NO_MORE_MNP; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci if (p & 3) { 728c2ecf20Sopenharmony_ci p--; 738c2ecf20Sopenharmony_ci } else { 748c2ecf20Sopenharmony_ci p = 0x40; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci tvco >>= 1; 778c2ecf20Sopenharmony_ci if (tvco < pi->vcomin) { 788c2ecf20Sopenharmony_ci return NO_MORE_MNP; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci *fvco = tvco; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci p &= 0x43; 848c2ecf20Sopenharmony_ci if (tvco < 550000) { 858c2ecf20Sopenharmony_ci/* p |= 0x00; */ 868c2ecf20Sopenharmony_ci } else if (tvco < 700000) { 878c2ecf20Sopenharmony_ci p |= 0x08; 888c2ecf20Sopenharmony_ci } else if (tvco < 1000000) { 898c2ecf20Sopenharmony_ci p |= 0x10; 908c2ecf20Sopenharmony_ci } else if (tvco < 1150000) { 918c2ecf20Sopenharmony_ci p |= 0x18; 928c2ecf20Sopenharmony_ci } else { 938c2ecf20Sopenharmony_ci p |= 0x20; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci m = 9; 968c2ecf20Sopenharmony_ci } else { 978c2ecf20Sopenharmony_ci m--; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci n = ((tvco * (m+1) + minfo->features.pll.ref_freq) / (minfo->features.pll.ref_freq * 2)) - 2; 1008c2ecf20Sopenharmony_ci } while (n < 0x03 || n > 0x7A); 1018c2ecf20Sopenharmony_ci return (m << 16) | (n << 8) | p; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic unsigned int g450_firstpll(const struct matrox_fb_info *minfo, 1058c2ecf20Sopenharmony_ci const struct matrox_pll_limits *pi, 1068c2ecf20Sopenharmony_ci unsigned int *vco, unsigned int fout) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci unsigned int p; 1098c2ecf20Sopenharmony_ci unsigned int vcomax; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci vcomax = pi->vcomax; 1128c2ecf20Sopenharmony_ci if (fout > (vcomax / 2)) { 1138c2ecf20Sopenharmony_ci if (fout > vcomax) { 1148c2ecf20Sopenharmony_ci *vco = vcomax; 1158c2ecf20Sopenharmony_ci } else { 1168c2ecf20Sopenharmony_ci *vco = fout; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci p = 0x40; 1198c2ecf20Sopenharmony_ci } else { 1208c2ecf20Sopenharmony_ci unsigned int tvco; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci p = 3; 1238c2ecf20Sopenharmony_ci tvco = g450_f2vco(p, fout); 1248c2ecf20Sopenharmony_ci while (p && (tvco > vcomax)) { 1258c2ecf20Sopenharmony_ci p--; 1268c2ecf20Sopenharmony_ci tvco >>= 1; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci if (tvco < pi->vcomin) { 1298c2ecf20Sopenharmony_ci tvco = pi->vcomin; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci *vco = tvco; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci return g450_nextpll(minfo, pi, vco, 0xFF0000 | p); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic inline unsigned int g450_setpll(const struct matrox_fb_info *minfo, 1378c2ecf20Sopenharmony_ci unsigned int mnp, unsigned int pll) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci switch (pll) { 1408c2ecf20Sopenharmony_ci case M_PIXEL_PLL_A: 1418c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPIXPLLAM, mnp >> 16); 1428c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPIXPLLAN, mnp >> 8); 1438c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPIXPLLAP, mnp); 1448c2ecf20Sopenharmony_ci return M1064_XPIXPLLSTAT; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci case M_PIXEL_PLL_B: 1478c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPIXPLLBM, mnp >> 16); 1488c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPIXPLLBN, mnp >> 8); 1498c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPIXPLLBP, mnp); 1508c2ecf20Sopenharmony_ci return M1064_XPIXPLLSTAT; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci case M_PIXEL_PLL_C: 1538c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPIXPLLCM, mnp >> 16); 1548c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPIXPLLCN, mnp >> 8); 1558c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPIXPLLCP, mnp); 1568c2ecf20Sopenharmony_ci return M1064_XPIXPLLSTAT; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci case M_SYSTEM_PLL: 1598c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, DAC1064_XSYSPLLM, mnp >> 16); 1608c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, DAC1064_XSYSPLLN, mnp >> 8); 1618c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, DAC1064_XSYSPLLP, mnp); 1628c2ecf20Sopenharmony_ci return DAC1064_XSYSPLLSTAT; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci case M_VIDEO_PLL: 1658c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XVIDPLLM, mnp >> 16); 1668c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XVIDPLLN, mnp >> 8); 1678c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XVIDPLLP, mnp); 1688c2ecf20Sopenharmony_ci return M1064_XVIDPLLSTAT; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic inline unsigned int g450_cmppll(const struct matrox_fb_info *minfo, 1748c2ecf20Sopenharmony_ci unsigned int mnp, unsigned int pll) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci unsigned char m = mnp >> 16; 1778c2ecf20Sopenharmony_ci unsigned char n = mnp >> 8; 1788c2ecf20Sopenharmony_ci unsigned char p = mnp; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci switch (pll) { 1818c2ecf20Sopenharmony_ci case M_PIXEL_PLL_A: 1828c2ecf20Sopenharmony_ci return (matroxfb_DAC_in(minfo, M1064_XPIXPLLAM) != m || 1838c2ecf20Sopenharmony_ci matroxfb_DAC_in(minfo, M1064_XPIXPLLAN) != n || 1848c2ecf20Sopenharmony_ci matroxfb_DAC_in(minfo, M1064_XPIXPLLAP) != p); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci case M_PIXEL_PLL_B: 1878c2ecf20Sopenharmony_ci return (matroxfb_DAC_in(minfo, M1064_XPIXPLLBM) != m || 1888c2ecf20Sopenharmony_ci matroxfb_DAC_in(minfo, M1064_XPIXPLLBN) != n || 1898c2ecf20Sopenharmony_ci matroxfb_DAC_in(minfo, M1064_XPIXPLLBP) != p); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci case M_PIXEL_PLL_C: 1928c2ecf20Sopenharmony_ci return (matroxfb_DAC_in(minfo, M1064_XPIXPLLCM) != m || 1938c2ecf20Sopenharmony_ci matroxfb_DAC_in(minfo, M1064_XPIXPLLCN) != n || 1948c2ecf20Sopenharmony_ci matroxfb_DAC_in(minfo, M1064_XPIXPLLCP) != p); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci case M_SYSTEM_PLL: 1978c2ecf20Sopenharmony_ci return (matroxfb_DAC_in(minfo, DAC1064_XSYSPLLM) != m || 1988c2ecf20Sopenharmony_ci matroxfb_DAC_in(minfo, DAC1064_XSYSPLLN) != n || 1998c2ecf20Sopenharmony_ci matroxfb_DAC_in(minfo, DAC1064_XSYSPLLP) != p); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci case M_VIDEO_PLL: 2028c2ecf20Sopenharmony_ci return (matroxfb_DAC_in(minfo, M1064_XVIDPLLM) != m || 2038c2ecf20Sopenharmony_ci matroxfb_DAC_in(minfo, M1064_XVIDPLLN) != n || 2048c2ecf20Sopenharmony_ci matroxfb_DAC_in(minfo, M1064_XVIDPLLP) != p); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci return 1; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic inline int g450_isplllocked(const struct matrox_fb_info *minfo, 2108c2ecf20Sopenharmony_ci unsigned int regidx) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci unsigned int j; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci for (j = 0; j < 1000; j++) { 2158c2ecf20Sopenharmony_ci if (matroxfb_DAC_in(minfo, regidx) & 0x40) { 2168c2ecf20Sopenharmony_ci unsigned int r = 0; 2178c2ecf20Sopenharmony_ci int i; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 2208c2ecf20Sopenharmony_ci r += matroxfb_DAC_in(minfo, regidx) & 0x40; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci return r >= (90 * 0x40); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci /* udelay(1)... but DAC_in is much slower... */ 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int g450_testpll(const struct matrox_fb_info *minfo, unsigned int mnp, 2308c2ecf20Sopenharmony_ci unsigned int pll) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci return g450_isplllocked(minfo, g450_setpll(minfo, mnp, pll)); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic void updatehwstate_clk(struct matrox_hw_state* hw, unsigned int mnp, unsigned int pll) { 2368c2ecf20Sopenharmony_ci switch (pll) { 2378c2ecf20Sopenharmony_ci case M_SYSTEM_PLL: 2388c2ecf20Sopenharmony_ci hw->DACclk[3] = mnp >> 16; 2398c2ecf20Sopenharmony_ci hw->DACclk[4] = mnp >> 8; 2408c2ecf20Sopenharmony_ci hw->DACclk[5] = mnp; 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_civoid matroxfb_g450_setpll_cond(struct matrox_fb_info *minfo, unsigned int mnp, 2468c2ecf20Sopenharmony_ci unsigned int pll) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci if (g450_cmppll(minfo, mnp, pll)) { 2498c2ecf20Sopenharmony_ci g450_setpll(minfo, mnp, pll); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic inline unsigned int g450_findworkingpll(struct matrox_fb_info *minfo, 2548c2ecf20Sopenharmony_ci unsigned int pll, 2558c2ecf20Sopenharmony_ci unsigned int *mnparray, 2568c2ecf20Sopenharmony_ci unsigned int mnpcount) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci unsigned int found = 0; 2598c2ecf20Sopenharmony_ci unsigned int idx; 2608c2ecf20Sopenharmony_ci unsigned int mnpfound = mnparray[0]; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci for (idx = 0; idx < mnpcount; idx++) { 2638c2ecf20Sopenharmony_ci unsigned int sarray[3]; 2648c2ecf20Sopenharmony_ci unsigned int *sptr; 2658c2ecf20Sopenharmony_ci { 2668c2ecf20Sopenharmony_ci unsigned int mnp; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci sptr = sarray; 2698c2ecf20Sopenharmony_ci mnp = mnparray[idx]; 2708c2ecf20Sopenharmony_ci if (mnp & 0x38) { 2718c2ecf20Sopenharmony_ci *sptr++ = mnp - 8; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci if ((mnp & 0x38) != 0x38) { 2748c2ecf20Sopenharmony_ci *sptr++ = mnp + 8; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci *sptr = mnp; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci while (sptr >= sarray) { 2798c2ecf20Sopenharmony_ci unsigned int mnp = *sptr--; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (g450_testpll(minfo, mnp - 0x0300, pll) && 2828c2ecf20Sopenharmony_ci g450_testpll(minfo, mnp + 0x0300, pll) && 2838c2ecf20Sopenharmony_ci g450_testpll(minfo, mnp - 0x0200, pll) && 2848c2ecf20Sopenharmony_ci g450_testpll(minfo, mnp + 0x0200, pll) && 2858c2ecf20Sopenharmony_ci g450_testpll(minfo, mnp - 0x0100, pll) && 2868c2ecf20Sopenharmony_ci g450_testpll(minfo, mnp + 0x0100, pll)) { 2878c2ecf20Sopenharmony_ci if (g450_testpll(minfo, mnp, pll)) { 2888c2ecf20Sopenharmony_ci return mnp; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci } else if (!found && g450_testpll(minfo, mnp, pll)) { 2918c2ecf20Sopenharmony_ci mnpfound = mnp; 2928c2ecf20Sopenharmony_ci found = 1; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci g450_setpll(minfo, mnpfound, pll); 2978c2ecf20Sopenharmony_ci return mnpfound; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic void g450_addcache(struct matrox_pll_cache* ci, unsigned int mnp_key, unsigned int mnp_value) { 3018c2ecf20Sopenharmony_ci if (++ci->valid > ARRAY_SIZE(ci->data)) { 3028c2ecf20Sopenharmony_ci ci->valid = ARRAY_SIZE(ci->data); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci memmove(ci->data + 1, ci->data, (ci->valid - 1) * sizeof(*ci->data)); 3058c2ecf20Sopenharmony_ci ci->data[0].mnp_key = mnp_key & G450_MNP_FREQBITS; 3068c2ecf20Sopenharmony_ci ci->data[0].mnp_value = mnp_value; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int g450_checkcache(struct matrox_fb_info *minfo, 3108c2ecf20Sopenharmony_ci struct matrox_pll_cache *ci, unsigned int mnp_key) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci unsigned int i; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci mnp_key &= G450_MNP_FREQBITS; 3158c2ecf20Sopenharmony_ci for (i = 0; i < ci->valid; i++) { 3168c2ecf20Sopenharmony_ci if (ci->data[i].mnp_key == mnp_key) { 3178c2ecf20Sopenharmony_ci unsigned int mnp; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci mnp = ci->data[i].mnp_value; 3208c2ecf20Sopenharmony_ci if (i) { 3218c2ecf20Sopenharmony_ci memmove(ci->data + 1, ci->data, i * sizeof(*ci->data)); 3228c2ecf20Sopenharmony_ci ci->data[0].mnp_key = mnp_key; 3238c2ecf20Sopenharmony_ci ci->data[0].mnp_value = mnp; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci return mnp; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci return NO_MORE_MNP; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int __g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, 3328c2ecf20Sopenharmony_ci unsigned int pll, unsigned int *mnparray, 3338c2ecf20Sopenharmony_ci unsigned int *deltaarray) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci unsigned int mnpcount; 3368c2ecf20Sopenharmony_ci const struct matrox_pll_limits* pi; 3378c2ecf20Sopenharmony_ci struct matrox_pll_cache* ci; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci switch (pll) { 3408c2ecf20Sopenharmony_ci case M_PIXEL_PLL_A: 3418c2ecf20Sopenharmony_ci case M_PIXEL_PLL_B: 3428c2ecf20Sopenharmony_ci case M_PIXEL_PLL_C: 3438c2ecf20Sopenharmony_ci { 3448c2ecf20Sopenharmony_ci u_int8_t tmp, xpwrctrl; 3458c2ecf20Sopenharmony_ci unsigned long flags; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci matroxfb_DAC_lock_irqsave(flags); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci xpwrctrl = matroxfb_DAC_in(minfo, M1064_XPWRCTRL); 3508c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPWRCTRL, xpwrctrl & ~M1064_XPWRCTRL_PANELPDN); 3518c2ecf20Sopenharmony_ci mga_outb(M_SEQ_INDEX, M_SEQ1); 3528c2ecf20Sopenharmony_ci mga_outb(M_SEQ_DATA, mga_inb(M_SEQ_DATA) | M_SEQ1_SCROFF); 3538c2ecf20Sopenharmony_ci tmp = matroxfb_DAC_in(minfo, M1064_XPIXCLKCTRL); 3548c2ecf20Sopenharmony_ci tmp |= M1064_XPIXCLKCTRL_DIS; 3558c2ecf20Sopenharmony_ci if (!(tmp & M1064_XPIXCLKCTRL_PLL_UP)) { 3568c2ecf20Sopenharmony_ci tmp |= M1064_XPIXCLKCTRL_PLL_UP; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPIXCLKCTRL, tmp); 3598c2ecf20Sopenharmony_ci /* DVI PLL preferred for frequencies up to 3608c2ecf20Sopenharmony_ci panel link max, standard PLL otherwise */ 3618c2ecf20Sopenharmony_ci if (fout >= minfo->max_pixel_clock_panellink) 3628c2ecf20Sopenharmony_ci tmp = 0; 3638c2ecf20Sopenharmony_ci else tmp = 3648c2ecf20Sopenharmony_ci M1064_XDVICLKCTRL_DVIDATAPATHSEL | 3658c2ecf20Sopenharmony_ci M1064_XDVICLKCTRL_C1DVICLKSEL | 3668c2ecf20Sopenharmony_ci M1064_XDVICLKCTRL_C1DVICLKEN | 3678c2ecf20Sopenharmony_ci M1064_XDVICLKCTRL_DVILOOPCTL | 3688c2ecf20Sopenharmony_ci M1064_XDVICLKCTRL_P1LOOPBWDTCTL; 3698c2ecf20Sopenharmony_ci /* Setting this breaks PC systems so don't do it */ 3708c2ecf20Sopenharmony_ci /* matroxfb_DAC_out(minfo, M1064_XDVICLKCTRL, tmp); */ 3718c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPWRCTRL, 3728c2ecf20Sopenharmony_ci xpwrctrl); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci matroxfb_DAC_unlock_irqrestore(flags); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci { 3778c2ecf20Sopenharmony_ci u_int8_t misc; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci misc = mga_inb(M_MISC_REG_READ) & ~0x0C; 3808c2ecf20Sopenharmony_ci switch (pll) { 3818c2ecf20Sopenharmony_ci case M_PIXEL_PLL_A: 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci case M_PIXEL_PLL_B: 3848c2ecf20Sopenharmony_ci misc |= 0x04; 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci default: 3878c2ecf20Sopenharmony_ci misc |= 0x0C; 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci mga_outb(M_MISC_REG, misc); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci pi = &minfo->limits.pixel; 3938c2ecf20Sopenharmony_ci ci = &minfo->cache.pixel; 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci case M_SYSTEM_PLL: 3968c2ecf20Sopenharmony_ci { 3978c2ecf20Sopenharmony_ci u_int32_t opt; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci pci_read_config_dword(minfo->pcidev, PCI_OPTION_REG, &opt); 4008c2ecf20Sopenharmony_ci if (!(opt & 0x20)) { 4018c2ecf20Sopenharmony_ci pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, opt | 0x20); 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci pi = &minfo->limits.system; 4058c2ecf20Sopenharmony_ci ci = &minfo->cache.system; 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci case M_VIDEO_PLL: 4088c2ecf20Sopenharmony_ci { 4098c2ecf20Sopenharmony_ci u_int8_t tmp; 4108c2ecf20Sopenharmony_ci unsigned int mnp; 4118c2ecf20Sopenharmony_ci unsigned long flags; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci matroxfb_DAC_lock_irqsave(flags); 4148c2ecf20Sopenharmony_ci tmp = matroxfb_DAC_in(minfo, M1064_XPWRCTRL); 4158c2ecf20Sopenharmony_ci if (!(tmp & 2)) { 4168c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, M1064_XPWRCTRL, tmp | 2); 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci mnp = matroxfb_DAC_in(minfo, M1064_XPIXPLLCM) << 16; 4208c2ecf20Sopenharmony_ci mnp |= matroxfb_DAC_in(minfo, M1064_XPIXPLLCN) << 8; 4218c2ecf20Sopenharmony_ci matroxfb_DAC_unlock_irqrestore(flags); 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci pi = &minfo->limits.video; 4248c2ecf20Sopenharmony_ci ci = &minfo->cache.video; 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci default: 4278c2ecf20Sopenharmony_ci return -EINVAL; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci mnpcount = 0; 4318c2ecf20Sopenharmony_ci { 4328c2ecf20Sopenharmony_ci unsigned int mnp; 4338c2ecf20Sopenharmony_ci unsigned int xvco; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci for (mnp = g450_firstpll(minfo, pi, &xvco, fout); mnp != NO_MORE_MNP; mnp = g450_nextpll(minfo, pi, &xvco, mnp)) { 4368c2ecf20Sopenharmony_ci unsigned int idx; 4378c2ecf20Sopenharmony_ci unsigned int vco; 4388c2ecf20Sopenharmony_ci unsigned int delta; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci vco = g450_mnp2vco(minfo, mnp); 4418c2ecf20Sopenharmony_ci delta = pll_freq_delta(fout, g450_vco2f(mnp, vco)); 4428c2ecf20Sopenharmony_ci for (idx = mnpcount; idx > 0; idx--) { 4438c2ecf20Sopenharmony_ci /* == is important; due to nextpll algorithm we get 4448c2ecf20Sopenharmony_ci sorted equally good frequencies from lower VCO 4458c2ecf20Sopenharmony_ci frequency to higher - with <= lowest wins, while 4468c2ecf20Sopenharmony_ci with < highest one wins */ 4478c2ecf20Sopenharmony_ci if (delta <= deltaarray[idx-1]) { 4488c2ecf20Sopenharmony_ci /* all else being equal except VCO, 4498c2ecf20Sopenharmony_ci * choose VCO not near (within 1/16th or so) VCOmin 4508c2ecf20Sopenharmony_ci * (freqs near VCOmin aren't as stable) 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci if (delta == deltaarray[idx-1] 4538c2ecf20Sopenharmony_ci && vco != g450_mnp2vco(minfo, mnparray[idx-1]) 4548c2ecf20Sopenharmony_ci && vco < (pi->vcomin * 17 / 16)) { 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci mnparray[idx] = mnparray[idx-1]; 4588c2ecf20Sopenharmony_ci deltaarray[idx] = deltaarray[idx-1]; 4598c2ecf20Sopenharmony_ci } else { 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci mnparray[idx] = mnp; 4648c2ecf20Sopenharmony_ci deltaarray[idx] = delta; 4658c2ecf20Sopenharmony_ci mnpcount++; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci /* VideoPLL and PixelPLL matched: do nothing... In all other cases we should get at least one frequency */ 4698c2ecf20Sopenharmony_ci if (!mnpcount) { 4708c2ecf20Sopenharmony_ci return -EBUSY; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci { 4738c2ecf20Sopenharmony_ci unsigned long flags; 4748c2ecf20Sopenharmony_ci unsigned int mnp; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci matroxfb_DAC_lock_irqsave(flags); 4778c2ecf20Sopenharmony_ci mnp = g450_checkcache(minfo, ci, mnparray[0]); 4788c2ecf20Sopenharmony_ci if (mnp != NO_MORE_MNP) { 4798c2ecf20Sopenharmony_ci matroxfb_g450_setpll_cond(minfo, mnp, pll); 4808c2ecf20Sopenharmony_ci } else { 4818c2ecf20Sopenharmony_ci mnp = g450_findworkingpll(minfo, pll, mnparray, mnpcount); 4828c2ecf20Sopenharmony_ci g450_addcache(ci, mnparray[0], mnp); 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci updatehwstate_clk(&minfo->hw, mnp, pll); 4858c2ecf20Sopenharmony_ci matroxfb_DAC_unlock_irqrestore(flags); 4868c2ecf20Sopenharmony_ci return mnp; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci/* It must be greater than number of possible PLL values. 4918c2ecf20Sopenharmony_ci * Currently there is 5(p) * 10(m) = 50 possible values. */ 4928c2ecf20Sopenharmony_ci#define MNP_TABLE_SIZE 64 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ciint matroxfb_g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, 4958c2ecf20Sopenharmony_ci unsigned int pll) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci unsigned int* arr; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci arr = kmalloc(sizeof(*arr) * MNP_TABLE_SIZE * 2, GFP_KERNEL); 5008c2ecf20Sopenharmony_ci if (arr) { 5018c2ecf20Sopenharmony_ci int r; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci r = __g450_setclk(minfo, fout, pll, arr, arr + MNP_TABLE_SIZE); 5048c2ecf20Sopenharmony_ci kfree(arr); 5058c2ecf20Sopenharmony_ci return r; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci return -ENOMEM; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(matroxfb_g450_setclk); 5118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(g450_mnp2f); 5128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(matroxfb_g450_setpll_cond); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ciMODULE_AUTHOR("(c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); 5158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Matrox G450/G550 PLL driver"); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 518