s3_Trio64DAC.c revision 340e3fbd
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/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/s3/s3_Trio64DAC.c,v 1.7tsi Exp $ */
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include "xf86.h"
34#include "xf86_OSproc.h"
35
36#include "compiler.h"
37
38#include "s3.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		if (clk != 10) {
238			outb(0x3c4, 0x12);
239			outb(0x3c5, n);
240			outb(0x3c4, 0x13);
241			outb(0x3c5, m);
242
243			outb(0x3c4, 0x15);
244			tmp = inb(0x3c5) & ~0x21;
245			outb(0x3c5, tmp | 0x02);
246			outb(0x3c5, tmp | 0x22);
247			outb(0x3c5, tmp | 0x02);
248		} else {
249			outb(0x3c4, 0x10);
250			outb(0x3c5, n);
251			outb(0x3c4, 0x11);
252			outb(0x3c5, m);
253			outb(0x3c4, 0x1a);
254			outb(0x3c5, n);
255
256			outb(0x3c4, 0x15);
257			tmp = inb(0x3c5) & ~0x21;
258			outb(0x3c5, tmp | 0x01);
259			outb(0x3c5, tmp | 0x21);
260			outb(0x3c5, tmp | 0x01);
261			outb(0x3c5, tmp);
262		}
263
264		outb(0x3c4, 0x08);
265		outb(0x3c5, 0x00);	/* lock em */
266	}
267}
268
269
270static void S3TrioSetClock(ScrnInfoPtr pScrn, long freq, int clk, int min_m,
271		    int min_n1, int max_n1, int min_n2, int max_n2,
272		    int pll_type, long freq_min, long freq_max)
273{
274	unsigned char m, n;
275
276	S3TrioCalcClock(freq, min_m, min_n1, max_n1, min_n2, max_n2,
277			freq_min, freq_max, &m, &n);
278
279	/* XXX for pll_type == TRIO */
280	S3TrioSetPLL(pScrn, clk, m, n);
281}
282
283void S3Trio64DAC_PreInit(ScrnInfoPtr pScrn)
284{
285	S3Ptr pS3 = S3PTR(pScrn);
286	unsigned char SR8, SR27;
287	int m, n, n1, n2, mclk;
288
289	outb(0x3c4, 0x08);
290	SR8 = inb(0x3c5);
291	outb(0x3c5, 0x06);
292
293	outb(0x3c4, 0x11);
294	m = inb(0x3c5);
295	outb(0x3c4, 0x10);
296	n = inb(0x3c5);
297
298	m &= 0x7f;
299	n1 = n & 0x1f;
300	n2 = (n >> 5) & 0x03;
301	mclk = ((1431818 * (m+2)) / (n1+2) / (1<<n2)+50)/100;
302	if (pS3->Chipset == PCI_CHIP_AURORA64VP) {
303		outb(0x3c4, 0x27);
304		SR27 = inb(0x3c5);
305		outb(0x3c4, 0x28);
306		(void) inb(0x3c5);
307		mclk /= ((SR27 >> 2) & 0x03) + 1;
308	}
309	pS3->mclk = mclk;
310
311	outb(0x3c4, 0x08);
312	outb(0x3c5, SR8);
313
314	xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f Mhz\n",
315		   mclk / 1000.0);
316}
317
318
319void S3Trio64DAC_Init(ScrnInfoPtr pScrn, DisplayModePtr mode)
320{
321	S3Ptr pS3 = S3PTR(pScrn);
322	int pixmux=0, invert_vclk=0, sr8, sr15, sr18, cr33;
323	unsigned char blank, tmp;
324
325	if (pS3->Chipset == PCI_CHIP_AURORA64VP)
326		S3TrioSetClock(pScrn, mode->Clock, 2, 1, 1, 63, 0, 3, 2,
327			       135000, 270000);
328	else if (pS3->Chipset == PCI_CHIP_TRIO64V2_DXGX)
329		S3TrioSetClock(pScrn, mode->Clock, 2, 1, 1, 31, 0, 3, 2,
330			       170000, 270000);
331	else
332		S3TrioSetClock(pScrn, mode->Clock, 2, 1, 1, 31, 0, 3, 2,
333			       135000, 270000);
334
335
336	outb(0x3c4, 1);
337	blank = inb(0x3c5);
338	outb(0x3c5, blank | 0x20);	/* blank the screen */
339
340	outb(0x3c4, 0x08);
341	sr8 = inb(0x3c5);
342	outb(0x3c5, 0x06);
343
344	outb(0x3c4, 0x0d0);
345	tmp = inb(0x3c5) & ~1;
346	outb(0x3c5, tmp);
347
348	outb(0x3c4, 0x15);
349	sr15 = inb(0x3c5) & ~0x10;
350
351	outb(0x3c4, 0x18);
352	sr18 = inb(0x3c5) & ~0x80;
353	outb(pS3->vgaCRIndex, 0x33);
354	cr33 = inb(pS3->vgaCRReg) & ~0x28;
355
356	if (pS3->Chipset == PCI_CHIP_TRIO64V2_DXGX)
357	{
358	  cr33 |= 0x20;
359	}
360
361	/* ! pixmux */
362	switch (pScrn->depth) {
363	case 8:
364		break;
365	case 15:
366		cr33 |= 0x08;
367		pixmux = 0x30;
368		break;
369	case 16:
370		cr33 |= 0x08;
371		pixmux = 0x50;
372		break;
373	case 32:
374		pixmux = 0xd0;
375		break;
376	}
377
378	outb(pS3->vgaCRReg, cr33);
379
380	outb(pS3->vgaCRIndex, 0x67);
381	outb(pS3->vgaCRReg, pixmux | invert_vclk);
382
383	outb(0x3c4, 0x15);
384	outb(0x3c5, sr15);
385	outb(0x3c4, 0x18);
386	outb(0x3c5, sr18);
387
388	if (pS3->Chipset == PCI_CHIP_AURORA64VP) {
389		outb(0x3c4, 0x28);
390		outb(0x3c5, 0x00);
391	}
392
393	outb(0x3c4, 0x08);
394	outb(0x3c5, sr8);
395
396	outb(0x3c4, 1);
397	outb(0x3c5, blank);	/* unblank the screen */
398}
399