tseng_mode.c revision 4b9470b1
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#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
51CARD8
52vgaHWReadBank(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
66void
67vgaHWWriteSegment(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
79CARD8
80vgaHWReadSegment(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
97void
98vgaHWWriteModeControl(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
118void
119vgaHWHerculesSecondPage(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 */
156CARD8
157ET6000IORead(TsengPtr pTseng, CARD8 Offset)
158{
159    return inb(pTseng->ET6000IOAddress + Offset);
160}
161
162void
163ET6000IOWrite(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 */
182static Bool
183STG1703Detect(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 */
244struct STG1703Regs {
245    CARD8 Command;
246    CARD8 Pixel;
247    CARD8 Timing;
248    CARD16 PLL;
249};
250
251/*
252 *
253 */
254static void
255STG1703PrintRegs(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 */
271static void
272STG1703Store(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 */
343static void
344STG1703Restore(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 */
414static CARD16
415STG1703Clock(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 */
464static struct STG1703Regs *
465STG1703Mode(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 */
526static Bool
527CH8398Detect(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 */
553struct CH8398Regs {
554    CARD8 Control;
555    CARD8 Aux;
556    CARD16 PLL;
557};
558
559/*
560 *
561 */
562static void
563CH8398PrintRegs(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 */
577static void
578CH8398Store(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 */
623static void
624CH8398Restore(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 */
660static CARD16
661CH8398Clock(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 */
707static struct CH8398Regs *
708CH8398Mode(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 */
748Bool
749TsengRAMDACProbe(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 */
801void
802TsengSetupClockRange(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 */
913static CARD16
914ET6000CalcClock(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 */
984void
985TsengAdjustFrame(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 */
1013ModeStatus
1014TsengValidMode(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 */
1037void
1038TsengSave(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 */
1138void
1139TsengRestore(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 */
1231Bool
1232TsengModeInit(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 Nordgård Hansen
1546 */
1547void
1548TsengCrtcDPMSSet(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 */
1627void
1628TsengHVSyncDPMSSet(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