1/*
2 *      Copyright 2001  Ani Joshi <ajoshi@unixbox.com>
3 *
4 *      XFree86 4.x driver for S3 chipsets
5 *
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that copyright
10 * notice and this permission notice appear in supporting documentation and
11 * that the name of Ani Joshi not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission.  Ani Joshi makes no representations
14 * about the suitability of this software for any purpose.  It is provided
15 * "as-is" without express or implied warranty.
16 *
17 * ANI JOSHI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19 * EVENT SHALL ANI JOSHI BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23 * PERFORMANCE OF THIS SOFTWARE.
24 *
25 *
26 */
27
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#include "xf86.h"
33#include "xf86_OSproc.h"
34
35#include "compiler.h"
36
37#include "s3.h"
38#include "s3_reg.h"
39
40/* this is really quite dumb */
41Bool S3Trio64DACProbe(ScrnInfoPtr pScrn)
42{
43	S3Ptr pS3 = S3PTR(pScrn);
44
45	if (!S3_TRIO_SERIES())
46		return FALSE;
47
48	RamDacInit(pScrn, pS3->RamDacRec);
49
50	pS3->RamDac = RamDacHelperCreateInfoRec();
51	pS3->RamDac->RamDacType = TRIO64_RAMDAC;
52
53	return TRUE;
54}
55
56
57void S3Trio64DAC_Save(ScrnInfoPtr pScrn)
58{
59	S3Ptr pS3 = S3PTR(pScrn);
60	S3RegPtr save = &pS3->SavedRegs;
61
62	save->dacregs[0] = inb(0x3cc);
63
64	outb(0x3c4, 0x08);
65	save->dacregs[1] = inb(0x3c5);
66	outb(0x3c5, 0x06);
67
68	outb(0x3c4, 0x09);
69	save->dacregs[2] = inb(0x3c5);
70	outb(0x3c4, 0x0a);
71	save->dacregs[3] = inb(0x3c5);
72	outb(0x3c4, 0x0b);
73	save->dacregs[4] = inb(0x3c5);
74	outb(0x3c4, 0x0d);
75	save->dacregs[5] = inb(0x3c5);
76	outb(0x3c4, 0x15);
77	save->dacregs[6] = inb(0x3c5) & 0xfe;
78	outb(0x3c5, save->dacregs[6]);
79
80	outb(0x3c4, 0x18);
81	save->dacregs[7] = inb(0x3c5);
82	outb(0x3c4, 0x10);
83	save->dacregs[8] = inb(0x3c5);
84	outb(0x3c4, 0x11);
85	save->dacregs[9] = inb(0x3c5);
86	outb(0x3c4, 0x12);
87	save->dacregs[10] = inb(0x3c5);
88	outb(0x3c4, 0x13);
89	save->dacregs[11] = inb(0x3c5);
90	outb(0x3c4, 0x1a);
91	save->dacregs[12] = inb(0x3c5);
92	outb(0x3c4, 0x1b);
93	save->dacregs[13] = inb(0x3c5);
94
95	if (pS3->Chipset == PCI_CHIP_AURORA64VP) {
96		int i;
97
98		for (i=0x1a; i <= 0x6f; i++) {
99			outb(0x3c4, i);
100			save->dacregs[i] = inb(0x3c5);
101		}
102	}
103
104	outb(0x3c4, 0x08);
105	outb(0x3c5, 0x00);
106}
107
108
109void S3Trio64DAC_Restore(ScrnInfoPtr pScrn)
110{
111	S3Ptr pS3 = S3PTR(pScrn);
112	S3RegPtr restore = &pS3->SavedRegs;
113	unsigned char tmp;
114
115	outb(0x3c2, restore->dacregs[0]);
116	outb(0x3c4, 0x08);
117	outb(0x3c5, 0x06);
118
119	outb(0x3c4, 0x09);
120	outb(0x3c5, restore->dacregs[2]);
121	outb(0x3c4, 0x0a);
122	outb(0x3c5, restore->dacregs[3]);
123	outb(0x3c4, 0x0b);
124	outb(0x3c5, restore->dacregs[4]);
125	outb(0x3c4, 0x0d);
126	outb(0x3c5, restore->dacregs[5]);
127
128	outb(0x3c4, 0x10);
129	outb(0x3c5, restore->dacregs[8]);
130	outb(0x3c4, 0x11);
131	outb(0x3c5, restore->dacregs[9]);
132	outb(0x3c4, 0x12);
133	outb(0x3c5, restore->dacregs[10]);
134	outb(0x3c4, 0x13);
135	outb(0x3c5, restore->dacregs[11]);
136	outb(0x3c4, 0x1a);
137	outb(0x3c5, restore->dacregs[12]);
138	outb(0x3c4, 0x1b);
139	outb(0x3c5, restore->dacregs[13]);
140	outb(0x3c4, 0x15);
141	tmp = inb(0x3c5);
142	outb(0x3c4, tmp & ~0x20);
143	outb(0x3c4, tmp | 0x20);
144	outb(0x3c4, tmp & ~0x20);
145
146	outb(0x3c4, 0x15);
147	outb(0x3c5, restore->dacregs[6]);
148	outb(0x3c4, 0x18);
149	outb(0x3c5, restore->dacregs[7]);
150
151	if (pS3->Chipset == PCI_CHIP_AURORA64VP) {
152		int i;
153
154		for (i = 0x1a; i <= 0x6f; i++) {
155			outb(0x3c4, i);
156			outb(0x3c5, restore->dacregs[i]);
157		}
158	}
159
160	outb(0x3c4, 0x08);
161	outb(0x3c5, restore->dacregs[1]);
162}
163
164
165static void
166S3TrioCalcClock(long freq, int min_m, int min_n1, int max_n1, int min_n2,
167		int max_n2, long freq_min, long freq_max,
168		unsigned char *mdiv, unsigned char *ndiv)
169{
170	double ffreq, ffreq_min, ffreq_max;
171	double div, diff, best_diff;
172	unsigned int m;
173	unsigned char n1, n2, best_n1=18, best_n2=2, best_m=127;
174
175#define BASE_FREQ	14.31818
176	ffreq = freq / 1000.0 / BASE_FREQ;
177	ffreq_min = freq_min / 1000.0 / BASE_FREQ;
178	ffreq_max = freq_max / 1000.0 / BASE_FREQ;
179
180	if (ffreq < ffreq_min / (1<<max_n2)) {
181		ErrorF("invalid frequency %1.3f Mhz [freq >= %1.3f Mhz]\n",
182			ffreq*BASE_FREQ, ffreq_min*BASE_FREQ/(1<<max_n2));
183		ffreq = ffreq_min / (1<<max_n2);
184	}
185	if (ffreq > ffreq_max / (1<<min_n2)) {
186		ErrorF("invalid frequency %1.3f Mhz [freq <= %1.3f Mhz]\n",
187			ffreq*BASE_FREQ, ffreq_max*BASE_FREQ/(1<<min_n2));
188		ffreq = ffreq_max / (1<<min_n2);
189	}
190
191	best_diff = ffreq;
192
193	for(n2=min_n2; n2<=max_n2; n2++) {
194		for(n1=min_n1+2; n1<=max_n1+2; n1++) {
195			m = (int)(ffreq*n1*(1<<n2)+0.5);
196			if (m<min_m+2 || m > 127+2)
197				continue;
198			div = (double)(m)/(double)(n1);
199			if ((div >= ffreq_min) &&
200			    (div <= ffreq_max)) {
201				diff = ffreq - div / (1<<n2);
202				if (diff < 0.0)
203					diff = -diff;
204				if (diff < best_diff) {
205					best_diff = diff;
206					best_m = m;
207					best_n1 = n1;
208					best_n2 = n2;
209				}
210			}
211		}
212	}
213
214	if (max_n1 == 63)
215		*ndiv = (best_n1 - 2) | (best_n2 << 6);
216	else
217		*ndiv = (best_n1 - 2) | (best_n2 << 5);
218	*mdiv = best_m - 2;
219}
220
221
222static void S3TrioSetPLL(ScrnInfoPtr pScrn, int clk, unsigned char m,
223		  unsigned char n)
224{
225	unsigned char tmp;
226
227	if (clk < 2) {
228		tmp = inb(0x3cc);
229		outb(0x3c2, (tmp & 0xf3) | (clk << 2));
230	} else {
231		tmp = inb(0x3cc);
232		outb(0x3c2, tmp | 0x0c);
233
234		outb(0x3c4, 0x08);
235		outb(0x3c5, 0x06);	/* unlock extended CR9-18 */
236
237		outb(0x3c4, 0x12);      /* write N1 and N2 to DCLK PLL */
238		outb(0x3c5, n);
239		outb(0x3c4, 0x13);      /* write M to DCLK PLL */
240		outb(0x3c5, m);
241
242#if 0
243/* this code was in previous driver version but it was never called.
244   So I decide to comment it. */
245		outb(0x3c4, 0x10);
246		outb(0x3c5, n);
247		outb(0x3c4, 0x11);
248		outb(0x3c5, m); */
249
250		outb(0x3c4, 0x1a);
251		outb(0x3c5, n);
252#endif
253		/* Toggle cr15_5 by sequence 0->1->0 to immediately apply
254		   new PLL parameters */
255		outb(0x3c4, 0x15);
256		tmp = inb(0x3c5) & ~0x20;
257		outb(0x3c5, tmp);
258		outb(0x3c5, tmp | 0x20);
259		outb(0x3c5, tmp);
260
261		outb(0x3c4, 0x08);
262		outb(0x3c5, 0x00);	/* lock em */
263	}
264}
265
266
267static void S3TrioSetClock(ScrnInfoPtr pScrn, long freq, int clk, int min_m,
268		    int min_n1, int max_n1, int min_n2, int max_n2,
269		    int pll_type, long freq_min, long freq_max)
270{
271	unsigned char m, n;
272
273	S3TrioCalcClock(freq, min_m, min_n1, max_n1, min_n2, max_n2,
274			freq_min, freq_max, &m, &n);
275
276	/* XXX for pll_type == TRIO */
277	S3TrioSetPLL(pScrn, clk, m, n);
278}
279
280void S3Trio64DAC_PreInit(ScrnInfoPtr pScrn)
281{
282	S3Ptr pS3 = S3PTR(pScrn);
283	unsigned char SR8, SR27;
284	int m, n, n1, n2, mclk;
285
286	outb(0x3c4, 0x08);
287	SR8 = inb(0x3c5);
288	outb(0x3c5, 0x06);
289
290	outb(0x3c4, 0x11);
291	m = inb(0x3c5);
292	outb(0x3c4, 0x10);
293	n = inb(0x3c5);
294
295	m &= 0x7f;
296	n1 = n & 0x1f;
297	n2 = (n >> 5) & 0x03;
298	mclk = ((1431818 * (m+2)) / (n1+2) / (1<<n2)+50)/100;
299	if (pS3->Chipset == PCI_CHIP_AURORA64VP) {
300		outb(0x3c4, 0x27);
301		SR27 = inb(0x3c5);
302		outb(0x3c4, 0x28);
303		(void) inb(0x3c5);
304		mclk /= ((SR27 >> 2) & 0x03) + 1;
305	}
306	pS3->mclk = mclk;
307
308	outb(0x3c4, 0x08);
309	outb(0x3c5, SR8);
310
311	xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f Mhz\n",
312		   mclk / 1000.0);
313}
314
315
316void S3Trio64DAC_Init(ScrnInfoPtr pScrn, DisplayModePtr mode)
317{
318	S3Ptr pS3 = S3PTR(pScrn);
319	int pixmux=0, invert_vclk=0, sr8, sr15, sr18, cr33;
320	unsigned char blank, tmp;
321
322	if (pS3->Chipset == PCI_CHIP_AURORA64VP)
323		S3TrioSetClock(pScrn, mode->Clock, 2, 1, 1, 63, 0, 3, 2,
324			       135000, 270000);
325	else if (pS3->Chipset == PCI_CHIP_TRIO64V2_DXGX)
326		S3TrioSetClock(pScrn, mode->Clock, 2, 1, 1, 31, 0, 3, 2,
327			       170000, 340000);
328	else
329		S3TrioSetClock(pScrn, mode->Clock, 2, 1, 1, 31, 0, 3, 2,
330			       135000, 270000);
331
332	outb(0x3c4, 0x01);
333	blank = inb(0x3c5);
334	outb(0x3c5, blank | 0x20);	/* blank the screen */
335
336	outb(0x3c4, 0x08);
337	sr8 = inb(0x3c5);
338	outb(0x3c5, 0x06);       /* unlock extended sequenser register */
339
340	outb(0x3c4, 0x0d);
341	tmp = inb(0x3c5) & ~0x01;
342	outb(0x3c5, tmp);       /* VCLK, HSYNC, VSYNC are outputs */
343
344	outb(0x3c4, 0x15);
345	sr15 = inb(0x3c5) & ~0x10;
346
347	outb(0x3c4, 0x18);
348	sr18 = inb(0x3c5) & ~0x80;
349	outb(pS3->vgaCRIndex, 0x33);
350	cr33 = inb(pS3->vgaCRReg) & ~0x28;
351
352	/* ! pixmux */
353	switch (pScrn->depth) {
354	case 8:
355		break;
356	case 15:
357		cr33 |= 0x08;
358		pixmux = 0x30;
359		break;
360	case 16:
361		cr33 |= 0x08;
362		pixmux = 0x50;
363		break;
364	case 24:
365	case 32:
366		pixmux = 0xd0;
367		break;
368	}
369
370	outb(pS3->vgaCRReg, cr33);
371
372	outb(pS3->vgaCRIndex, 0x67);
373	WaitVSync();
374	outb(pS3->vgaCRReg, pixmux | invert_vclk);
375
376	outb(0x3c4, 0x15);
377	outb(0x3c5, sr15);
378	outb(0x3c4, 0x18);
379	outb(0x3c5, sr18);
380
381	if (pS3->Chipset == PCI_CHIP_AURORA64VP) {
382		outb(0x3c4, 0x28);
383		outb(0x3c5, 0x00);
384	}
385
386	outb(0x3c4, 0x08);
387	outb(0x3c5, sr8);
388
389	outb(0x3c4, 0x01);
390	outb(0x3c5, blank);	/* unblank the screen */
391}
392