linux/drivers/video/fbdev/matrox/g450_pll.c
<<
>>
Prefs
   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/10
  10 *
  11 * This file is subject to the terms and conditions of the GNU General Public
  12 * License. See the file COPYING in the main directory of this archive for
  13 * 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     0x01FFFFFF
  53#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 to
 360                                   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 get
 444                                   sorted equally good frequencies from lower VCO 
 445                                   frequency to higher - with <= lowest wins, while
 446                                   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) VCOmin
 450                                         * (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  64
 493
 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