s3_GENDAC.c revision 96cdd0b9
1/*
2 * Copyright (c) 2009 KIYOHARA Takashi
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif
30
31#include "xf86.h"
32#include "xf86_OSproc.h"
33
34#include "compiler.h"
35
36#include "s3.h"
37
38#define GENDAC_INDEX		0x3C8
39#define GENDAC_DATA		0x3C9
40
41
42static void S3GENDACSetClock(ScrnInfoPtr, long, int, int, int, int, int, int,
43			     int, long, long);
44static void S3GENDACCalcClock(long, int, int, int, int, int, long, long,
45			      unsigned char *, unsigned char *);
46static void S3GENDACSetPLL(ScrnInfoPtr, int, unsigned char, unsigned char);
47
48
49static void xf86dactopel(void);
50
51static void
52xf86dactopel()
53{
54	outb(0x3C8,0);
55	return;
56}
57
58
59Bool S3GENDACProbe(ScrnInfoPtr pScrn)
60{
61	/* probe for S3 GENDAC/SDAC */
62	/*
63	 * S3 GENDAC and SDAC have two fixed read only PLL clocks
64	 *     CLK0 f0: 25.255MHz   M-byte 0x28  N-byte 0x61
65	 *     CLK0 f1: 28.311MHz   M-byte 0x3d  N-byte 0x62
66	 * which can be used to detect GENDAC and SDAC since there is no chip-id
67	 * for the GENDAC.
68	 *
69	 * NOTE: for the GENDAC on a MIRO 10SD (805+GENDAC) reading PLL values
70	 * for CLK0 f0 and f1 always returns 0x7f
71	 * (but is documented "read only")
72	 */
73
74	S3Ptr pS3 = S3PTR(pScrn);
75	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
76	unsigned char saveCR55, saveCR45, saveCR43, savelut[6];
77	unsigned int i;		/* don't use signed int, UW2.0 compiler bug */
78	long clock01, clock23;
79	int found = 0;
80
81	if (!S3_864_SERIES())	/* need? */
82		return FALSE;
83
84	outb(vgaCRIndex, 0x43);
85	saveCR43 = inb(vgaCRReg);
86	outb(vgaCRReg, saveCR43 & ~0x02);
87
88	outb(vgaCRIndex, 0x45);
89	saveCR45 = inb(vgaCRReg);
90	outb(vgaCRReg, saveCR45 & ~0x20);
91
92	outb(vgaCRIndex, 0x55);
93	saveCR55 = inb(vgaCRReg);
94	outb(vgaCRReg, saveCR55 & ~1);
95
96	outb(0x3c7,0);
97	for(i = 0; i < 2 * 3; i++)	/* save first two LUT entries */
98		savelut[i] = inb(0x3c9);
99	outb(0x3c8,0);
100	for(i = 0; i < 2 * 3; i++)	/* set first two LUT entries to zero */
101		outb(0x3c9, 0);
102
103	outb(vgaCRIndex, 0x55);
104	outb(vgaCRReg, saveCR55 | 1);
105
106	outb(0x3c7,0);
107	for(i = clock01 = 0; i < 4; i++)
108		clock01 = (clock01 << 8) | (inb(0x3c9) & 0xff);
109	for(i = clock23 = 0; i < 4; i++)
110		clock23 = (clock23 << 8) | (inb(0x3c9) & 0xff);
111
112	outb(vgaCRIndex, 0x55);
113	outb(vgaCRReg, saveCR55 & ~1);
114
115	outb(0x3c8,0);
116	for(i = 0; i < 2 * 3; i++)	/* restore first two LUT entries */
117		outb(0x3c9, savelut[i]);
118
119	outb(vgaCRIndex, 0x55);
120	outb(vgaCRReg, saveCR55);
121
122	if (clock01 == 0x28613d62 ||
123	    (clock01 == 0x7f7f7f7f && clock23 != 0x7f7f7f7f)) {
124		xf86dactopel();
125		inb(0x3c6);
126		inb(0x3c6);
127		inb(0x3c6);
128
129		/* the fourth read will show the SDAC chip ID and revision */
130		if (((i = inb(0x3c6)) & 0xf0) == 0x70)
131			found = SDAC_RAMDAC;
132		else
133			found = GENDAC_RAMDAC;
134		saveCR43 &= ~0x02;
135		saveCR45 &= ~0x20;
136		xf86dactopel();
137	}
138
139	outb(vgaCRIndex, 0x45);
140	outb(vgaCRReg, saveCR45);
141
142	outb(vgaCRIndex, 0x43);
143	outb(vgaCRReg, saveCR43);
144
145	if (found) {
146		RamDacInit(pScrn, pS3->RamDacRec);
147		pS3->RamDac = RamDacHelperCreateInfoRec();
148		pS3->RamDac->RamDacType = found;
149		return TRUE;
150	}
151	return FALSE;
152}
153
154void S3GENDAC_PreInit(ScrnInfoPtr pScrn)
155{
156	S3Ptr pS3 = S3PTR(pScrn);
157	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
158	unsigned char saveCR55;
159	int m, n, n1, n2, mclk;
160
161	outb(vgaCRIndex, 0x55);
162	saveCR55 = inb(vgaCRReg);
163	outb(vgaCRReg, saveCR55 | 1);
164
165	outb(0x3C7, 10); /* read MCLK */
166	m = inb(0x3C9);
167	n = inb(0x3C9);
168
169	outb(vgaCRIndex, 0x55);
170	outb(vgaCRReg, saveCR55);
171
172	m &= 0x7f;
173	n1 = n & 0x1f;
174	n2 = (n >> 5) & 0x03;
175	mclk = ((1431818 * (m + 2)) / (n1 + 2) / (1 << n2) + 50) / 100;
176
177	pS3->mclk = mclk;
178	xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f MHz\n",
179	    mclk / 1000.0);
180}
181
182void S3GENDAC_Init(ScrnInfoPtr pScrn, DisplayModePtr mode)
183{
184	S3Ptr pS3 = S3PTR(pScrn);
185	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
186	int daccomm = 0;	/* GENDAC command */
187	unsigned char blank, tmp;
188
189	S3GENDACSetClock(pScrn, mode->Clock * (pScrn->depth >> 3), 2, 1, 1, 31,
190	    0, 3, 1, 100000, 250000);
191
192	outb(0x3C4, 1);
193	blank = inb(0x3C5);
194	outb(0x3C5, blank | 0x20); /* blank the screen */
195
196	switch (pScrn->depth)
197	{
198	case 8:		/* 8-bit color, 1VCLK/pixel */
199		break;
200
201	case 15:	/* 15-bit color, 2VCLK/pixel */
202		daccomm = 0x20;
203		break;
204
205	case 16:	/* 16-bit color, 2VCLK/pixel */
206		daccomm = 0x60;
207		break;
208
209	case 32:	/* 32-bit color, 3VCLK/pixel */
210		daccomm = 0x40;
211	}
212
213	outb(vgaCRIndex, 0x55);
214	tmp = inb(vgaCRReg) | 1;
215	outb(vgaCRReg, tmp);
216
217	outb(0x3c6, daccomm);		/* set GENDAC mux mode */
218
219	outb(vgaCRIndex, 0x55);
220	tmp = inb(vgaCRReg) & ~1;
221	outb(vgaCRReg, tmp);
222
223	outb(0x3C4, 1);
224	outb(0x3C5, blank);		/* unblank the screen */
225}
226
227void S3SDAC_Init(ScrnInfoPtr pScrn, DisplayModePtr mode)
228{
229	S3Ptr pS3 = S3PTR(pScrn);
230	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
231	int pixmux = 0;		/* SDAC command and CR67 */
232	int blank_delay = 0;	/* CR6D */
233	int invert_vclk = 0;	/* CR66 bit 0 */
234	unsigned char blank, tmp;
235
236#if 0
237	S3GENDACSetClock(pScrn,
238	    (pScrn->depth == 32) ? mode->Clock * 2 : mode->Clock,
239	    2, 1, 1, 31, 0, 3, 1, 100000, 250000);
240#else
241	/* XXXX: for prep */
242	long freq;
243
244	switch (pScrn->depth) {
245	case 32:
246		freq = mode->Clock * 2;	/* XXXX: frem xfree86 3.x */
247		break;
248	case 16:
249		freq = mode->Clock / 2;
250		break;
251	default:
252		freq = mode->Clock;
253		break;
254	}
255	S3GENDACSetClock(pScrn, freq,
256	    2, 1, 1, 31, 0, 3, 1, 100000, 250000);
257#endif
258
259	outb(vgaCRIndex, 0x42);/* select the clock */
260	tmp = inb(vgaCRReg) & 0xf0;
261	outb(vgaCRReg, tmp | 0x02);
262	usleep(150000);
263
264	outb(0x3C4, 1);
265	blank = inb(0x3C5);
266	outb(0x3C5, blank | 0x20); /* blank the screen */
267
268	switch (pScrn->depth)
269	{
270	case 8:		/* 8-bit color, 1 VCLK/pixel */
271		pixmux = 0x10;
272		blank_delay = 2;
273		invert_vclk = 1;
274		break;
275
276	case 15:	/* 15-bit color, 1VCLK/pixel */
277		pixmux = 0x30;
278		blank_delay = 2;
279		break;
280
281	case 16:	/* 16-bit color, 1VCLK/pixel */
282		pixmux = 0x50;
283		blank_delay = 2;
284		break;
285
286	case 32:	/* 32-bit color, 2VCLK/pixel */
287		pixmux = 0x70;
288		blank_delay = 2;
289	}
290
291	outb(vgaCRIndex, 0x55);
292	tmp = inb(vgaCRReg) | 1;
293	outb(vgaCRReg, tmp);
294
295	outb(vgaCRIndex, 0x67);
296	outb(vgaCRReg, pixmux | invert_vclk);	/* set S3 mux mode */
297	outb(0x3c6, pixmux);			/* set SDAC mux mode */
298
299	outb(vgaCRIndex, 0x6D);
300	outb(vgaCRReg, blank_delay);		/* set blank delay */
301
302	outb(vgaCRIndex, 0x55);
303	tmp = inb(vgaCRReg) & ~1;
304	outb(vgaCRReg, tmp);
305
306	outb(0x3C4, 1);
307	outb(0x3C5, blank);			/* unblank the screen */
308}
309
310void S3GENDAC_Save(ScrnInfoPtr pScrn)
311{
312	S3Ptr pS3 = S3PTR(pScrn);
313	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
314	S3RegPtr save = &pS3->SavedRegs;
315	unsigned char tmp;
316
317	outb(vgaCRIndex, 0x55);
318	tmp = inb(vgaCRReg);
319	outb(vgaCRReg, tmp | 1);
320
321	save->dacregs[0] = inb(0x3c6);	/* Enhanced command register */
322	save->dacregs[2] = inb(0x3c8);	/* PLL write index */
323	save->dacregs[1] = inb(0x3c7);	/* PLL read index */
324	outb(0x3c7, 2);		/* index to f2 reg */
325	save->dacregs[3] = inb(0x3c9);	/* f2 PLL M divider */
326	save->dacregs[4] = inb(0x3c9);	/* f2 PLL N1/N2 divider */
327	outb(0x3c7, 0x0e);	/* index to PLL control */
328	save->dacregs[5] = inb(0x3c9);	/* PLL control */
329
330	outb(vgaCRReg, tmp & ~1);
331}
332
333void S3GENDAC_Restore(ScrnInfoPtr pScrn)
334{
335	S3Ptr pS3 = S3PTR(pScrn);
336	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
337	S3RegPtr restore = &pS3->SavedRegs;
338	unsigned char tmp;
339
340	outb(vgaCRIndex, 0x55);
341	tmp = inb(vgaCRReg);
342	outb(vgaCRReg, tmp | 1);
343
344	outb(0x3c6, restore->dacregs[0]);	/* Enhanced command register */
345	outb(0x3c8, 2);			/* index to f2 reg */
346	outb(0x3c9, restore->dacregs[3]);	/* f2 PLL M divider */
347	outb(0x3c9, restore->dacregs[4]);	/* f2 PLL N1/N2 divider */
348	outb(0x3c8, 0x0e);		/* index to PLL control */
349	outb(0x3c9, restore->dacregs[5]);	/* PLL control */
350	outb(0x3c8, restore->dacregs[2]);	/* PLL write index */
351	outb(0x3c7, restore->dacregs[1]);	/* PLL read index */
352
353	outb(vgaCRReg, tmp & ~1);
354}
355
356static void
357S3GENDACSetClock(ScrnInfoPtr pScrn, long freq, int clk, int min_m, int min_n1,
358	       int max_n1, int min_n2, int max_n2, int pll_type, long freq_min,
359	       long freq_max)
360{
361	unsigned char m, n;
362
363	S3GENDACCalcClock(freq, min_m, min_n1, max_n1, min_n2, max_n2,
364	    freq_min, freq_max, &m, &n);
365
366	/* XXX for pll_type == GENDAC */
367	S3GENDACSetPLL(pScrn, clk, m, n);
368}
369
370/* This function is copy from S3GENDACCalcClock() */
371static void
372S3GENDACCalcClock(long freq, int min_m, int min_n1, int max_n1, int min_n2,
373		  int max_n2, long freq_min, long freq_max,
374		  unsigned char *mdiv, unsigned char *ndiv)
375{
376	double ffreq, ffreq_min, ffreq_max;
377	double div, diff, best_diff;
378	unsigned int m;
379	unsigned char n1, n2, best_n1=18, best_n2=2, best_m=127;
380
381#define BASE_FREQ	14.31818
382	ffreq = freq / 1000.0 / BASE_FREQ;
383	ffreq_min = freq_min / 1000.0 / BASE_FREQ;
384	ffreq_max = freq_max / 1000.0 / BASE_FREQ;
385
386	if (ffreq < ffreq_min / (1 << max_n2)) {
387		ErrorF("invalid frequency %1.3f Mhz [freq >= %1.3f Mhz]\n",
388		    ffreq*BASE_FREQ, ffreq_min*BASE_FREQ / (1 << max_n2));
389		ffreq = ffreq_min / (1 << max_n2);
390	}
391	if (ffreq > ffreq_max / (1 << min_n2)) {
392		ErrorF("invalid frequency %1.3f Mhz [freq <= %1.3f Mhz]\n",
393		    ffreq*BASE_FREQ, ffreq_max*BASE_FREQ / (1 << min_n2));
394		ffreq = ffreq_max / (1<<min_n2);
395	}
396
397	/* work out suitable timings */
398
399	best_diff = ffreq;
400
401	for (n2 = min_n2; n2 <= max_n2; n2++) {
402		for (n1 = min_n1 + 2; n1 <= max_n1 + 2; n1++) {
403			m = (int)(ffreq * n1 * (1 << n2) + 0.5);
404			if (m < min_m + 2 || m > 127 + 2)
405				continue;
406			div = (double)(m) / (double)(n1);
407			if ((div >= ffreq_min) && (div <= ffreq_max)) {
408				diff = ffreq - div / (1 << n2);
409				if (diff < 0.0)
410					diff = -diff;
411				if (diff < best_diff) {
412					best_diff = diff;
413					best_m = m;
414					best_n1 = n1;
415					best_n2 = n2;
416				}
417			}
418		}
419	}
420
421	if (max_n1 == 63)
422		*ndiv = (best_n1 - 2) | (best_n2 << 6);
423	else
424		*ndiv = (best_n1 - 2) | (best_n2 << 5);
425	*mdiv = best_m - 2;
426}
427
428static void
429S3GENDACSetPLL(ScrnInfoPtr pScrn, int clk, unsigned char m, unsigned char n)
430{
431	S3Ptr pS3 = S3PTR(pScrn);
432	unsigned char tmp, tmp1;
433	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
434
435	/* set RS2 via CR55, yuck */
436	outb(vgaCRIndex, 0x55);
437	tmp = inb(vgaCRReg) & 0xFC;
438	outb(vgaCRReg, tmp | 0x01);
439	tmp1 = inb(GENDAC_INDEX);
440
441	outb(GENDAC_INDEX, clk);
442	outb(GENDAC_DATA, m);
443	outb(GENDAC_DATA, n);
444
445	/* Now clean up our mess */
446	outb(GENDAC_INDEX, tmp1);
447	outb(vgaCRReg, tmp);
448}
449