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