Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright 2005-2006 Luc Verhaegen.
      3  * Copyright 1993-1997 The XFree86 Project, Inc.
      4  * Copyright 1990-1991 Thomas Roell.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sub license,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice (including the
     14  * next paragraph) shall be included in all copies or substantial portions
     15  * of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
     20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     23  * DEALINGS IN THE SOFTWARE.
     24  */
     25 
     26 #ifdef HAVE_CONFIG_H
     27 #include "config.h"
     28 #endif
     29 
     30 #include "tseng.h"
     31 
     32 /*
     33  * lacking from hwp
     34  */
     35 
     36 #define VGA_BANK 0x3CB
     37 
     38 void
     39 vgaHWWriteBank(vgaHWPtr hwp, CARD8 value)
     40 {
     41     if (hwp->MMIOBase)
     42 	MMIO_OUT8(hwp->MMIOBase, hwp->MMIOOffset + VGA_BANK, value);
     43     else
     44 #if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
     45 	outb(hwp->PIOOffset + VGA_BANK, value);
     46 #else
     47 	pci_io_write8(hwp->io, VGA_BANK, value);
     48 #endif
     49 }
     50 
     51 CARD8
     52 vgaHWReadBank(vgaHWPtr hwp)
     53 {
     54     if (hwp->MMIOBase)
     55 	return MMIO_IN8(hwp->MMIOBase, hwp->MMIOOffset + VGA_BANK);
     56     else
     57 #if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
     58 	return inb(hwp->PIOOffset + VGA_BANK);
     59 #else
     60 	return pci_io_read8(hwp->io, VGA_BANK);
     61 #endif
     62 }
     63 
     64 #define VGA_SEGMENT 0x3CD
     65 
     66 void
     67 vgaHWWriteSegment(vgaHWPtr hwp, CARD8 value)
     68 {
     69     if (hwp->MMIOBase)
     70 	MMIO_OUT8(hwp->MMIOBase, hwp->MMIOOffset + VGA_SEGMENT, value);
     71     else
     72 #if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
     73 	outb(hwp->PIOOffset + VGA_SEGMENT, value);
     74 #else
     75 	pci_io_write8(hwp->io, VGA_SEGMENT, value);
     76 #endif
     77 }
     78 
     79 CARD8
     80 vgaHWReadSegment(vgaHWPtr hwp)
     81 {
     82     if (hwp->MMIOBase)
     83 	return MMIO_IN8(hwp->MMIOBase, hwp->MMIOOffset + VGA_SEGMENT);
     84     else
     85 #if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
     86 	return inb(hwp->PIOOffset + VGA_SEGMENT);
     87 #else
     88 	return pci_io_read8(hwp->io, VGA_SEGMENT);
     89 #endif
     90 }
     91 
     92 /*
     93  * 0x3D8 Tseng Display Mode Control
     94  */
     95 #define VGA_MODE_CONTROL 0x08
     96 
     97 void
     98 vgaHWWriteModeControl(vgaHWPtr hwp, CARD8 value)
     99 {
    100     if (hwp->MMIOBase)
    101         MMIO_OUT8(hwp->MMIOBase,
    102                   hwp->MMIOOffset + hwp->IOBase + VGA_MODE_CONTROL, value);
    103     else
    104 #if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
    105 	outb(hwp->PIOOffset + VGA_MODE_CONTROL, value);
    106 #else
    107 	pci_io_write8(hwp->io, VGA_MODE_CONTROL, value);
    108 #endif
    109 }
    110 
    111 /*
    112  * 0x3BF: Hercules compatibility mode.
    113  * Enable/Disable Second page (B800h-BFFFh)
    114  */
    115 
    116 #define VGA_HERCULES 0x3BF
    117 
    118 void
    119 vgaHWHerculesSecondPage(vgaHWPtr hwp, Bool Enable)
    120 {
    121     CARD8 tmp;
    122 
    123     if (hwp->MMIOBase) {
    124         tmp = MMIO_IN8(hwp->MMIOBase, hwp->MMIOOffset + VGA_HERCULES);
    125 
    126         if (Enable)
    127             tmp |= 0x02;
    128         else
    129             tmp &= ~0x02;
    130 
    131         MMIO_OUT8(hwp->MMIOBase, hwp->MMIOOffset + VGA_HERCULES, tmp);
    132     } else {
    133 #if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
    134 	tmp = inb(hwp->PIOOffset + VGA_HERCULES);
    135 #else
    136 	tmp = pci_io_read8(hwp->io, VGA_HERCULES);
    137 #endif
    138 
    139         if (Enable)
    140             tmp |= 0x02;
    141         else
    142             tmp &= ~0x02;
    143 
    144 #if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
    145 	outb(hwp->PIOOffset + VGA_HERCULES, tmp);
    146 #else
    147 	pci_io_write8(hwp->io, VGA_HERCULES, tmp);
    148 #endif
    149     }
    150 }
    151 
    152 /*
    153  * ET6000 IO Range handling.
    154  *
    155  */
    156 CARD8
    157 ET6000IORead(TsengPtr pTseng, CARD8 Offset)
    158 {
    159     return inb(pTseng->ET6000IOAddress + Offset);
    160 }
    161 
    162 void
    163 ET6000IOWrite(TsengPtr pTseng, CARD8 Offset, CARD8 Value)
    164 {
    165     outb(pTseng->ET6000IOAddress + Offset, Value);
    166 }
    167 
    168 /*
    169  *
    170  * RAMDAC handling.
    171  *
    172  */
    173 
    174 /*
    175  *
    176  * SGS-Thomson STG-1703
    177  *
    178  */
    179 /*
    180  *
    181  */
    182 static Bool
    183 STG1703Detect(ScrnInfoPtr pScrn)
    184 {
    185     TsengPtr pTseng = TsengPTR(pScrn);
    186     vgaHWPtr hwp = VGAHWPTR(pScrn);
    187     CARD8 temp, cid, did, readDacMask;
    188 
    189     /* TSENGFUNC(pScrn->scrnIndex); */
    190 
    191     /* store command register and DacMask */
    192     hwp->writeDacWriteAddr(hwp, 0x00);
    193     readDacMask = hwp->readDacMask(hwp);
    194     hwp->readDacMask(hwp);
    195     hwp->readDacMask(hwp);
    196     hwp->readDacMask(hwp);
    197     temp = hwp->readDacMask(hwp);
    198 
    199     /* enable extended registers */
    200     hwp->writeDacWriteAddr(hwp, 0x00);
    201     hwp->readDacMask(hwp);
    202     hwp->readDacMask(hwp);
    203     hwp->readDacMask(hwp);
    204     hwp->readDacMask(hwp);
    205     hwp->writeDacMask(hwp, temp | 0x10);
    206 
    207     /* set index 0x0000 and read IDs */
    208     hwp->writeDacWriteAddr(hwp, 0x00);
    209     hwp->readDacMask(hwp);
    210     hwp->readDacMask(hwp);
    211     hwp->readDacMask(hwp);
    212     hwp->readDacMask(hwp);
    213     hwp->readDacMask(hwp);
    214     hwp->writeDacMask(hwp, 0x00);
    215     hwp->writeDacMask(hwp, 0x00);
    216     cid = hwp->readDacMask(hwp); /* company ID */
    217     did = hwp->readDacMask(hwp); /* device ID */
    218 
    219     /* restore command register */
    220     hwp->writeDacWriteAddr(hwp, 0x00);
    221     hwp->readDacMask(hwp);
    222     hwp->readDacMask(hwp);
    223     hwp->readDacMask(hwp);
    224     hwp->readDacMask(hwp);
    225     hwp->writeDacMask(hwp, temp);
    226 
    227     /* restore DacMask */
    228     hwp->writeDacWriteAddr(hwp, 0x00);
    229     hwp->writeDacMask(hwp, readDacMask);
    230 
    231     hwp->writeDacWriteAddr(hwp, 0x00);
    232 
    233     if ((cid == 0x44) && (did == 0x03))	{
    234         xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected STG-1703 RAMDAC.\n");
    235         pTseng->RAMDAC = STG1703;
    236         return TRUE;
    237     }
    238     return FALSE;
    239 }
    240 
    241 /*
    242  *
    243  */
    244 struct STG1703Regs {
    245     CARD8 Command;
    246     CARD8 Pixel;
    247     CARD8 Timing;
    248     CARD16 PLL;
    249 };
    250 
    251 /*
    252  *
    253  */
    254 static void
    255 STG1703PrintRegs(ScrnInfoPtr pScrn, struct STG1703Regs *Regs)
    256 {
    257     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 7, "STG1703 Registers:\n");
    258     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 7, "Command: 0x%02X\n",
    259                    Regs->Command);
    260     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 7, "Pixel mode: 0x%02X\n",
    261                    Regs->Pixel);
    262     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 7, "Timing: 0x%02X\n",
    263                    Regs->Timing);
    264     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 7, "PLL: 0x%04X\n",
    265                    Regs->PLL);
    266 }
    267 
    268 /*
    269  *
    270  */
    271 static void
    272 STG1703Store(ScrnInfoPtr pScrn, struct STG1703Regs *Regs)
    273 {
    274     vgaHWPtr hwp = VGAHWPTR(pScrn);
    275     CARD8 readDacMask;
    276 
    277     /* save command register and dacMask*/
    278     hwp->writeDacWriteAddr(hwp, 0x00);
    279     readDacMask = hwp->readDacMask(hwp);
    280     hwp->readDacMask(hwp);
    281     hwp->readDacMask(hwp);
    282     hwp->readDacMask(hwp);
    283     Regs->Command = hwp->readDacMask(hwp);
    284 
    285     /* enable indexed register space */
    286     hwp->writeDacWriteAddr(hwp, 0x00);
    287     hwp->readDacMask(hwp);
    288     hwp->readDacMask(hwp);
    289     hwp->readDacMask(hwp);
    290     hwp->readDacMask(hwp);
    291     hwp->writeDacMask(hwp, Regs->Command | 0x10);
    292 
    293     /* set the index for the pixel mode */
    294     hwp->writeDacWriteAddr(hwp, 0x00);
    295     hwp->readDacMask(hwp);
    296     hwp->readDacMask(hwp);
    297     hwp->readDacMask(hwp);
    298     hwp->readDacMask(hwp);
    299     hwp->readDacMask(hwp);
    300     hwp->writeDacMask(hwp, 0x03);
    301     hwp->writeDacMask(hwp, 0x00);
    302 
    303     Regs->Pixel = hwp->readDacMask(hwp); /* pixel mode */
    304 
    305     hwp->readDacMask(hwp); /* skip secondary pixel mode */
    306 
    307     Regs->Timing = hwp->readDacMask(hwp); /* pipeline timing */
    308 
    309     /* start over for the pll register */
    310     hwp->writeDacWriteAddr(hwp, 0x00);
    311     hwp->readDacMask(hwp);
    312     hwp->readDacMask(hwp);
    313     hwp->readDacMask(hwp);
    314     hwp->readDacMask(hwp);
    315     hwp->readDacMask(hwp);
    316 
    317     /* set the index for VCLK2 */
    318     hwp->writeDacMask(hwp, 0x24);
    319     hwp->writeDacMask(hwp, 0x00);
    320 
    321     Regs->PLL = hwp->readDacMask(hwp);
    322     Regs->PLL |= (hwp->readDacMask(hwp) << 8);
    323 
    324     /* restore command register and dacMask */
    325     hwp->writeDacWriteAddr(hwp, 0x00);
    326     hwp->readDacMask(hwp);
    327     hwp->readDacMask(hwp);
    328     hwp->readDacMask(hwp);
    329     hwp->readDacMask(hwp);
    330     hwp->writeDacMask(hwp, Regs->Command);
    331 
    332     hwp->writeDacWriteAddr(hwp, 0x00);
    333     hwp->writeDacMask(hwp, readDacMask);
    334 
    335     hwp->writeDacWriteAddr(hwp, 0x00);
    336 
    337     STG1703PrintRegs(pScrn, Regs);
    338 }
    339 
    340 /*
    341  *
    342  */
    343 static void
    344 STG1703Restore(ScrnInfoPtr pScrn, struct STG1703Regs *Regs)
    345 {
    346     vgaHWPtr hwp = VGAHWPTR(pScrn);
    347     CARD8 temp, readDacMask;
    348 
    349     STG1703PrintRegs(pScrn, Regs);
    350 
    351     /* save command register and dacMask*/
    352     hwp->writeDacWriteAddr(hwp, 0x00);
    353     readDacMask = hwp->readDacMask(hwp);
    354     hwp->readDacMask(hwp);
    355     hwp->readDacMask(hwp);
    356     hwp->readDacMask(hwp);
    357     temp = hwp->readDacMask(hwp);
    358 
    359     /* enable indexed register space */
    360     hwp->writeDacWriteAddr(hwp, 0x00);
    361     hwp->readDacMask(hwp);
    362     hwp->readDacMask(hwp);
    363     hwp->readDacMask(hwp);
    364     hwp->readDacMask(hwp);
    365     hwp->writeDacMask(hwp, temp | 0x10);
    366 
    367     /* set the index for the pixel mode */
    368     hwp->writeDacWriteAddr(hwp, 0x00);
    369     hwp->readDacMask(hwp);
    370     hwp->readDacMask(hwp);
    371     hwp->readDacMask(hwp);
    372     hwp->readDacMask(hwp);
    373     hwp->readDacMask(hwp);
    374     hwp->writeDacMask(hwp, 0x03);
    375     hwp->writeDacMask(hwp, 0x00);
    376 
    377     hwp->writeDacMask(hwp, Regs->Pixel); /* pixel mode */
    378     hwp->writeDacMask(hwp, Regs->Pixel); /* also secondary */
    379     hwp->writeDacMask(hwp, Regs->Timing); /* pipeline timing */
    380 
    381     /* start over for the pll register */
    382     hwp->writeDacWriteAddr(hwp, 0x00);
    383     hwp->readDacMask(hwp);
    384     hwp->readDacMask(hwp);
    385     hwp->readDacMask(hwp);
    386     hwp->readDacMask(hwp);
    387     hwp->readDacMask(hwp);
    388 
    389     /* set the index for VCLK2 */
    390     hwp->writeDacMask(hwp, 0x26);
    391     hwp->writeDacMask(hwp, 0x00);
    392 
    393     hwp->writeDacMask(hwp, Regs->PLL & 0xFF);
    394     hwp->writeDacMask(hwp, Regs->PLL >> 8);
    395 
    396     /* restore command register */
    397     hwp->writeDacWriteAddr(hwp, 0x00);
    398     hwp->readDacMask(hwp);
    399     hwp->readDacMask(hwp);
    400     hwp->readDacMask(hwp);
    401     hwp->readDacMask(hwp);
    402     hwp->writeDacMask(hwp, Regs->Command);
    403 
    404     /* Restore DacMask */
    405     hwp->writeDacWriteAddr(hwp, 0x00);
    406     hwp->writeDacMask(hwp, readDacMask);
    407 
    408     hwp->writeDacWriteAddr(hwp, 0x00);
    409 }
    410 
    411 /*
    412  * Hope that the TVP3703 ramdac pll is the same as the STG1703.
    413  */
    414 static CARD16
    415 STG1703Clock(ScrnInfoPtr pScrn, int Clock)
    416 {
    417     CARD8 N1, N2, M;
    418     CARD16 PLL = 0;
    419     CARD32 Closest = 0xFFFFFFFF;
    420 
    421     for (N2 = 0; N2 < 4; N2++) {
    422         for (N1 = 7; N1 < 15; N1++) {
    423             CARD8 divider = N1 << N2;
    424             CARD32 temp;
    425 
    426             /* check boundaries */
    427             temp = Clock * divider;
    428             if ((temp < (64000 * N1)) || (temp > (135000 * N1)))
    429                 continue;
    430 
    431             /* calculate 2M */
    432             temp =  (2 * Clock * divider) / 14318;
    433             if ((temp > 258) || (temp < 4)) /* (127 + 2) * 2 */
    434                 continue;
    435 
    436             /* round up/down */
    437             if (temp & 1)
    438                 M = temp / 2 + 1;
    439             else
    440                 M = temp / 2;
    441 
    442             /* is this the closest match? */
    443             temp = (14318 * M) / divider;
    444 
    445             if (temp > Clock)
    446                 temp -= Clock;
    447             else
    448                 temp = Clock - temp;
    449 
    450             if (temp < Closest) {
    451                 PLL = (M - 2) | ((N1 - 2) << 8) | (N2 << 13);
    452                 Closest = temp;
    453             }
    454         }
    455     }
    456 
    457     return PLL;
    458 }
    459 
    460 /*
    461  * Copy the given Regs into a freshly alloced STG1703Regs
    462  * and init it for the new mode.
    463  */
    464 static struct STG1703Regs *
    465 STG1703Mode(ScrnInfoPtr pScrn, struct STG1703Regs *Saved,
    466             DisplayModePtr mode)
    467 {
    468     struct STG1703Regs *Regs;
    469 
    470     Regs = xnfalloc(sizeof(struct STG1703Regs));
    471     memcpy(Regs, Saved, sizeof(struct STG1703Regs));
    472 
    473     Regs->Command &= 0x04; /* keep 7.5 IRE setup setting */
    474     Regs->Command |= 0x08; /* enable extended pixel modes */
    475 
    476     switch (pScrn->bitsPerPixel) {
    477     case 8:
    478         Regs->Pixel = 0x05;
    479         /* high bits of Command are already zeroed */
    480         break;
    481     case 16:
    482         Regs->Pixel = 0x03;
    483         Regs->Command |= 0xC0; /* 16bpp */
    484         break;
    485     case 24:
    486         Regs->Pixel = 0x09;
    487         Regs->Command |= 0xE0; /* 24bpp */
    488         break;
    489     case 32:
    490         Regs->Pixel = 0x04; /* 24bpp in 4Bytes */
    491         Regs->Command |= 0xE0; /* 24bpp */
    492         break;
    493     default:
    494         Regs->Pixel = 0x00;
    495         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "STG1703 RAMDAC doesn't"
    496                    " support %dbpp.\n", pScrn->bitsPerPixel);
    497     }
    498 
    499     /* set PLL (input) range */
    500     if (mode->SynthClock <= 16000)
    501         Regs->Timing = 0;
    502     else if (mode->SynthClock <= 32000)
    503         Regs->Timing = 1;
    504     else if (mode->SynthClock <= 67500)
    505         Regs->Timing = 2;
    506     else
    507         Regs->Timing = 3;
    508 
    509     /* Calculate dotclock here */
    510     Regs->PLL = STG1703Clock(pScrn, mode->Clock);
    511 
    512     STG1703PrintRegs(pScrn, Regs);
    513 
    514     return Regs;
    515 }
    516 
    517 /*
    518  *
    519  * Chrontel CH8398A
    520  *
    521  */
    522 
    523 /*
    524  *
    525  */
    526 static Bool
    527 CH8398Detect(ScrnInfoPtr pScrn)
    528 {
    529     TsengPtr pTseng = TsengPTR(pScrn);
    530     vgaHWPtr hwp = VGAHWPTR(pScrn);
    531     CARD8 temp;
    532 
    533     /* TSENGFUNC(pScrn->scrnIndex); */
    534 
    535     hwp->writeDacWriteAddr(hwp, 0x00);
    536     hwp->readDacMask(hwp);
    537     hwp->readDacMask(hwp);
    538     hwp->readDacMask(hwp);
    539     temp = hwp->readDacMask(hwp);
    540     hwp->writeDacWriteAddr(hwp, 0x00);
    541 
    542     if (temp == 0xC0) {
    543         xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected Chrontel CH8398 RAMDAC.\n");
    544         pTseng->RAMDAC = CH8398;
    545         return TRUE;
    546     }
    547     return FALSE;
    548 }
    549 
    550 /*
    551  *
    552  */
    553 struct CH8398Regs {
    554     CARD8 Control;
    555     CARD8 Aux;
    556     CARD16 PLL;
    557 };
    558 
    559 /*
    560  *
    561  */
    562 static void
    563 CH8398PrintRegs(ScrnInfoPtr pScrn, struct CH8398Regs *Regs)
    564 {
    565     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 7, "CH8398 Registers:\n");
    566     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 7, "Control: 0x%02X\n",
    567                    Regs->Control);
    568     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 7, "Aux: 0x%02X\n",
    569                    Regs->Aux);
    570     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 7, "PLL: 0x%04X\n",
    571                    Regs->PLL);
    572 }
    573 
    574 /*
    575  *
    576  */
    577 static void
    578 CH8398Store(ScrnInfoPtr pScrn, struct CH8398Regs *Regs)
    579 {
    580     vgaHWPtr hwp = VGAHWPTR(pScrn);
    581 
    582     /* Get the control and auxiliary registers. */
    583     hwp->writeDacWriteAddr(hwp, 0x00);
    584     hwp->readDacMask(hwp);
    585     hwp->readDacMask(hwp);
    586     hwp->readDacMask(hwp);
    587     hwp->readDacMask(hwp);
    588     Regs->Control = hwp->readDacMask(hwp);
    589     Regs->Aux = hwp->readDacMask(hwp);
    590 
    591     /* Enable PLL RAM access mode through AUX */
    592     hwp->writeDacWriteAddr(hwp, 0x00);
    593     hwp->readDacMask(hwp);
    594     hwp->readDacMask(hwp);
    595     hwp->readDacMask(hwp);
    596     hwp->readDacMask(hwp);
    597     hwp->readDacMask(hwp);
    598     hwp->writeDacMask(hwp, Regs->Aux | 0x80);
    599 
    600     /* Read PLL */
    601     hwp->writeDacReadAddr(hwp, 0x03);
    602     Regs->PLL = hwp->readDacData(hwp); /* N */
    603     Regs->PLL |= hwp->readDacData(hwp) << 8; /* M and K*/
    604 
    605     /* Disable PLL RAM access mode */
    606     hwp->writeDacWriteAddr(hwp, 0x00);
    607     hwp->readDacMask(hwp);
    608     hwp->readDacMask(hwp);
    609     hwp->readDacMask(hwp);
    610     hwp->readDacMask(hwp);
    611     hwp->readDacMask(hwp);
    612     hwp->writeDacMask(hwp, Regs->Aux & ~0x80);
    613 
    614     /* exit sequence */
    615     hwp->writeDacWriteAddr(hwp, 0x00);
    616 
    617     CH8398PrintRegs(pScrn, Regs);
    618 }
    619 
    620 /*
    621  *
    622  */
    623 static void
    624 CH8398Restore(ScrnInfoPtr pScrn, struct CH8398Regs *Regs)
    625 {
    626     vgaHWPtr hwp = VGAHWPTR(pScrn);
    627 
    628     CH8398PrintRegs(pScrn, Regs);
    629 
    630     /* Write control and auxiliary registers */
    631     hwp->writeDacWriteAddr(hwp, 0x00);
    632     hwp->readDacMask(hwp);
    633     hwp->readDacMask(hwp);
    634     hwp->readDacMask(hwp);
    635     hwp->readDacMask(hwp);
    636     hwp->writeDacMask(hwp, Regs->Control);
    637     hwp->writeDacMask(hwp, Regs->Aux | 0x80); /* enable PLL RAM mode as well */
    638 
    639     /* Write PLL */
    640     hwp->writeDacWriteAddr(hwp, 0x02);
    641     hwp->writeDacData(hwp, Regs->PLL & 0xFF); /* N */
    642     hwp->writeDacData(hwp, Regs->PLL >> 8); /* M and K */
    643 
    644     /* Disable PLL RAM access mode */
    645     hwp->writeDacWriteAddr(hwp, 0x00);
    646     hwp->readDacMask(hwp);
    647     hwp->readDacMask(hwp);
    648     hwp->readDacMask(hwp);
    649     hwp->readDacMask(hwp);
    650     hwp->readDacMask(hwp);
    651     hwp->writeDacMask(hwp, Regs->Aux & ~0x80);
    652 
    653     /* exit sequence */
    654     hwp->writeDacWriteAddr(hwp, 0x00);
    655 }
    656 
    657 /*
    658  *
    659  */
    660 static CARD16
    661 CH8398Clock(ScrnInfoPtr pScrn, int Clock)
    662 {
    663     CARD16 PLL = 0;
    664     CARD8 N, M, K;
    665     CARD32 Closest = 0xFFFFFFFF;
    666 
    667     if (Clock > 68000)
    668         K = 0;
    669     else
    670         K = 1;
    671 
    672     for (M = 2; M < 12; M++) {
    673         CARD16 divider = M << K;
    674         CARD32 temp;
    675 
    676         /* calculate 2N */
    677         temp =  (2 * Clock * divider) / 14318;
    678         if ((temp > 526) || (temp < 16)) /* (255 + 8) * 2 */
    679             continue;
    680 
    681         /* round up/down */
    682         if (temp & 1)
    683             N = temp / 2 + 1;
    684         else
    685             N = temp / 2;
    686 
    687         /* is this the closest match? */
    688         temp = (14318 * N) / divider;
    689 
    690         if (temp > Clock)
    691             temp -= Clock;
    692         else
    693             temp = Clock - temp;
    694 
    695         if (temp < Closest) {
    696             PLL = (N - 8) | ((M - 2) << 8) | (K << 14);
    697             Closest = temp;
    698         }
    699     }
    700 
    701     return PLL;
    702 }
    703 
    704 /*
    705  *
    706  */
    707 static struct CH8398Regs *
    708 CH8398Mode(ScrnInfoPtr pScrn, struct CH8398Regs *Saved,
    709            DisplayModePtr mode)
    710 {
    711     struct CH8398Regs *Regs;
    712     int Clock = mode->Clock;
    713 
    714     Regs = xnfalloc(sizeof(struct CH8398Regs));
    715     memcpy(Regs, Saved, sizeof(struct CH8398Regs));
    716 
    717     Regs->Control &= 0x0F;
    718 
    719     switch (pScrn->bitsPerPixel) {
    720     case 8:
    721         Regs->Control |= 0x20;
    722         break;
    723     case 16:
    724         Regs->Control |= 0x30;
    725         break;
    726     case 24:
    727         Regs->Control |= 0xB0;
    728         break;
    729     case 32:
    730         Regs->Control |= 0x50; /* 24bpp in 4bytes */
    731         break;
    732     default:
    733         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "CH8398 RAMDAC doesn't"
    734                    " support %dbpp.\n", pScrn->bitsPerPixel);
    735     }
    736 
    737     Regs->PLL = CH8398Clock(pScrn, Clock);
    738 
    739     CH8398PrintRegs(pScrn, Regs);
    740 
    741     return Regs;
    742 }
    743 
    744 
    745 /*
    746  *
    747  */
    748 Bool
    749 TsengRAMDACProbe(ScrnInfoPtr pScrn)
    750 {
    751     TsengPtr pTseng = TsengPTR(pScrn);
    752 
    753     PDEBUG("	Check_Tseng_Ramdac\n");
    754 
    755     if (pTseng->ChipType == ET6000) {
    756         int mclk;
    757         int dbyte;
    758 
    759         /* There are some limits here though: 80000 <= MemClk <= 110000 */
    760         ET6000IORead(pTseng, 0x67);
    761         ET6000IOWrite(pTseng, 0x67, 0x0A);
    762         mclk = (ET6000IORead(pTseng, 0x69) + 2) * 14318;
    763         dbyte = ET6000IORead(pTseng, 0x69);
    764         mclk /= ((dbyte & 0x1f) + 2) * (1 << ((dbyte >> 5) & 0x03));
    765         pTseng->MemClk = mclk;
    766 
    767         return TRUE;
    768     } else { /* ET4000W32P has external ramdacs */
    769 
    770         /* First look for CH8398 - as this is a non-invasive detection */
    771         if (CH8398Detect(pScrn))
    772             return TRUE;
    773 
    774         /* Now that we know that we won't mess up a CH8398 */
    775         if (STG1703Detect(pScrn))
    776             return TRUE;
    777 
    778         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to probe RAMDAC\n");
    779         return FALSE;
    780         /* hwp->writeDacMask(hwp, 0xFF); */
    781     }
    782 
    783     return TRUE;
    784 }
    785 
    786 /*
    787  * Memory bandwidth is important in > 8bpp modes, especially on ET4000
    788  *
    789  * This code evaluates a video mode with respect to requested dot clock
    790  * (depends on the VGA chip and the RAMDAC) and the resulting bandwidth
    791  * demand on memory (which in turn depends on color depth).
    792  *
    793  * For each mode, the minimum of max data transfer speed (dot clock
    794  * limit) and memory bandwidth determines if the mode is allowed.
    795  *
    796  * We should also take acceleration into account: accelerated modes
    797  * strain the bandwidth heavily, because they cause lots of random
    798  * acesses to video memory, which is bad for bandwidth due to smaller
    799  * page-mode memory requests.
    800  */
    801 void
    802 TsengSetupClockRange(ScrnInfoPtr pScrn)
    803 {
    804     TsengPtr pTseng = TsengPTR(pScrn);
    805     int dacspeed, mem_bw;
    806 
    807     PDEBUG("	tseng_clock_setup\n");
    808 
    809     if (pTseng->ChipType == ET6000) {
    810         /*
    811          * According to Tseng (about the ET6000):
    812          * "Besides the 135 MHz maximum pixel clock frequency, the other limit has to
    813          * do with where you get FIFO breakdown (usually appears as stray horizontal
    814          * lines on the screen). Assuming the accelerator is running steadily doing a
    815          * worst case operation, to avoid FIFO breakdown you should keep the product
    816          *   pixel_clock*(bytes/pixel) <= 225 MHz . This is based on an XCLK
    817          * (system/memory) clock of 92 MHz (which is what we currently use) and
    818          * a value in the RAS/CAS Configuration register (CFG 44) of either 015h
    819          * or 014h (depending on the type of MDRAM chips). Also, the FIFO low
    820          * threshold control bit (bit 4 of CFG 41) should be set for modes where
    821          * pixel_clock*(bytes/pixel) > 130 MHz . These limits are for the
    822          * current ET6000 chips. The ET6100 will raise the pixel clock limit
    823          * to 175 MHz and the pixel_clock*(bytes/pixel) FIFO breakdown limit
    824          * to about 275 MHz."
    825          */
    826 
    827         if (pTseng->ChipRev == REV_ET6100) {
    828             dacspeed = 175000;
    829             mem_bw = 280000; /* 275000 is _just_ not enough for 1152x864x24 @ 70Hz */
    830         } else { /* ET6000 */
    831             dacspeed = 135000;
    832             mem_bw = 225000;
    833         }
    834 
    835         switch (pScrn->bitsPerPixel) {
    836         case 16:
    837             mem_bw /= 2;
    838             break;
    839         case 24:
    840             mem_bw /= 3;
    841             break;
    842         case 32:
    843             mem_bw /= 4;
    844             break;
    845         case 8:
    846         default:
    847             break;
    848         }
    849 
    850         pTseng->max_vco_freq = dacspeed*2+1;
    851     } else { /* ET4000W32p */
    852 
    853         switch (pTseng->RAMDAC) {
    854         case STG1703:
    855             if (pScrn->bitsPerPixel == 8)
    856                 dacspeed = 135000;
    857             else
    858                 dacspeed = 110000;
    859             break;
    860         case CH8398:
    861             dacspeed = 135000;
    862             break;
    863         default:
    864             dacspeed = 0;
    865             break;
    866         }
    867 
    868         if (pScrn->videoRam > 1024)
    869             mem_bw = 150000; /* interleaved DRAM gives 70% more bandwidth */
    870         else
    871             mem_bw = 90000;
    872 
    873         switch (pScrn->bitsPerPixel) {
    874         case 8:
    875             /* Don't touch mem_bw or dac_speed */
    876             break;
    877         case 16:
    878             mem_bw /= 2;
    879             /* 1:1 dotclock */
    880             break;
    881         case 24:
    882             mem_bw /= 3;
    883             dacspeed = dacspeed * 3 / 2;
    884             break;
    885         case 32:
    886             mem_bw /= 4;
    887             dacspeed /= 2;
    888             break;
    889         default:
    890             break;
    891         }
    892     }
    893 
    894     pTseng->clockRange.next = NULL;
    895     pTseng->clockRange.minClock = 12000;
    896     if (mem_bw < dacspeed)
    897         pTseng->clockRange.maxClock = mem_bw;
    898     else
    899         pTseng->clockRange.maxClock = dacspeed;
    900     pTseng->clockRange.clockIndex = -1;      /* programmable -- not used */
    901     pTseng->clockRange.interlaceAllowed = TRUE;
    902     pTseng->clockRange.doubleScanAllowed = TRUE;
    903     pTseng->clockRange.ClockMulFactor = 1;
    904     pTseng->clockRange.ClockDivFactor = 1;
    905     pTseng->clockRange.PrivFlags = 0;
    906 }
    907 
    908 
    909 /*
    910  *
    911  */
    912 #define BASE_FREQ         14.31818     /* MHz */
    913 static CARD16
    914 ET6000CalcClock(long freq, int min_m, int min_n1, int max_n1, int min_n2,
    915                 int max_n2, long freq_min, long freq_max)
    916 {
    917     double ffreq, ffreq_min, ffreq_max;
    918     double div, diff, best_diff;
    919     unsigned int m;
    920     CARD8 n1, n2;
    921     CARD8 best_n1 = 16 + 2, best_n2 = 2, best_m = 125 + 2;
    922     CARD8 ndiv, mdiv;
    923 
    924 
    925     PDEBUG("	commonCalcClock\n");
    926 
    927     ffreq = freq / 1000.0 / BASE_FREQ;
    928     ffreq_min = freq_min / 1000.0 / BASE_FREQ;
    929     ffreq_max = freq_max / 1000.0 / BASE_FREQ;
    930 
    931     if (ffreq < ffreq_min / (1 << max_n2)) {
    932 	ErrorF("invalid frequency %1.3f MHz  [freq >= %1.3f MHz]\n",
    933 	    ffreq * BASE_FREQ, ffreq_min * BASE_FREQ / (1 << max_n2));
    934 	ffreq = ffreq_min / (1 << max_n2);
    935     }
    936     if (ffreq > ffreq_max / (1 << min_n2)) {
    937 	ErrorF("invalid frequency %1.3f MHz  [freq <= %1.3f MHz]\n",
    938 	    ffreq * BASE_FREQ, ffreq_max * BASE_FREQ / (1 << min_n2));
    939 	ffreq = ffreq_max / (1 << min_n2);
    940     }
    941     /* work out suitable timings */
    942 
    943     best_diff = ffreq;
    944 
    945     for (n2 = min_n2; n2 <= max_n2; n2++) {
    946 	for (n1 = min_n1 + 2; n1 <= max_n1 + 2; n1++) {
    947 	    m = (int)(ffreq * n1 * (1 << n2) + 0.5);
    948 	    if (m < min_m + 2 || m > 127 + 2)
    949 		continue;
    950 	    div = (double)(m) / (double)(n1);
    951 	    if ((div >= ffreq_min) &&
    952 		(div <= ffreq_max)) {
    953 		diff = ffreq - div / (1 << n2);
    954 		if (diff < 0.0)
    955 		    diff = -diff;
    956 		if (diff < best_diff) {
    957 		    best_diff = diff;
    958 		    best_m = m;
    959 		    best_n1 = n1;
    960 		    best_n2 = n2;
    961 		}
    962 	    }
    963 	}
    964     }
    965 
    966 #ifdef EXTENDED_DEBUG
    967     ErrorF("Clock parameters for %1.6f MHz: m=%d, n1=%d, n2=%d\n",
    968 	((double)(best_m) / (double)(best_n1) / (1 << best_n2)) * BASE_FREQ,
    969 	best_m - 2, best_n1 - 2, best_n2);
    970 #endif
    971 
    972     if (max_n1 == 63)
    973 	ndiv = (best_n1 - 2) | (best_n2 << 6);
    974     else
    975 	ndiv = (best_n1 - 2) | (best_n2 << 5);
    976     mdiv = best_m - 2;
    977 
    978     return (ndiv << 8) | mdiv;
    979 }
    980 
    981 /*
    982  * adjust the current video frame (viewport) to display the mousecursor.
    983  */
    984 void
    985 TsengAdjustFrame(ADJUST_FRAME_ARGS_DECL)
    986 {
    987     SCRN_INFO_PTR(arg);
    988     TsengPtr pTseng = TsengPTR(pScrn);
    989     vgaHWPtr hwp = VGAHWPTR(pScrn);
    990     int Base;
    991 
    992     PDEBUG("	TsengAdjustFrame\n");
    993 
    994     if (pTseng->ShowCache && y)
    995         y += 256;
    996 
    997     if (pScrn->bitsPerPixel < 8)
    998 	Base = (y * pScrn->displayWidth + x + 3) >> 3;
    999     else {
   1000 	Base = ((y * pScrn->displayWidth + x + 1) * pTseng->Bytesperpixel) >> 2;
   1001 	/* adjust Base address so it is a non-fractional multiple of pTseng->Bytesperpixel */
   1002 	Base -= (Base % pTseng->Bytesperpixel);
   1003     }
   1004 
   1005     hwp->writeCrtc(hwp, 0x0C, (Base >> 8) & 0xFF);
   1006     hwp->writeCrtc(hwp, 0x0D, Base & 0xFF);
   1007     hwp->writeCrtc(hwp, 0x33, (Base >> 16) & 0x0F);
   1008 }
   1009 
   1010 /*
   1011  *
   1012  */
   1013 ModeStatus
   1014 TsengValidMode(SCRN_ARG_TYPE arg, DisplayModePtr mode, Bool verbose, int flags)
   1015 {
   1016 
   1017     PDEBUG("	TsengValidMode\n");
   1018 
   1019 #ifdef FIXME
   1020   is this needed? xf86ValidMode gets HMAX and VMAX variables, so it could deal with this.
   1021   need to recheck hsize with mode->Htotal*mulFactor/divFactor
   1022     /* Check for CRTC timing bits overflow. */
   1023     if (mode->HTotal > Tseng_HMAX) {
   1024 	return MODE_BAD_HVALUE;
   1025     }
   1026     if (mode->VTotal > Tseng_VMAX) {
   1027 	return MODE_BAD_VVALUE;
   1028     }
   1029 #endif
   1030 
   1031     return MODE_OK;
   1032 }
   1033 
   1034 /*
   1035  * Save the current video mode
   1036  */
   1037 void
   1038 TsengSave(ScrnInfoPtr pScrn)
   1039 {
   1040     vgaHWPtr hwp = VGAHWPTR(pScrn);
   1041     TsengPtr pTseng = TsengPTR(pScrn);
   1042     vgaRegPtr vgaReg;
   1043     TsengRegPtr tsengReg;
   1044     unsigned char saveseg1 = 0, saveseg2 = 0;
   1045 
   1046     PDEBUG("	TsengSave\n");
   1047 
   1048     vgaReg = &hwp->SavedReg;
   1049     tsengReg = &pTseng->SavedReg;
   1050 
   1051     /*
   1052      * This function will handle creating the data structure and filling
   1053      * in the generic VGA portion.
   1054      */
   1055     vgaHWSave(pScrn, vgaReg, VGA_SR_ALL);
   1056 
   1057     /*
   1058      * we need this here , cause we MUST disable the ROM SYNC feature
   1059      * this bit changed with W32p_rev_c...
   1060      */
   1061     tsengReg->CR34 = hwp->readCrtc(hwp, 0x34);
   1062 
   1063     if ((pTseng->ChipType == ET4000) &&
   1064         ((pTseng->ChipRev == REV_A) || (pTseng->ChipRev == REV_B)))
   1065 	/* data books say translation ROM is controlled by bits 4 and 5 */
   1066 	hwp->writeCrtc(hwp, 0x34, tsengReg->CR34 & 0xCF);
   1067 
   1068     saveseg1 = vgaHWReadSegment(hwp);
   1069     vgaHWWriteSegment(hwp, 0x00); /* segment select 1 */
   1070 
   1071     saveseg2 = vgaHWReadBank(hwp);
   1072     vgaHWWriteBank(hwp, 0x00); /* segment select 2 */
   1073 
   1074     tsengReg->ExtSegSel[0] = saveseg1;
   1075     tsengReg->ExtSegSel[1] = saveseg2;
   1076 
   1077     tsengReg->CR33 = hwp->readCrtc(hwp, 0x33);
   1078     tsengReg->CR35 = hwp->readCrtc(hwp, 0x35);
   1079 
   1080     if (pTseng->ChipType == ET4000) {
   1081 	tsengReg->CR36 = hwp->readCrtc(hwp, 0x36);
   1082 	tsengReg->CR37 = hwp->readCrtc(hwp, 0x37);
   1083 	tsengReg->CR32 = hwp->readCrtc(hwp, 0x32);
   1084     }
   1085 
   1086     TsengCursorStore(pScrn, tsengReg);
   1087 
   1088     tsengReg->SR06 = hwp->readSeq(hwp, 0x06);
   1089     tsengReg->SR07 = hwp->readSeq(hwp, 0x07) | 0x14;
   1090 
   1091     tsengReg->ExtATC = hwp->readAttr(hwp, 0x36);
   1092     hwp->writeAttr(hwp, 0x36, tsengReg->ExtATC);
   1093 
   1094     if (pTseng->ChipType == ET4000) {
   1095         switch (pTseng->RAMDAC) {
   1096         case STG1703:
   1097             if (!tsengReg->RAMDAC)
   1098                 tsengReg->RAMDAC = (struct STG1703Regs *)
   1099                     xnfalloc(sizeof(struct STG1703Regs));
   1100             STG1703Store(pScrn, tsengReg->RAMDAC);
   1101             break;
   1102         case CH8398:
   1103             if (!tsengReg->RAMDAC)
   1104                 tsengReg->RAMDAC = (struct CH8398Regs *)
   1105                     xnfalloc(sizeof(struct CH8398Regs));
   1106             CH8398Store(pScrn, tsengReg->RAMDAC);
   1107             break;
   1108         default:
   1109             break;
   1110 	}
   1111     } else {
   1112 	/* Save ET6000 CLKDAC PLL registers */
   1113 	ET6000IOWrite(pTseng, 0x67, 0x03);
   1114 	tsengReg->ET6K_PLL = ET6000IORead(pTseng, 0x69);
   1115 	tsengReg->ET6K_PLL |= ET6000IORead(pTseng, 0x69) << 8;
   1116 
   1117 	/* save MClk values */
   1118 	ET6000IOWrite(pTseng, 0x67, 0x0A);
   1119 	tsengReg->ET6K_MClk = ET6000IORead(pTseng, 0x69);
   1120 	tsengReg->ET6K_MClk |= ET6000IORead(pTseng, 0x69) << 8;
   1121 
   1122 	tsengReg->ET6K_13 = ET6000IORead(pTseng, 0x13);
   1123 	tsengReg->ET6K_40 = ET6000IORead(pTseng, 0x40);
   1124 	tsengReg->ET6K_58 = ET6000IORead(pTseng, 0x58);
   1125 	tsengReg->ET6K_41 = ET6000IORead(pTseng, 0x41);
   1126 	tsengReg->ET6K_44 = ET6000IORead(pTseng, 0x44);
   1127 	tsengReg->ET6K_46 = ET6000IORead(pTseng, 0x46);
   1128     }
   1129 
   1130     tsengReg->CR30 = hwp->readCrtc(hwp, 0x30);
   1131     tsengReg->CR31 = hwp->readCrtc(hwp, 0x31);
   1132     tsengReg->CR3F = hwp->readCrtc(hwp, 0x3F);
   1133 }
   1134 
   1135 /*
   1136  * Restore a video mode
   1137  */
   1138 void
   1139 TsengRestore(ScrnInfoPtr pScrn, vgaRegPtr vgaReg, TsengRegPtr tsengReg,
   1140 	     int flags)
   1141 {
   1142     vgaHWPtr hwp = VGAHWPTR(pScrn);
   1143     TsengPtr pTseng = TsengPTR(pScrn);
   1144 
   1145     PDEBUG("	TsengRestore\n");
   1146 
   1147     vgaHWProtect(pScrn, TRUE);
   1148 
   1149     vgaHWWriteSegment(hwp, 0x00);		       /* segment select bits 0..3 */
   1150     vgaHWWriteBank(hwp, 0x00); /* segment select bits 4,5 */
   1151 
   1152     if (pTseng->ChipType == ET4000) {
   1153         switch (pTseng->RAMDAC) {
   1154         case STG1703:
   1155             STG1703Restore(pScrn, tsengReg->RAMDAC);
   1156             break;
   1157         case CH8398:
   1158             CH8398Restore(pScrn, tsengReg->RAMDAC);
   1159             break;
   1160         default:
   1161             break;
   1162 	}
   1163     } else {
   1164 	/* Restore ET6000 CLKDAC PLL registers */
   1165 	ET6000IOWrite(pTseng, 0x67, 0x03);
   1166 	ET6000IOWrite(pTseng, 0x69, tsengReg->ET6K_PLL & 0xFF);
   1167 	ET6000IOWrite(pTseng, 0x69, tsengReg->ET6K_PLL >> 8);
   1168 
   1169 	/* set MClk values if needed, but don't touch them if not needed
   1170          *
   1171          * Since setting the MClk to highly illegal value results in a
   1172          * total system crash, we'd better play it safe here.
   1173          * N1 must be <= 4, and N2 should always be 1
   1174          */
   1175         if ((tsengReg->ET6K_MClk & 0xF800) != 0x2000) {
   1176             xf86Msg(X_ERROR, "Internal Error in MClk registers: MClk: 0x%04X\n",
   1177 		    tsengReg->ET6K_MClk);
   1178         } else {
   1179             ET6000IOWrite(pTseng, 0x67, 10);
   1180             ET6000IOWrite(pTseng, 0x69, tsengReg->ET6K_MClk & 0xFF);
   1181             ET6000IOWrite(pTseng, 0x69, tsengReg->ET6K_MClk >> 8);
   1182 	}
   1183 
   1184 	ET6000IOWrite(pTseng, 0x13, tsengReg->ET6K_13);
   1185 	ET6000IOWrite(pTseng, 0x40, tsengReg->ET6K_40);
   1186 	ET6000IOWrite(pTseng, 0x58, tsengReg->ET6K_58);
   1187 	ET6000IOWrite(pTseng, 0x41, tsengReg->ET6K_41);
   1188 	ET6000IOWrite(pTseng, 0x44, tsengReg->ET6K_44);
   1189 	ET6000IOWrite(pTseng, 0x46, tsengReg->ET6K_46);
   1190     }
   1191 
   1192     hwp->writeCrtc(hwp, 0x3F, tsengReg->CR3F);
   1193     hwp->writeCrtc(hwp, 0x30, tsengReg->CR30);
   1194     hwp->writeCrtc(hwp, 0x31, tsengReg->CR31);
   1195 
   1196     vgaHWRestore(pScrn, vgaReg, flags); /* TODO: does this belong HERE, in the middle? */
   1197 
   1198     hwp->writeSeq(hwp, 0x06, tsengReg->SR06);
   1199     hwp->writeSeq(hwp, 0x07, tsengReg->SR07);
   1200 
   1201     hwp->writeAttr(hwp, 0x36, tsengReg->ExtATC);
   1202 
   1203     hwp->writeCrtc(hwp, 0x33, tsengReg->CR33);
   1204     hwp->writeCrtc(hwp, 0x34, tsengReg->CR34);
   1205     hwp->writeCrtc(hwp, 0x35, tsengReg->CR35);
   1206 
   1207     if (pTseng->ChipType == ET4000) {
   1208         hwp->writeCrtc(hwp, 0x37, tsengReg->CR37);
   1209 	hwp->writeCrtc(hwp, 0x32, tsengReg->CR32);
   1210     }
   1211 
   1212     TsengCursorRestore(pScrn, tsengReg);
   1213 
   1214     vgaHWWriteSegment(hwp, tsengReg->ExtSegSel[0]);
   1215     vgaHWWriteBank(hwp, tsengReg->ExtSegSel[1]);
   1216 
   1217     vgaHWProtect(pScrn, FALSE);
   1218 
   1219     /*
   1220      * We must change CRTC 0x36 only OUTSIDE the TsengProtect(pScrn,
   1221      * TRUE)/TsengProtect(pScrn, FALSE) pair, because the sequencer reset
   1222      * also resets the linear mode bits in CRTC 0x36.
   1223      */
   1224     if (pTseng->ChipType == ET4000)
   1225 	hwp->writeCrtc(hwp, 0x36, tsengReg->CR36);
   1226 }
   1227 
   1228 /*
   1229  *
   1230  */
   1231 Bool
   1232 TsengModeInit(ScrnInfoPtr pScrn, DisplayModePtr OrigMode)
   1233 {
   1234     vgaHWPtr hwp = VGAHWPTR(pScrn);
   1235     TsengPtr pTseng = TsengPTR(pScrn);
   1236     TsengRegPtr initial = &(pTseng->SavedReg);
   1237     TsengRegRec new[1];
   1238     DisplayModeRec mode[1];
   1239     int row_offset;
   1240 
   1241     PDEBUG("	TsengModeInit\n");
   1242 
   1243     new->RAMDAC = NULL;
   1244 
   1245     /* We're altering this mode */
   1246     memcpy(mode, OrigMode, sizeof(DisplayModeRec));
   1247     mode->next = NULL;
   1248     mode->prev = NULL;
   1249 
   1250     if (pTseng->ChipType == ET4000) {
   1251         int hmul = pTseng->Bytesperpixel;
   1252 
   1253         /* Modify mode timings accordingly
   1254          *
   1255          * If we move the vgaHWInit code up here directly, we no longer have
   1256          * to adjust mode->Crtc* but just handle things properly up here.
   1257          */
   1258         /* now divide and multiply the horizontal timing parameters as required */
   1259         mode->CrtcHTotal = (mode->CrtcHTotal * hmul) / 2;
   1260         mode->CrtcHDisplay = (mode->CrtcHDisplay * hmul) / 2;
   1261         mode->CrtcHSyncStart = (mode->CrtcHSyncStart * hmul) / 2;
   1262         mode->CrtcHSyncEnd = (mode->CrtcHSyncEnd * hmul) / 2;
   1263         mode->CrtcHBlankStart = (mode->CrtcHBlankStart * hmul) / 2;
   1264         mode->CrtcHBlankEnd = (mode->CrtcHBlankEnd * hmul) / 2;
   1265         mode->CrtcHSkew = (mode->CrtcHSkew * hmul) / 2;
   1266 
   1267         mode->Clock = (mode->Clock * hmul) / 2;
   1268 
   1269         if (pScrn->bitsPerPixel == 24) {
   1270             int rgb_skew;
   1271             /*
   1272              * in 24bpp, the position of the BLANK signal determines the
   1273              * phase of the R,G and B values. XFree86 sets blanking equal to
   1274              * the Sync, so setting the Sync correctly will also set the
   1275              * BLANK corectly, and thus also the RGB phase
   1276              */
   1277             rgb_skew = (mode->CrtcHTotal / 8 - mode->CrtcHBlankEnd / 8 - 1) % 3;
   1278             mode->CrtcHBlankEnd += rgb_skew * 8 + 24;
   1279             /* HBlankEnd must come BEFORE HTotal */
   1280             if (mode->CrtcHBlankEnd > mode->CrtcHTotal)
   1281                 mode->CrtcHBlankEnd -= 24;
   1282         }
   1283     }
   1284 
   1285     /* Some mode info */
   1286     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 7, "Setting up %s (%dMhz, %dbpp)\n",
   1287                    mode->name, mode->Clock, pScrn->bitsPerPixel);
   1288     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 7,
   1289                    "H: 0x%03X 0x%03X 0x%03X 0x%03X 0x%03X 0x%03X\n",
   1290                    mode->CrtcHDisplay, mode->CrtcHBlankStart, mode->CrtcHSyncStart,
   1291                    mode->CrtcHSyncEnd, mode->CrtcHBlankEnd, mode->CrtcHTotal);
   1292     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 7,
   1293                    "V: 0x%03X 0x%03X 0x%03X 0x%03X 0x%03X 0x%03X\n",
   1294                    mode->CrtcVDisplay, mode->CrtcVBlankStart, mode->CrtcVSyncStart,
   1295                    mode->CrtcVSyncEnd, mode->CrtcVBlankEnd, mode->CrtcVTotal);
   1296 
   1297     /* prepare standard VGA register contents */
   1298     if (!vgaHWInit(pScrn, mode))
   1299 	return (FALSE);
   1300     pScrn->vtSema = TRUE;
   1301 
   1302     /* prepare extended (Tseng) register contents */
   1303     /*
   1304      * Start by copying all the saved registers in the "new" data, so we
   1305      * only have to modify those that need to change.
   1306      */
   1307 
   1308     memcpy(new, initial, sizeof(TsengRegRec));
   1309 
   1310     if (pScrn->bitsPerPixel < 8) {
   1311 	/* Don't ask me why this is needed on the ET6000 and not on the others */
   1312 	if (pTseng->ChipType == ET6000)
   1313 	    hwp->ModeReg.Sequencer[1] |= 0x04;
   1314 	row_offset = hwp->ModeReg.CRTC[19];
   1315     } else {
   1316 	hwp->ModeReg.Attribute[16] = 0x01;	/* use the FAST 256 Color Mode */
   1317 	row_offset = pScrn->displayWidth >> 3;	/* overruled by 16/24/32 bpp code */
   1318     }
   1319 
   1320     hwp->ModeReg.CRTC[20] = 0x60;
   1321     hwp->ModeReg.CRTC[23] = 0xAB;
   1322     new->SR06 = 0x00;
   1323     new->SR07 = 0xBC;
   1324     new->CR33 = 0x00;
   1325 
   1326     new->CR35 = (mode->Flags & V_INTERLACE ? 0x80 : 0x00)
   1327 	| 0x10
   1328 	| ((mode->CrtcVSyncStart & 0x400) >> 7)
   1329 	| (((mode->CrtcVDisplay - 1) & 0x400) >> 8)
   1330 	| (((mode->CrtcVTotal - 2) & 0x400) >> 9)
   1331 	| (((mode->CrtcVBlankStart - 1) & 0x400) >> 10);
   1332 
   1333     if (pScrn->bitsPerPixel < 8)
   1334 	new->ExtATC = 0x00;
   1335     else
   1336 	new->ExtATC = 0x80;
   1337 
   1338     if (pScrn->bitsPerPixel >= 8) {
   1339 	if ((pTseng->ChipType == ET4000) && pTseng->FastDram) {
   1340 	    /*
   1341 	     *  make sure Trsp is no more than 75ns
   1342 	     *            Tcsw is 25ns
   1343 	     *            Tcsp is 25ns
   1344 	     *            Trcd is no more than 50ns
   1345 	     * Timings assume SCLK = 40MHz
   1346 	     *
   1347 	     * Note, this is experimental, but works for me (DHD)
   1348 	     */
   1349 	    /* Tcsw, Tcsp, Trsp */
   1350 	    new->CR32 &= ~0x1F;
   1351 	    if (initial->CR32 & 0x18)
   1352 		new->CR32 |= 0x08;
   1353 	    /* Trcd */
   1354 	    new->CR32 &= ~0x20;
   1355 	}
   1356     }
   1357     /*
   1358      * Here we make sure that CRTC regs 0x34 and 0x37 are untouched, except for
   1359      * some bits we want to change.
   1360      * Notably bit 7 of CRTC 0x34, which changes RAS setup time from 4 to 0 ns
   1361      * (performance),
   1362      * and bit 7 of CRTC 0x37, which changes the CRTC FIFO low treshold control.
   1363      * At really high pixel clocks, this will avoid lots of garble on the screen
   1364      * when something is being drawn. This only happens WAY beyond 80 MHz
   1365      * (those 135 MHz ramdac's...)
   1366      */
   1367     if (pTseng->ChipType == ET4000) {
   1368 	if (!pTseng->SlowDram)
   1369 	    new->CR34 |= 0x80;
   1370 	if ((mode->Clock * pTseng->Bytesperpixel) > 80000)
   1371 	    new->CR37 |= 0x80;
   1372 	/*
   1373 	 * now on to the memory interleave setting (CR32 bit 7)
   1374 	 */
   1375 	if (pTseng->SetW32Interleave) {
   1376 	    if (pTseng->W32Interleave)
   1377 		new->CR32 |= 0x80;
   1378 	    else
   1379 		new->CR32 &= 0x7F;
   1380 	}
   1381 
   1382 	/*
   1383 	 * CR34 bit 4 controls the PCI Burst option
   1384 	 */
   1385 	if (pTseng->SetPCIBurst) {
   1386 	    if (pTseng->PCIBurst)
   1387 		new->CR34 |= 0x10;
   1388 	    else
   1389 		new->CR34 &= 0xEF;
   1390 	}
   1391     }
   1392 
   1393     /* prepare clock-related registers when not Legend.
   1394      * cannot really SET the clock here yet, since the ET4000Save()
   1395      * is called LATER, so it would save the wrong state...
   1396      * ET4000Restore() is used to actually SET vga regs.
   1397      */
   1398     if (pTseng->ChipType == ET4000) {
   1399         switch (pTseng->RAMDAC) {
   1400         case STG1703:
   1401             new->RAMDAC =
   1402                 (struct STG1703Regs *) STG1703Mode(pScrn, initial->RAMDAC, mode);
   1403             break;
   1404         case CH8398:
   1405             new->RAMDAC =
   1406                 (struct CH8398Regs *) CH8398Mode(pScrn, initial->RAMDAC, mode);
   1407             break;
   1408         default:
   1409             break;
   1410         }
   1411     } else {
   1412 	/* setting min_n2 to "1" will ensure a more stable clock ("0" is allowed though) */
   1413 	new->ET6K_PLL = ET6000CalcClock(mode->SynthClock, 1, 1, 31, 1, 3,
   1414                                         100000, pTseng->max_vco_freq);
   1415 
   1416 	/* above 130MB/sec, we enable the "LOW FIFO threshold" */
   1417 	if (mode->Clock * pTseng->Bytesperpixel > 130000) {
   1418 	    new->ET6K_41 |= 0x10;
   1419 	    if (pTseng->ChipRev == REV_ET6100)
   1420 		new->ET6K_46 |= 0x04;
   1421 	} else {
   1422 	    new->ET6K_41 &= ~0x10;
   1423 	    if (pTseng->ChipRev == REV_ET6100)
   1424 		new->ET6K_46 &= ~0x04;
   1425 	}
   1426 
   1427         /* according to Tseng Labs, N1 must be <= 4, and N2 should always be 1 for MClk */
   1428         new->ET6K_MClk = ET6000CalcClock(pTseng->MemClk, 1, 1, 4, 1, 1, 100000,
   1429                                          pTseng->clockRange.maxClock * 2);
   1430 
   1431 	/*
   1432 	 * Even when we don't allow setting the MClk value as described
   1433 	 * above, we can use the FAST/MED/SLOW DRAM options to set up
   1434 	 * the RAS/CAS delays as decided by the value of ET6K_44.
   1435 	 * This is also a more correct use of the flags, as it describes
   1436 	 * how fast the RAM works. [HNH].
   1437 	 */
   1438 	if (pTseng->FastDram)
   1439 	    new->ET6K_44 = 0x04; /* Fastest speed(?) */
   1440 	else if (pTseng->MedDram)
   1441 	    new->ET6K_44 = 0x15; /* Medium speed */
   1442 	else if (pTseng->SlowDram)
   1443 	    new->ET6K_44 = 0x35; /* Slow speed */
   1444 	else
   1445 	    ;		               /* keep current value */
   1446     }
   1447     /*
   1448      * Set the clock selection bits. Because of the odd mapping between
   1449      * Tseng clock select bits and what XFree86 does, "CSx" refers to a
   1450      * register bit with the same name in the Tseng data books.
   1451      *
   1452      * XFree86 uses the following mapping:
   1453      *
   1454      *  Tseng register bit name		XFree86 clock select bit
   1455      *	    CS0				    0
   1456      *      CS1				    1
   1457      *      CS2				    2
   1458      *      MCLK/2			    3
   1459      *      CS3				    4
   1460      *      CS4				    not used
   1461      */
   1462     /* CS0 and CS1 are set by standard VGA code (vgaHW) */
   1463     /* CS2 = CRTC 0x34 bit 1 */
   1464     new->CR34 &= 0xFD;
   1465     /* for programmable clocks: disable MCLK/2 and MCLK/4 independent of hibit */
   1466     new->SR07 = (new->SR07 & 0xBE);
   1467     /* clock select bit 4 = CS3 , clear CS4 */
   1468     new->CR31 &= 0x3F;
   1469 
   1470     /*
   1471      * linear mode handling
   1472      */
   1473     if (pTseng->ChipType == ET6000) {
   1474         new->ET6K_13 = pTseng->FbAddress >> 24;
   1475         new->ET6K_40 |= 0x09;
   1476     } else {			       /* et4000 style linear memory */
   1477         new->CR36 |= 0x10;
   1478         new->CR30 = (pTseng->FbAddress >> 22) & 0xFF;
   1479         hwp->ModeReg.Graphics[6] &= ~0x0C;
   1480         new->CursorCtrl &= ~0x01;  /* disable IMA port (to get >1MB lin mem) */
   1481     }
   1482 
   1483     /*
   1484      * 16/24/32 bpp handling.
   1485      */
   1486     if (pTseng->ChipType == ET6000) {
   1487         /* ATC index 0x16 -- bits-per-PCLK */
   1488         new->ExtATC &= 0xCF;
   1489         new->ExtATC |= (pTseng->Bytesperpixel - 1) << 4;
   1490 
   1491         if (pScrn->bitsPerPixel == 15)
   1492             new->ET6K_58 &= ~0x02; /* 5-5-5 RGB mode */
   1493         else if (pScrn->bitsPerPixel == 16)
   1494             new->ET6K_58 |= 0x02; /* 5-6-5 RGB mode */
   1495     } else {
   1496         /* ATC index 0x16 -- bits-per-PCLK */
   1497         new->ExtATC &= 0xCF;
   1498         new->ExtATC |= 0x20;
   1499     }
   1500 
   1501     row_offset *= pTseng->Bytesperpixel;
   1502 
   1503 
   1504     /*
   1505      * Horizontal overflow settings: for modes with > 2048 pixels per line
   1506      */
   1507 
   1508     hwp->ModeReg.CRTC[19] = row_offset;
   1509     new->CR3F = ((((mode->CrtcHTotal >> 3) - 5) & 0x100) >> 8)
   1510 	| ((((mode->CrtcHDisplay >> 3) - 1) & 0x100) >> 7)
   1511 	| ((((mode->CrtcHBlankStart >> 3) - 1) & 0x100) >> 6)
   1512 	| (((mode->CrtcHSyncStart >> 3) & 0x100) >> 4)
   1513 	| ((row_offset & 0x200) >> 3)
   1514 	| ((row_offset & 0x100) >> 1);
   1515 
   1516     /*
   1517      * Enable memory mapped IO registers when acceleration is needed.
   1518      */
   1519 
   1520     if (pTseng->UseAccel) {
   1521 	if (pTseng->ChipType == ET6000)
   1522             new->ET6K_40 |= 0x02;	/* MMU can't be used here (causes system hang...) */
   1523 	else
   1524 	    new->CR36 |= 0x28;
   1525     }
   1526     vgaHWUnlock(hwp);		       /* TODO: is this needed (tsengEnterVT does this) */
   1527 
   1528     /* Program the registers */
   1529     TsengRestore(pScrn, &hwp->ModeReg, new, VGA_SR_MODE);
   1530 
   1531     /* clean up */
   1532     if (new->RAMDAC)
   1533         free(new->RAMDAC);
   1534 
   1535     return TRUE;
   1536 }
   1537 
   1538 /*
   1539  * TsengCrtcDPMSSet --
   1540  *
   1541  * Sets VESA Display Power Management Signaling (DPMS) Mode.
   1542  * This routine is for the ET4000W32P rev. c and later, which can
   1543  * use CRTC indexed register 34 to turn off H/V Sync signals.
   1544  *
   1545  * '97 Harald Nordgrd Hansen
   1546  */
   1547 void
   1548 TsengCrtcDPMSSet(ScrnInfoPtr pScrn, int PowerManagementMode, int flags)
   1549 {
   1550     vgaHWPtr hwp = VGAHWPTR(pScrn);
   1551     CARD8 seq1, crtc34;
   1552 
   1553 #if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 8
   1554     xf86EnableAccess(pScrn);
   1555 #endif
   1556     switch (PowerManagementMode) {
   1557     case DPMSModeOn:
   1558     default:
   1559 	/* Screen: On; HSync: On, VSync: On */
   1560 	seq1 = 0x00;
   1561 	crtc34 = 0x00;
   1562 	break;
   1563     case DPMSModeStandby:
   1564 	/* Screen: Off; HSync: Off, VSync: On */
   1565 	seq1 = 0x20;
   1566 	crtc34 = 0x01;
   1567 	break;
   1568     case DPMSModeSuspend:
   1569 	/* Screen: Off; HSync: On, VSync: Off */
   1570 	seq1 = 0x20;
   1571 	crtc34 = 0x20;
   1572 	break;
   1573     case DPMSModeOff:
   1574 	/* Screen: Off; HSync: Off, VSync: Off */
   1575 	seq1 = 0x20;
   1576 	crtc34 = 0x21;
   1577 	break;
   1578     }
   1579 
   1580     seq1 |= hwp->readSeq(hwp, 0x01) & ~0x20;
   1581     hwp->writeSeq(hwp, 0x01, seq1);
   1582 
   1583     crtc34 |= hwp->readCrtc(hwp, 0x34) & ~0x21;
   1584     hwp->writeCrtc(hwp, 0x34, crtc34);
   1585 }
   1586 
   1587 /*
   1588  * TsengHVSyncDPMSSet --
   1589  *
   1590  * Sets VESA Display Power Management Signaling (DPMS) Mode.
   1591  * This routine is for Tseng et4000 chips that do not have any
   1592  * registers to disable sync output.
   1593  *
   1594  * The "classic" (standard VGA compatible) method; disabling all syncs,
   1595  * causes video memory corruption on Tseng cards, according to "Tseng
   1596  * ET4000/W32 family tech note #20":
   1597  *
   1598  *   "Setting CRTC Indexed Register 17 bit 7 = 0 will disable the video
   1599  *    syncs (=VESA DPMS power down), but will also disable DRAM refresh cycles"
   1600  *
   1601  * The method used here is derived from the same tech note, which describes
   1602  * a method to disable specific sync signals on chips that do not have
   1603  * direct support for it:
   1604  *
   1605  *    To get vsync off, program VSYNC_START > VTOTAL
   1606  *    (approximately). In particular, the formula used is:
   1607  *
   1608  *        VSYNC.ADJ = (VTOT - VSYNC.NORM) + VTOT + 4
   1609  *
   1610  *        To test for this state, test if VTOT + 1 < VSYNC
   1611  *
   1612  *
   1613  *    To get hsync off, program HSYNC_START > HTOTAL
   1614  *    (approximately). In particular, the following formula is used:
   1615  *
   1616  *        HSYNC.ADJ = (HTOT - HSYNC.NORM) + HTOT + 7
   1617  *
   1618  *        To test for this state, test if HTOT + 3 < HSYNC
   1619  *
   1620  * The advantage of these formulas is that the ON state can be restored by
   1621  * reversing the formula. The original state need not be stored anywhere...
   1622  *
   1623  * The trick in the above approach is obviously to put the start of the sync
   1624  * _beyond_ the total H or V counter range, which causes the sync to never
   1625  * toggle.
   1626  */
   1627 void
   1628 TsengHVSyncDPMSSet(ScrnInfoPtr pScrn,
   1629     int PowerManagementMode, int flags)
   1630 {
   1631     vgaHWPtr hwp = VGAHWPTR(pScrn);
   1632     CARD8 seq1, tmpb;
   1633     CARD32 HSync, VSync, HTot, VTot, tmp;
   1634     Bool chgHSync, chgVSync;
   1635 
   1636     /* Code here to read the current values of HSync through VTot:
   1637      *  HSYNC:
   1638      *    bits 0..7 : CRTC index 0x04
   1639      *    bit 8     : CRTC index 0x3F, bit 4
   1640      */
   1641     HSync = hwp->readCrtc(hwp, 0x04);
   1642     HSync += (hwp->readCrtc(hwp, 0x3F) & 0x10) << 4;
   1643 
   1644     /*  VSYNC:
   1645      *    bits 0..7 : CRTC index 0x10
   1646      *    bits 8..9 : CRTC index 0x07 bits 2 (VSYNC bit 8) and 7 (VSYNC bit 9)
   1647      *    bit 10    : CRTC index 0x35 bit 3
   1648      */
   1649     VSync = hwp->readCrtc(hwp, 0x10);
   1650     tmp = hwp->readCrtc(hwp, 0x07);
   1651     VSync += ((tmp & 0x04) << 6) + ((tmp & 0x80) << 2);
   1652     VSync += (hwp->readCrtc(hwp, 0x35) & 0x08) << 7;
   1653 
   1654     /*  HTOT:
   1655      *    bits 0..7 : CRTC index 0x00.
   1656      *    bit 8     : CRTC index 0x3F, bit 0
   1657      */
   1658     HTot = hwp->readCrtc(hwp, 0x00);
   1659     HTot += (hwp->readCrtc(hwp, 0x3F) & 0x01) << 8;
   1660     /*  VTOT:
   1661      *    bits 0..7 : CRTC index 0x06
   1662      *    bits 8..9 : CRTC index 0x07 bits 0 (VTOT bit 8) and 5 (VTOT bit 9)
   1663      *    bit 10    : CRTC index 0x35 bit 1
   1664      */
   1665     VTot = hwp->readCrtc(hwp, 0x06);
   1666     tmp = hwp->readCrtc(hwp, 0x07);
   1667     VTot += ((tmp & 0x01) << 8) + ((tmp & 0x20) << 4);
   1668     VTot += (hwp->readCrtc(hwp, 0x35) & 0x02) << 9;
   1669 
   1670     /* Don't write these unless we have to. */
   1671     chgHSync = chgVSync = FALSE;
   1672 
   1673     switch (PowerManagementMode) {
   1674     case DPMSModeOn:
   1675     default:
   1676 	/* Screen: On; HSync: On, VSync: On */
   1677 	seq1 = 0x00;
   1678 	if (HSync > HTot + 3) {	       /* Sync is off now, turn it on. */
   1679 	    HSync = (HTot - HSync) + HTot + 7;
   1680 	    chgHSync = TRUE;
   1681 	}
   1682 	if (VSync > VTot + 1) {	       /* Sync is off now, turn it on. */
   1683 	    VSync = (VTot - VSync) + VTot + 4;
   1684 	    chgVSync = TRUE;
   1685 	}
   1686 	break;
   1687     case DPMSModeStandby:
   1688 	/* Screen: Off; HSync: Off, VSync: On */
   1689 	seq1 = 0x20;
   1690 	if (HSync <= HTot + 3) {       /* Sync is on now, turn it off. */
   1691 	    HSync = (HTot - HSync) + HTot + 7;
   1692 	    chgHSync = TRUE;
   1693 	}
   1694 	if (VSync > VTot + 1) {	       /* Sync is off now, turn it on. */
   1695 	    VSync = (VTot - VSync) + VTot + 4;
   1696 	    chgVSync = TRUE;
   1697 	}
   1698 	break;
   1699     case DPMSModeSuspend:
   1700 	/* Screen: Off; HSync: On, VSync: Off */
   1701 	seq1 = 0x20;
   1702 	if (HSync > HTot + 3) {	       /* Sync is off now, turn it on. */
   1703 	    HSync = (HTot - HSync) + HTot + 7;
   1704 	    chgHSync = TRUE;
   1705 	}
   1706 	if (VSync <= VTot + 1) {       /* Sync is on now, turn it off. */
   1707 	    VSync = (VTot - VSync) + VTot + 4;
   1708 	    chgVSync = TRUE;
   1709 	}
   1710 	break;
   1711     case DPMSModeOff:
   1712 	/* Screen: Off; HSync: Off, VSync: Off */
   1713 	seq1 = 0x20;
   1714 	if (HSync <= HTot + 3) {       /* Sync is on now, turn it off. */
   1715 	    HSync = (HTot - HSync) + HTot + 7;
   1716 	    chgHSync = TRUE;
   1717 	}
   1718 	if (VSync <= VTot + 1) {       /* Sync is on now, turn it off. */
   1719 	    VSync = (VTot - VSync) + VTot + 4;
   1720 	    chgVSync = TRUE;
   1721 	}
   1722 	break;
   1723     }
   1724 
   1725     /* If the new hsync or vsync overflows, don't change anything. */
   1726     if (HSync >= 1 << 9 || VSync >= 1 << 11) {
   1727 	ErrorF("tseng: warning: Cannot go into DPMS from this resolution.\n");
   1728 	chgVSync = chgHSync = FALSE;
   1729     }
   1730     /* The code to turn on and off video output is equal for all. */
   1731     if (chgHSync || chgVSync) {
   1732 	seq1 |= hwp->readSeq(hwp, 0x01) & ~0x20;
   1733 	hwp->writeSeq(hwp, 0x01, seq1);
   1734     }
   1735     /* Then the code to write VSync and HSync to the card.
   1736      *  HSYNC:
   1737      *    bits 0..7 : CRTC index 0x04
   1738      *    bit 8     : CRTC index 0x3F, bit 4
   1739      */
   1740     if (chgHSync) {
   1741 	tmpb = HSync & 0xFF;
   1742 	hwp->writeCrtc(hwp, 0x04, tmpb);
   1743 
   1744 	tmpb = (HSync & 0x100) >> 4;
   1745 	tmpb |= hwp->readCrtc(hwp, 0x3F) & ~0x10;
   1746         hwp->writeCrtc(hwp, 0x3F, tmpb);
   1747     }
   1748     /*  VSYNC:
   1749      *    bits 0..7 : CRTC index 0x10
   1750      *    bits 8..9 : CRTC index 0x07 bits 2 (VSYNC bit 8) and 7 (VSYNC bit 9)
   1751      *    bit 10    : CRTC index 0x35 bit 3
   1752      */
   1753     if (chgVSync) {
   1754 	tmpb = VSync & 0xFF;
   1755 	hwp->writeCrtc(hwp, 0x10, tmpb);
   1756 
   1757 	tmpb = (VSync & 0x100) >> 6;
   1758 	tmpb |= (VSync & 0x200) >> 2;
   1759 	tmpb |= hwp->readCrtc(hwp, 0x07) & ~0x84;
   1760 	hwp->writeCrtc(hwp, 0x07, tmpb);
   1761 
   1762 	tmpb = (VSync & 0x400) >> 7;
   1763 	tmpb |= hwp->readCrtc(hwp, 0x35) & ~0x08;
   1764 	hwp->writeCrtc(hwp, 0x35, tmpb);
   1765     }
   1766 }
   1767