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