196cdd0b9Skiyohara/*
296cdd0b9Skiyohara * Copyright (c) 2009 KIYOHARA Takashi
396cdd0b9Skiyohara * All rights reserved.
496cdd0b9Skiyohara *
596cdd0b9Skiyohara * Redistribution and use in source and binary forms, with or without
696cdd0b9Skiyohara * modification, are permitted provided that the following conditions
796cdd0b9Skiyohara * are met:
896cdd0b9Skiyohara * 1. Redistributions of source code must retain the above copyright
996cdd0b9Skiyohara *    notice, this list of conditions and the following disclaimer.
1096cdd0b9Skiyohara * 2. Redistributions in binary form must reproduce the above copyright
1196cdd0b9Skiyohara *    notice, this list of conditions and the following disclaimer in the
1296cdd0b9Skiyohara *    documentation and/or other materials provided with the distribution.
1396cdd0b9Skiyohara *
1496cdd0b9Skiyohara * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1596cdd0b9Skiyohara * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1696cdd0b9Skiyohara * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1796cdd0b9Skiyohara * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
1896cdd0b9Skiyohara * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1996cdd0b9Skiyohara * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2096cdd0b9Skiyohara * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2196cdd0b9Skiyohara * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2296cdd0b9Skiyohara * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2396cdd0b9Skiyohara * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2496cdd0b9Skiyohara * POSSIBILITY OF SUCH DAMAGE.
2596cdd0b9Skiyohara *
2696cdd0b9Skiyohara */
2796cdd0b9Skiyohara#ifdef HAVE_CONFIG_H
2896cdd0b9Skiyohara#include "config.h"
2996cdd0b9Skiyohara#endif
3096cdd0b9Skiyohara
3196cdd0b9Skiyohara#include "xf86.h"
3296cdd0b9Skiyohara#include "xf86_OSproc.h"
33a9060c92Schristos#include <X11/Xos.h>
3496cdd0b9Skiyohara
3596cdd0b9Skiyohara#include "compiler.h"
3696cdd0b9Skiyohara
3796cdd0b9Skiyohara#include "s3.h"
3896cdd0b9Skiyohara
3996cdd0b9Skiyohara#define GENDAC_INDEX		0x3C8
4096cdd0b9Skiyohara#define GENDAC_DATA		0x3C9
4196cdd0b9Skiyohara
4296cdd0b9Skiyohara
4396cdd0b9Skiyoharastatic void S3GENDACSetClock(ScrnInfoPtr, long, int, int, int, int, int, int,
4496cdd0b9Skiyohara			     int, long, long);
4596cdd0b9Skiyoharastatic void S3GENDACCalcClock(long, int, int, int, int, int, long, long,
4696cdd0b9Skiyohara			      unsigned char *, unsigned char *);
4796cdd0b9Skiyoharastatic void S3GENDACSetPLL(ScrnInfoPtr, int, unsigned char, unsigned char);
4896cdd0b9Skiyohara
4996cdd0b9Skiyohara
5096cdd0b9Skiyoharastatic void xf86dactopel(void);
5196cdd0b9Skiyohara
5296cdd0b9Skiyoharastatic void
5396cdd0b9Skiyoharaxf86dactopel()
5496cdd0b9Skiyohara{
5596cdd0b9Skiyohara	outb(0x3C8,0);
5696cdd0b9Skiyohara	return;
5796cdd0b9Skiyohara}
5896cdd0b9Skiyohara
5996cdd0b9Skiyohara
6096cdd0b9SkiyoharaBool S3GENDACProbe(ScrnInfoPtr pScrn)
6196cdd0b9Skiyohara{
6296cdd0b9Skiyohara	/* probe for S3 GENDAC/SDAC */
6396cdd0b9Skiyohara	/*
6496cdd0b9Skiyohara	 * S3 GENDAC and SDAC have two fixed read only PLL clocks
6596cdd0b9Skiyohara	 *     CLK0 f0: 25.255MHz   M-byte 0x28  N-byte 0x61
6696cdd0b9Skiyohara	 *     CLK0 f1: 28.311MHz   M-byte 0x3d  N-byte 0x62
6796cdd0b9Skiyohara	 * which can be used to detect GENDAC and SDAC since there is no chip-id
6896cdd0b9Skiyohara	 * for the GENDAC.
6996cdd0b9Skiyohara	 *
7096cdd0b9Skiyohara	 * NOTE: for the GENDAC on a MIRO 10SD (805+GENDAC) reading PLL values
7196cdd0b9Skiyohara	 * for CLK0 f0 and f1 always returns 0x7f
7296cdd0b9Skiyohara	 * (but is documented "read only")
7396cdd0b9Skiyohara	 */
7496cdd0b9Skiyohara
7596cdd0b9Skiyohara	S3Ptr pS3 = S3PTR(pScrn);
7696cdd0b9Skiyohara	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
7796cdd0b9Skiyohara	unsigned char saveCR55, saveCR45, saveCR43, savelut[6];
7896cdd0b9Skiyohara	unsigned int i;		/* don't use signed int, UW2.0 compiler bug */
7996cdd0b9Skiyohara	long clock01, clock23;
8096cdd0b9Skiyohara	int found = 0;
8196cdd0b9Skiyohara
8296cdd0b9Skiyohara	if (!S3_864_SERIES())	/* need? */
8396cdd0b9Skiyohara		return FALSE;
8496cdd0b9Skiyohara
8596cdd0b9Skiyohara	outb(vgaCRIndex, 0x43);
8696cdd0b9Skiyohara	saveCR43 = inb(vgaCRReg);
8796cdd0b9Skiyohara	outb(vgaCRReg, saveCR43 & ~0x02);
8896cdd0b9Skiyohara
8996cdd0b9Skiyohara	outb(vgaCRIndex, 0x45);
9096cdd0b9Skiyohara	saveCR45 = inb(vgaCRReg);
9196cdd0b9Skiyohara	outb(vgaCRReg, saveCR45 & ~0x20);
9296cdd0b9Skiyohara
9396cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
9496cdd0b9Skiyohara	saveCR55 = inb(vgaCRReg);
9596cdd0b9Skiyohara	outb(vgaCRReg, saveCR55 & ~1);
9696cdd0b9Skiyohara
9796cdd0b9Skiyohara	outb(0x3c7,0);
9896cdd0b9Skiyohara	for(i = 0; i < 2 * 3; i++)	/* save first two LUT entries */
9996cdd0b9Skiyohara		savelut[i] = inb(0x3c9);
10096cdd0b9Skiyohara	outb(0x3c8,0);
10196cdd0b9Skiyohara	for(i = 0; i < 2 * 3; i++)	/* set first two LUT entries to zero */
10296cdd0b9Skiyohara		outb(0x3c9, 0);
10396cdd0b9Skiyohara
10496cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
10596cdd0b9Skiyohara	outb(vgaCRReg, saveCR55 | 1);
10696cdd0b9Skiyohara
10796cdd0b9Skiyohara	outb(0x3c7,0);
10896cdd0b9Skiyohara	for(i = clock01 = 0; i < 4; i++)
10996cdd0b9Skiyohara		clock01 = (clock01 << 8) | (inb(0x3c9) & 0xff);
11096cdd0b9Skiyohara	for(i = clock23 = 0; i < 4; i++)
11196cdd0b9Skiyohara		clock23 = (clock23 << 8) | (inb(0x3c9) & 0xff);
11296cdd0b9Skiyohara
11396cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
11496cdd0b9Skiyohara	outb(vgaCRReg, saveCR55 & ~1);
11596cdd0b9Skiyohara
11696cdd0b9Skiyohara	outb(0x3c8,0);
11796cdd0b9Skiyohara	for(i = 0; i < 2 * 3; i++)	/* restore first two LUT entries */
11896cdd0b9Skiyohara		outb(0x3c9, savelut[i]);
11996cdd0b9Skiyohara
12096cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
12196cdd0b9Skiyohara	outb(vgaCRReg, saveCR55);
12296cdd0b9Skiyohara
12396cdd0b9Skiyohara	if (clock01 == 0x28613d62 ||
12496cdd0b9Skiyohara	    (clock01 == 0x7f7f7f7f && clock23 != 0x7f7f7f7f)) {
12596cdd0b9Skiyohara		xf86dactopel();
12696cdd0b9Skiyohara		inb(0x3c6);
12796cdd0b9Skiyohara		inb(0x3c6);
12896cdd0b9Skiyohara		inb(0x3c6);
12996cdd0b9Skiyohara
13096cdd0b9Skiyohara		/* the fourth read will show the SDAC chip ID and revision */
13196cdd0b9Skiyohara		if (((i = inb(0x3c6)) & 0xf0) == 0x70)
13296cdd0b9Skiyohara			found = SDAC_RAMDAC;
13396cdd0b9Skiyohara		else
13496cdd0b9Skiyohara			found = GENDAC_RAMDAC;
13596cdd0b9Skiyohara		saveCR43 &= ~0x02;
13696cdd0b9Skiyohara		saveCR45 &= ~0x20;
13796cdd0b9Skiyohara		xf86dactopel();
13896cdd0b9Skiyohara	}
13996cdd0b9Skiyohara
14096cdd0b9Skiyohara	outb(vgaCRIndex, 0x45);
14196cdd0b9Skiyohara	outb(vgaCRReg, saveCR45);
14296cdd0b9Skiyohara
14396cdd0b9Skiyohara	outb(vgaCRIndex, 0x43);
14496cdd0b9Skiyohara	outb(vgaCRReg, saveCR43);
14596cdd0b9Skiyohara
14696cdd0b9Skiyohara	if (found) {
14796cdd0b9Skiyohara		RamDacInit(pScrn, pS3->RamDacRec);
14896cdd0b9Skiyohara		pS3->RamDac = RamDacHelperCreateInfoRec();
14996cdd0b9Skiyohara		pS3->RamDac->RamDacType = found;
15096cdd0b9Skiyohara		return TRUE;
15196cdd0b9Skiyohara	}
15296cdd0b9Skiyohara	return FALSE;
15396cdd0b9Skiyohara}
15496cdd0b9Skiyohara
15596cdd0b9Skiyoharavoid S3GENDAC_PreInit(ScrnInfoPtr pScrn)
15696cdd0b9Skiyohara{
15796cdd0b9Skiyohara	S3Ptr pS3 = S3PTR(pScrn);
15896cdd0b9Skiyohara	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
15996cdd0b9Skiyohara	unsigned char saveCR55;
16096cdd0b9Skiyohara	int m, n, n1, n2, mclk;
16196cdd0b9Skiyohara
16296cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
16396cdd0b9Skiyohara	saveCR55 = inb(vgaCRReg);
16496cdd0b9Skiyohara	outb(vgaCRReg, saveCR55 | 1);
16596cdd0b9Skiyohara
16696cdd0b9Skiyohara	outb(0x3C7, 10); /* read MCLK */
16796cdd0b9Skiyohara	m = inb(0x3C9);
16896cdd0b9Skiyohara	n = inb(0x3C9);
16996cdd0b9Skiyohara
17096cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
17196cdd0b9Skiyohara	outb(vgaCRReg, saveCR55);
17296cdd0b9Skiyohara
17396cdd0b9Skiyohara	m &= 0x7f;
17496cdd0b9Skiyohara	n1 = n & 0x1f;
17596cdd0b9Skiyohara	n2 = (n >> 5) & 0x03;
17696cdd0b9Skiyohara	mclk = ((1431818 * (m + 2)) / (n1 + 2) / (1 << n2) + 50) / 100;
17796cdd0b9Skiyohara
17896cdd0b9Skiyohara	pS3->mclk = mclk;
17996cdd0b9Skiyohara	xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f MHz\n",
18096cdd0b9Skiyohara	    mclk / 1000.0);
18196cdd0b9Skiyohara}
18296cdd0b9Skiyohara
18396cdd0b9Skiyoharavoid S3GENDAC_Init(ScrnInfoPtr pScrn, DisplayModePtr mode)
18496cdd0b9Skiyohara{
18596cdd0b9Skiyohara	S3Ptr pS3 = S3PTR(pScrn);
18696cdd0b9Skiyohara	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
18796cdd0b9Skiyohara	int daccomm = 0;	/* GENDAC command */
18896cdd0b9Skiyohara	unsigned char blank, tmp;
18996cdd0b9Skiyohara
19096cdd0b9Skiyohara	S3GENDACSetClock(pScrn, mode->Clock * (pScrn->depth >> 3), 2, 1, 1, 31,
19196cdd0b9Skiyohara	    0, 3, 1, 100000, 250000);
19296cdd0b9Skiyohara
19396cdd0b9Skiyohara	outb(0x3C4, 1);
19496cdd0b9Skiyohara	blank = inb(0x3C5);
19596cdd0b9Skiyohara	outb(0x3C5, blank | 0x20); /* blank the screen */
19696cdd0b9Skiyohara
19796cdd0b9Skiyohara	switch (pScrn->depth)
19896cdd0b9Skiyohara	{
19996cdd0b9Skiyohara	case 8:		/* 8-bit color, 1VCLK/pixel */
20096cdd0b9Skiyohara		break;
20196cdd0b9Skiyohara
20296cdd0b9Skiyohara	case 15:	/* 15-bit color, 2VCLK/pixel */
20396cdd0b9Skiyohara		daccomm = 0x20;
20496cdd0b9Skiyohara		break;
20596cdd0b9Skiyohara
20696cdd0b9Skiyohara	case 16:	/* 16-bit color, 2VCLK/pixel */
20796cdd0b9Skiyohara		daccomm = 0x60;
20896cdd0b9Skiyohara		break;
20996cdd0b9Skiyohara
21096cdd0b9Skiyohara	case 32:	/* 32-bit color, 3VCLK/pixel */
21196cdd0b9Skiyohara		daccomm = 0x40;
21296cdd0b9Skiyohara	}
21396cdd0b9Skiyohara
21496cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
21596cdd0b9Skiyohara	tmp = inb(vgaCRReg) | 1;
21696cdd0b9Skiyohara	outb(vgaCRReg, tmp);
21796cdd0b9Skiyohara
21896cdd0b9Skiyohara	outb(0x3c6, daccomm);		/* set GENDAC mux mode */
21996cdd0b9Skiyohara
22096cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
22196cdd0b9Skiyohara	tmp = inb(vgaCRReg) & ~1;
22296cdd0b9Skiyohara	outb(vgaCRReg, tmp);
22396cdd0b9Skiyohara
22496cdd0b9Skiyohara	outb(0x3C4, 1);
22596cdd0b9Skiyohara	outb(0x3C5, blank);		/* unblank the screen */
22696cdd0b9Skiyohara}
22796cdd0b9Skiyohara
22896cdd0b9Skiyoharavoid S3SDAC_Init(ScrnInfoPtr pScrn, DisplayModePtr mode)
22996cdd0b9Skiyohara{
23096cdd0b9Skiyohara	S3Ptr pS3 = S3PTR(pScrn);
23196cdd0b9Skiyohara	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
23296cdd0b9Skiyohara	int pixmux = 0;		/* SDAC command and CR67 */
23396cdd0b9Skiyohara	int blank_delay = 0;	/* CR6D */
23496cdd0b9Skiyohara	int invert_vclk = 0;	/* CR66 bit 0 */
23596cdd0b9Skiyohara	unsigned char blank, tmp;
23696cdd0b9Skiyohara
23796cdd0b9Skiyohara#if 0
23896cdd0b9Skiyohara	S3GENDACSetClock(pScrn,
23996cdd0b9Skiyohara	    (pScrn->depth == 32) ? mode->Clock * 2 : mode->Clock,
24096cdd0b9Skiyohara	    2, 1, 1, 31, 0, 3, 1, 100000, 250000);
24196cdd0b9Skiyohara#else
24296cdd0b9Skiyohara	/* XXXX: for prep */
24396cdd0b9Skiyohara	long freq;
24496cdd0b9Skiyohara
24596cdd0b9Skiyohara	switch (pScrn->depth) {
24696cdd0b9Skiyohara	case 32:
24796cdd0b9Skiyohara		freq = mode->Clock * 2;	/* XXXX: frem xfree86 3.x */
24896cdd0b9Skiyohara		break;
24996cdd0b9Skiyohara	case 16:
25096cdd0b9Skiyohara		freq = mode->Clock / 2;
25196cdd0b9Skiyohara		break;
25296cdd0b9Skiyohara	default:
25396cdd0b9Skiyohara		freq = mode->Clock;
25496cdd0b9Skiyohara		break;
25596cdd0b9Skiyohara	}
25696cdd0b9Skiyohara	S3GENDACSetClock(pScrn, freq,
25796cdd0b9Skiyohara	    2, 1, 1, 31, 0, 3, 1, 100000, 250000);
25896cdd0b9Skiyohara#endif
25996cdd0b9Skiyohara
26096cdd0b9Skiyohara	outb(vgaCRIndex, 0x42);/* select the clock */
26196cdd0b9Skiyohara	tmp = inb(vgaCRReg) & 0xf0;
26296cdd0b9Skiyohara	outb(vgaCRReg, tmp | 0x02);
26396cdd0b9Skiyohara	usleep(150000);
26496cdd0b9Skiyohara
26596cdd0b9Skiyohara	outb(0x3C4, 1);
26696cdd0b9Skiyohara	blank = inb(0x3C5);
26796cdd0b9Skiyohara	outb(0x3C5, blank | 0x20); /* blank the screen */
26896cdd0b9Skiyohara
26996cdd0b9Skiyohara	switch (pScrn->depth)
27096cdd0b9Skiyohara	{
27196cdd0b9Skiyohara	case 8:		/* 8-bit color, 1 VCLK/pixel */
27296cdd0b9Skiyohara		pixmux = 0x10;
27396cdd0b9Skiyohara		blank_delay = 2;
27496cdd0b9Skiyohara		invert_vclk = 1;
27596cdd0b9Skiyohara		break;
27696cdd0b9Skiyohara
27796cdd0b9Skiyohara	case 15:	/* 15-bit color, 1VCLK/pixel */
27896cdd0b9Skiyohara		pixmux = 0x30;
27996cdd0b9Skiyohara		blank_delay = 2;
28096cdd0b9Skiyohara		break;
28196cdd0b9Skiyohara
28296cdd0b9Skiyohara	case 16:	/* 16-bit color, 1VCLK/pixel */
28396cdd0b9Skiyohara		pixmux = 0x50;
28496cdd0b9Skiyohara		blank_delay = 2;
28596cdd0b9Skiyohara		break;
28696cdd0b9Skiyohara
28796cdd0b9Skiyohara	case 32:	/* 32-bit color, 2VCLK/pixel */
28896cdd0b9Skiyohara		pixmux = 0x70;
28996cdd0b9Skiyohara		blank_delay = 2;
29096cdd0b9Skiyohara	}
29196cdd0b9Skiyohara
29296cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
29396cdd0b9Skiyohara	tmp = inb(vgaCRReg) | 1;
29496cdd0b9Skiyohara	outb(vgaCRReg, tmp);
29596cdd0b9Skiyohara
29696cdd0b9Skiyohara	outb(vgaCRIndex, 0x67);
29796cdd0b9Skiyohara	outb(vgaCRReg, pixmux | invert_vclk);	/* set S3 mux mode */
29896cdd0b9Skiyohara	outb(0x3c6, pixmux);			/* set SDAC mux mode */
29996cdd0b9Skiyohara
30096cdd0b9Skiyohara	outb(vgaCRIndex, 0x6D);
30196cdd0b9Skiyohara	outb(vgaCRReg, blank_delay);		/* set blank delay */
30296cdd0b9Skiyohara
30396cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
30496cdd0b9Skiyohara	tmp = inb(vgaCRReg) & ~1;
30596cdd0b9Skiyohara	outb(vgaCRReg, tmp);
30696cdd0b9Skiyohara
30796cdd0b9Skiyohara	outb(0x3C4, 1);
30896cdd0b9Skiyohara	outb(0x3C5, blank);			/* unblank the screen */
30996cdd0b9Skiyohara}
31096cdd0b9Skiyohara
31196cdd0b9Skiyoharavoid S3GENDAC_Save(ScrnInfoPtr pScrn)
31296cdd0b9Skiyohara{
31396cdd0b9Skiyohara	S3Ptr pS3 = S3PTR(pScrn);
31496cdd0b9Skiyohara	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
31596cdd0b9Skiyohara	S3RegPtr save = &pS3->SavedRegs;
31696cdd0b9Skiyohara	unsigned char tmp;
31796cdd0b9Skiyohara
31896cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
31996cdd0b9Skiyohara	tmp = inb(vgaCRReg);
32096cdd0b9Skiyohara	outb(vgaCRReg, tmp | 1);
32196cdd0b9Skiyohara
32296cdd0b9Skiyohara	save->dacregs[0] = inb(0x3c6);	/* Enhanced command register */
32396cdd0b9Skiyohara	save->dacregs[2] = inb(0x3c8);	/* PLL write index */
32496cdd0b9Skiyohara	save->dacregs[1] = inb(0x3c7);	/* PLL read index */
32596cdd0b9Skiyohara	outb(0x3c7, 2);		/* index to f2 reg */
32696cdd0b9Skiyohara	save->dacregs[3] = inb(0x3c9);	/* f2 PLL M divider */
32796cdd0b9Skiyohara	save->dacregs[4] = inb(0x3c9);	/* f2 PLL N1/N2 divider */
32896cdd0b9Skiyohara	outb(0x3c7, 0x0e);	/* index to PLL control */
32996cdd0b9Skiyohara	save->dacregs[5] = inb(0x3c9);	/* PLL control */
33096cdd0b9Skiyohara
33196cdd0b9Skiyohara	outb(vgaCRReg, tmp & ~1);
33296cdd0b9Skiyohara}
33396cdd0b9Skiyohara
33496cdd0b9Skiyoharavoid S3GENDAC_Restore(ScrnInfoPtr pScrn)
33596cdd0b9Skiyohara{
33696cdd0b9Skiyohara	S3Ptr pS3 = S3PTR(pScrn);
33796cdd0b9Skiyohara	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
33896cdd0b9Skiyohara	S3RegPtr restore = &pS3->SavedRegs;
33996cdd0b9Skiyohara	unsigned char tmp;
34096cdd0b9Skiyohara
34196cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
34296cdd0b9Skiyohara	tmp = inb(vgaCRReg);
34396cdd0b9Skiyohara	outb(vgaCRReg, tmp | 1);
34496cdd0b9Skiyohara
34596cdd0b9Skiyohara	outb(0x3c6, restore->dacregs[0]);	/* Enhanced command register */
34696cdd0b9Skiyohara	outb(0x3c8, 2);			/* index to f2 reg */
34796cdd0b9Skiyohara	outb(0x3c9, restore->dacregs[3]);	/* f2 PLL M divider */
34896cdd0b9Skiyohara	outb(0x3c9, restore->dacregs[4]);	/* f2 PLL N1/N2 divider */
34996cdd0b9Skiyohara	outb(0x3c8, 0x0e);		/* index to PLL control */
35096cdd0b9Skiyohara	outb(0x3c9, restore->dacregs[5]);	/* PLL control */
35196cdd0b9Skiyohara	outb(0x3c8, restore->dacregs[2]);	/* PLL write index */
35296cdd0b9Skiyohara	outb(0x3c7, restore->dacregs[1]);	/* PLL read index */
35396cdd0b9Skiyohara
35496cdd0b9Skiyohara	outb(vgaCRReg, tmp & ~1);
35596cdd0b9Skiyohara}
35696cdd0b9Skiyohara
35796cdd0b9Skiyoharastatic void
35896cdd0b9SkiyoharaS3GENDACSetClock(ScrnInfoPtr pScrn, long freq, int clk, int min_m, int min_n1,
35996cdd0b9Skiyohara	       int max_n1, int min_n2, int max_n2, int pll_type, long freq_min,
36096cdd0b9Skiyohara	       long freq_max)
36196cdd0b9Skiyohara{
36296cdd0b9Skiyohara	unsigned char m, n;
36396cdd0b9Skiyohara
36496cdd0b9Skiyohara	S3GENDACCalcClock(freq, min_m, min_n1, max_n1, min_n2, max_n2,
36596cdd0b9Skiyohara	    freq_min, freq_max, &m, &n);
36696cdd0b9Skiyohara
36796cdd0b9Skiyohara	/* XXX for pll_type == GENDAC */
36896cdd0b9Skiyohara	S3GENDACSetPLL(pScrn, clk, m, n);
36996cdd0b9Skiyohara}
37096cdd0b9Skiyohara
37196cdd0b9Skiyohara/* This function is copy from S3GENDACCalcClock() */
37296cdd0b9Skiyoharastatic void
37396cdd0b9SkiyoharaS3GENDACCalcClock(long freq, int min_m, int min_n1, int max_n1, int min_n2,
37496cdd0b9Skiyohara		  int max_n2, long freq_min, long freq_max,
37596cdd0b9Skiyohara		  unsigned char *mdiv, unsigned char *ndiv)
37696cdd0b9Skiyohara{
37796cdd0b9Skiyohara	double ffreq, ffreq_min, ffreq_max;
37896cdd0b9Skiyohara	double div, diff, best_diff;
37996cdd0b9Skiyohara	unsigned int m;
38096cdd0b9Skiyohara	unsigned char n1, n2, best_n1=18, best_n2=2, best_m=127;
38196cdd0b9Skiyohara
38296cdd0b9Skiyohara#define BASE_FREQ	14.31818
38396cdd0b9Skiyohara	ffreq = freq / 1000.0 / BASE_FREQ;
38496cdd0b9Skiyohara	ffreq_min = freq_min / 1000.0 / BASE_FREQ;
38596cdd0b9Skiyohara	ffreq_max = freq_max / 1000.0 / BASE_FREQ;
38696cdd0b9Skiyohara
38796cdd0b9Skiyohara	if (ffreq < ffreq_min / (1 << max_n2)) {
38896cdd0b9Skiyohara		ErrorF("invalid frequency %1.3f Mhz [freq >= %1.3f Mhz]\n",
38996cdd0b9Skiyohara		    ffreq*BASE_FREQ, ffreq_min*BASE_FREQ / (1 << max_n2));
39096cdd0b9Skiyohara		ffreq = ffreq_min / (1 << max_n2);
39196cdd0b9Skiyohara	}
39296cdd0b9Skiyohara	if (ffreq > ffreq_max / (1 << min_n2)) {
39396cdd0b9Skiyohara		ErrorF("invalid frequency %1.3f Mhz [freq <= %1.3f Mhz]\n",
39496cdd0b9Skiyohara		    ffreq*BASE_FREQ, ffreq_max*BASE_FREQ / (1 << min_n2));
39596cdd0b9Skiyohara		ffreq = ffreq_max / (1<<min_n2);
39696cdd0b9Skiyohara	}
39796cdd0b9Skiyohara
39896cdd0b9Skiyohara	/* work out suitable timings */
39996cdd0b9Skiyohara
40096cdd0b9Skiyohara	best_diff = ffreq;
40196cdd0b9Skiyohara
40296cdd0b9Skiyohara	for (n2 = min_n2; n2 <= max_n2; n2++) {
40396cdd0b9Skiyohara		for (n1 = min_n1 + 2; n1 <= max_n1 + 2; n1++) {
40496cdd0b9Skiyohara			m = (int)(ffreq * n1 * (1 << n2) + 0.5);
40596cdd0b9Skiyohara			if (m < min_m + 2 || m > 127 + 2)
40696cdd0b9Skiyohara				continue;
40796cdd0b9Skiyohara			div = (double)(m) / (double)(n1);
40896cdd0b9Skiyohara			if ((div >= ffreq_min) && (div <= ffreq_max)) {
40996cdd0b9Skiyohara				diff = ffreq - div / (1 << n2);
41096cdd0b9Skiyohara				if (diff < 0.0)
41196cdd0b9Skiyohara					diff = -diff;
41296cdd0b9Skiyohara				if (diff < best_diff) {
41396cdd0b9Skiyohara					best_diff = diff;
41496cdd0b9Skiyohara					best_m = m;
41596cdd0b9Skiyohara					best_n1 = n1;
41696cdd0b9Skiyohara					best_n2 = n2;
41796cdd0b9Skiyohara				}
41896cdd0b9Skiyohara			}
41996cdd0b9Skiyohara		}
42096cdd0b9Skiyohara	}
42196cdd0b9Skiyohara
42296cdd0b9Skiyohara	if (max_n1 == 63)
42396cdd0b9Skiyohara		*ndiv = (best_n1 - 2) | (best_n2 << 6);
42496cdd0b9Skiyohara	else
42596cdd0b9Skiyohara		*ndiv = (best_n1 - 2) | (best_n2 << 5);
42696cdd0b9Skiyohara	*mdiv = best_m - 2;
42796cdd0b9Skiyohara}
42896cdd0b9Skiyohara
42996cdd0b9Skiyoharastatic void
43096cdd0b9SkiyoharaS3GENDACSetPLL(ScrnInfoPtr pScrn, int clk, unsigned char m, unsigned char n)
43196cdd0b9Skiyohara{
43296cdd0b9Skiyohara	S3Ptr pS3 = S3PTR(pScrn);
43396cdd0b9Skiyohara	unsigned char tmp, tmp1;
43496cdd0b9Skiyohara	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
43596cdd0b9Skiyohara
43696cdd0b9Skiyohara	/* set RS2 via CR55, yuck */
43796cdd0b9Skiyohara	outb(vgaCRIndex, 0x55);
43896cdd0b9Skiyohara	tmp = inb(vgaCRReg) & 0xFC;
43996cdd0b9Skiyohara	outb(vgaCRReg, tmp | 0x01);
44096cdd0b9Skiyohara	tmp1 = inb(GENDAC_INDEX);
44196cdd0b9Skiyohara
44296cdd0b9Skiyohara	outb(GENDAC_INDEX, clk);
44396cdd0b9Skiyohara	outb(GENDAC_DATA, m);
44496cdd0b9Skiyohara	outb(GENDAC_DATA, n);
44596cdd0b9Skiyohara
44696cdd0b9Skiyohara	/* Now clean up our mess */
44796cdd0b9Skiyohara	outb(GENDAC_INDEX, tmp1);
44896cdd0b9Skiyohara	outb(vgaCRReg, tmp);
44996cdd0b9Skiyohara}
450