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 <unistd.h>
33
34#include "xf86.h"
35#include "xf86_OSproc.h"
36#include <X11/Xos.h>
37
38#include "compiler.h"
39
40#include "TI.h"
41
42#include "s3.h"
43
44
45#define TI_WRITE_ADDR           0x3C8   /* CR55 low bit == 0 */
46#define TI_RAMDAC_DATA          0x3C9   /* CR55 low bit == 0 */
47#define TI_PIXEL_MASK           0x3C6   /* CR55 low bit == 0 */
48#define TI_READ_ADDR            0x3C7   /* CR55 low bit == 0 */
49#define TI_INDEX_REG            0x3C6   /* CR55 low bit == 1 */
50#define TI_DATA_REG             0x3C7   /* CR55 low bit == 1 */
51
52#define	TIDAC_output_clock_select	0x1b
53#define	TIDAC_auxiliary_ctrl		0x29
54#define	TIDAC_general_io_ctrl		0x2a
55#define TIDAC_general_io_data		0x2b
56#define TIDAC_cursor_color_0_red	0x23
57#define TIDAC_cursor_color_0_green	0x24
58#define TIDAC_cursor_color_0_blue	0x25
59#define TIDAC_cursor_color_1_red	0x26
60#define TIDAC_cursor_color_1_green	0x27
61#define TIDAC_cursor_color_1_blue	0x28
62#define TIDAC_cursor_x_low		0x00
63#define TIDAC_cursor_x_high		0x01
64#define TIDAC_cursor_y_low		0x02
65#define TIDAC_cursor_y_high		0x03
66#define TIDAC_cursor_ram_addr_low	0x08
67#define TIDAC_cursor_ram_addr_high	0x09
68#define TIDAC_cursor_ram_data		0x0a
69
70
71#define TI_REF_FREQ             14.31818  /* 3025 only */
72
73#undef  FREQ_MIN
74#define FREQ_MIN   12000
75#define FREQ_MAX  220000
76
77
78
79void S3OutTiIndReg(ScrnInfoPtr pScrn, CARD32 reg, unsigned char mask,
80		   unsigned char data)
81{
82	S3Ptr pS3 = S3PTR(pScrn);
83	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
84	unsigned char tmp, tmp1, tmp2 = 0x00;
85
86	outb(vgaCRIndex, 0x55);
87	tmp = inb(vgaCRReg) & 0xfc;
88	outb(vgaCRReg, tmp | 0x01);
89	tmp1 = inb(TI_INDEX_REG);
90	outb(TI_INDEX_REG, reg);
91
92	if (mask != 0x00)
93		tmp2 = inb(TI_DATA_REG) & mask;
94	outb(TI_DATA_REG, tmp2 | data);
95
96	outb(TI_INDEX_REG, tmp1);
97	outb(vgaCRReg, tmp);
98}
99
100
101static unsigned char S3InTiIndReg(ScrnInfoPtr pScrn, CARD32 reg)
102{
103	S3Ptr pS3 = S3PTR(pScrn);
104	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
105	unsigned char tmp, tmp1, ret;
106
107	outb(vgaCRIndex, 0x55);
108	tmp = inb(vgaCRReg) & 0xfc;
109	outb(vgaCRReg, tmp | 0x01);
110	tmp1 = inb(TI_INDEX_REG);
111	outb(TI_INDEX_REG, reg);
112
113	ret = inb(TI_DATA_REG);
114
115	outb(TI_INDEX_REG, tmp1);
116	outb(vgaCRReg, tmp);
117
118	return ret;
119}
120
121
122Bool S3TiDACProbe(ScrnInfoPtr pScrn)
123{
124	S3Ptr pS3 = S3PTR(pScrn);
125	int found = 0;
126	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
127	unsigned char cr55, cr45, cr43, cr5c;
128	unsigned char TIndx, TIndx2, TIdata;
129
130	if (!S3_964_SERIES())
131		return FALSE;
132
133	outb(vgaCRIndex, 0x43);
134	cr43 = inb(vgaCRReg);
135	outb(vgaCRReg, cr43 & ~0x02);
136
137	outb(vgaCRIndex, 0x45);
138	cr45 = inb(vgaCRReg);
139
140	outb(vgaCRIndex, 0x55);
141	cr55 = inb(vgaCRReg);
142	outb(vgaCRReg, (cr55 & 0xfc) | 0x01);
143
144	TIndx = inb(TI_INDEX_REG);
145	outb(TI_INDEX_REG, TIDAC_id);
146	if(inb(TI_DATA_REG) == 0x20) {
147		found = TI3020_RAMDAC;
148		cr43 &= ~0x02;
149		cr45 &= ~0x20;
150	} else {
151		outb(vgaCRIndex, 0x5c);
152		cr5c = inb(vgaCRReg);
153		outb(vgaCRReg, cr5c & 0xdf);
154		TIndx2 = inb(TI_INDEX_REG);
155		outb(TI_INDEX_REG, TIDAC_ind_curs_ctrl);
156		TIdata = inb(TI_DATA_REG);
157		outb(TI_DATA_REG, TIdata & 0x7f);
158
159		outb(TI_INDEX_REG, TIDAC_id);
160		if (inb(TI_DATA_REG) == 0x25) {
161			found = TI3025_RAMDAC;
162			cr43 &= ~0x02;
163			cr45 &= ~0x20;
164		}
165
166		outb(TI_INDEX_REG, TIDAC_ind_curs_ctrl);
167		outb(TI_DATA_REG, TIdata);
168		outb(TI_INDEX_REG, TIndx2);
169		outb(vgaCRIndex, 0x5c);
170		outb(vgaCRReg, cr5c);
171	}
172
173	outb(TI_INDEX_REG, TIndx);
174	outb(vgaCRIndex, 0x55);
175	outb(vgaCRReg, cr55);
176
177	outb(vgaCRIndex, 0x45);
178	outb(vgaCRReg, cr45);
179
180	outb(vgaCRIndex, 0x43);
181	outb(vgaCRReg, cr43);
182
183	if (found) {
184		RamDacInit(pScrn, pS3->RamDacRec);
185		pS3->RamDac = RamDacHelperCreateInfoRec();
186		pS3->RamDac->RamDacType = found;
187		return TRUE;
188	}
189
190	return FALSE;
191}
192
193
194void S3TiDAC_Save(ScrnInfoPtr pScrn)
195{
196	S3Ptr pS3 = S3PTR(pScrn);
197	S3RegPtr save = &pS3->SavedRegs;
198	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
199
200	if (pS3->RamDac->RamDacType == TI3025_RAMDAC) {
201		unsigned char cr5c;
202
203		outb(vgaCRIndex, 0x5c);
204		cr5c = inb(vgaCRReg);
205		outb(vgaCRReg, cr5c & 0xdf);
206
207		save->dacregs[TIDAC_ind_curs_ctrl] =
208				 S3InTiIndReg(pScrn, TIDAC_ind_curs_ctrl);
209		S3OutTiIndReg(pScrn, TIDAC_ind_curs_ctrl, 0x7f, 0x00);
210	}
211
212	save->dacregs[TIDAC_true_color_ctrl] =
213			S3InTiIndReg(pScrn, TIDAC_true_color_ctrl);
214	save->dacregs[TIDAC_multiplex_ctrl] =
215			S3InTiIndReg(pScrn, TIDAC_multiplex_ctrl);
216	save->dacregs[TIDAC_clock_select] =
217			S3InTiIndReg(pScrn, TIDAC_clock_select);
218	save->dacregs[TIDAC_output_clock_select] =
219			S3InTiIndReg(pScrn, TIDAC_output_clock_select);
220	save->dacregs[TIDAC_general_ctrl] =
221			S3InTiIndReg(pScrn, TIDAC_general_ctrl);
222	save->dacregs[TIDAC_auxiliary_ctrl] =
223			S3InTiIndReg(pScrn, TIDAC_auxiliary_ctrl);
224	S3OutTiIndReg(pScrn, TIDAC_general_io_ctrl, 0x00, 0x1f);
225	save->dacregs[TIDAC_general_io_data] =
226			S3InTiIndReg(pScrn, TIDAC_general_io_data);
227
228	if (pS3->RamDac->RamDacType == TI3025_RAMDAC) {
229		save->dacregs[0x0e] = S3InTiIndReg(pScrn, 0x0e);
230		save->dacregs[TIDAC_misc_ctrl] =
231			S3InTiIndReg(pScrn, TIDAC_misc_ctrl);
232		S3OutTiIndReg(pScrn, TIDAC_pll_addr, 0x00, 0x00);
233		save->dacregs[0x40] = S3InTiIndReg(pScrn, TIDAC_pll_pixel_data);
234		S3OutTiIndReg(pScrn, TIDAC_pll_pixel_data, 0x00,
235			      save->dacregs[0x40]);
236		save->dacregs[0x41] = S3InTiIndReg(pScrn, TIDAC_pll_pixel_data);
237		S3OutTiIndReg(pScrn, TIDAC_pll_pixel_data, 0x00,
238			      save->dacregs[0x41]);
239		save->dacregs[0x42] = S3InTiIndReg(pScrn, TIDAC_pll_pixel_data);
240		S3OutTiIndReg(pScrn, TIDAC_pll_pixel_data, 0x00,
241			      save->dacregs[0x42]);
242		save->dacregs[0x43] = S3InTiIndReg(pScrn, TIDAC_pll_memory_data);
243		S3OutTiIndReg(pScrn, TIDAC_pll_memory_data, 0x00,
244			      save->dacregs[0x43]);
245		save->dacregs[0x44] = S3InTiIndReg(pScrn, TIDAC_pll_memory_data);
246		S3OutTiIndReg(pScrn, TIDAC_pll_memory_data, 0x00,
247			      save->dacregs[0x44]);
248		save->dacregs[0x45] = S3InTiIndReg(pScrn, TIDAC_pll_memory_data);
249		S3OutTiIndReg(pScrn, TIDAC_pll_memory_data, 0x00,
250			      save->dacregs[0x45]);
251		save->dacregs[0x46] = S3InTiIndReg(pScrn, TIDAC_pll_loop_data);
252		S3OutTiIndReg(pScrn, TIDAC_pll_loop_data, 0x00,
253			      save->dacregs[0x46]);
254		save->dacregs[0x47] = S3InTiIndReg(pScrn, TIDAC_pll_loop_data);
255		S3OutTiIndReg(pScrn, TIDAC_pll_loop_data, 0x00,
256			      save->dacregs[0x47]);
257		save->dacregs[0x48] = S3InTiIndReg(pScrn, TIDAC_pll_loop_data);
258		S3OutTiIndReg(pScrn, TIDAC_pll_loop_data, 0x00,
259			      save->dacregs[0x48]);
260	}
261}
262
263
264void S3TiDAC_Restore(ScrnInfoPtr pScrn)
265{
266	S3Ptr pS3 = S3PTR(pScrn);
267	S3RegPtr restore = &pS3->SavedRegs;
268
269	S3OutTiIndReg(pScrn, TIDAC_true_color_ctrl, 0x00,
270		      restore->dacregs[TIDAC_true_color_ctrl]);
271	S3OutTiIndReg(pScrn, TIDAC_multiplex_ctrl, 0x00,
272		      restore->dacregs[TIDAC_multiplex_ctrl]);
273	S3OutTiIndReg(pScrn, TIDAC_clock_select, 0x00,
274		      restore->dacregs[TIDAC_clock_select]);
275	S3OutTiIndReg(pScrn, TIDAC_output_clock_select, 0x00,
276		      restore->dacregs[TIDAC_output_clock_select]);
277	S3OutTiIndReg(pScrn, TIDAC_general_ctrl, 0x00,
278		      restore->dacregs[TIDAC_general_ctrl]);
279	S3OutTiIndReg(pScrn, TIDAC_auxiliary_ctrl, 0x00,
280		      restore->dacregs[TIDAC_auxiliary_ctrl]);
281	S3OutTiIndReg(pScrn, TIDAC_general_io_ctrl, 0x00, 0x1f);
282	S3OutTiIndReg(pScrn, TIDAC_general_io_data, 0x00,
283		      restore->dacregs[TIDAC_general_io_data]);
284	if (pS3->RamDac->RamDacType == TI3025_RAMDAC) {
285		S3OutTiIndReg(pScrn, TIDAC_pll_addr, 0x00,
286			      restore->dacregs[TIDAC_pll_addr]);
287		S3OutTiIndReg(pScrn, TIDAC_pll_pixel_data, 0x00,
288			      restore->dacregs[0x40]);
289		S3OutTiIndReg(pScrn, TIDAC_pll_pixel_data, 0x00,
290			      restore->dacregs[0x41]);
291		S3OutTiIndReg(pScrn, TIDAC_pll_pixel_data, 0x00,
292			      restore->dacregs[0x42]);
293
294		S3OutTiIndReg(pScrn, TIDAC_pll_memory_data, 0x00,
295			      restore->dacregs[0x43]);
296		S3OutTiIndReg(pScrn, TIDAC_pll_memory_data, 0x00,
297			      restore->dacregs[0x44]);
298		S3OutTiIndReg(pScrn, TIDAC_pll_memory_data, 0x00,
299			      restore->dacregs[0x45] | 0x80);
300
301		S3OutTiIndReg(pScrn, TIDAC_pll_loop_data, 0x00,
302			      restore->dacregs[0x46]);
303		S3OutTiIndReg(pScrn, TIDAC_pll_loop_data, 0x00,
304			      restore->dacregs[0x47]);
305		S3OutTiIndReg(pScrn, TIDAC_pll_loop_data, 0x00,
306			      restore->dacregs[0x48]);
307
308		S3OutTiIndReg(pScrn, 0x0e, 0x00, restore->dacregs[0x0e]);
309		S3OutTiIndReg(pScrn, TIDAC_misc_ctrl, 0x00,
310			      restore->dacregs[TIDAC_misc_ctrl]);
311	}
312
313	S3OutTiIndReg(pScrn, TIDAC_ind_curs_ctrl, 0x00,
314		      restore->dacregs[TIDAC_ind_curs_ctrl]);
315}
316
317
318void S3TiDAC_PreInit(ScrnInfoPtr pScrn)
319{
320        S3Ptr pS3 = S3PTR(pScrn);
321        int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
322	int mclk, m, n, p, mcc, cr5c;
323
324	outb(vgaCRIndex, 0x5c);
325	cr5c = inb(vgaCRReg);
326	outb(vgaCRReg, cr5c & 0xdf);
327
328	S3OutTiIndReg(pScrn, TIDAC_pll_addr, 0x00, 0x00);
329	n = S3InTiIndReg(pScrn, TIDAC_pll_memory_data) & 0x7f;
330	S3OutTiIndReg(pScrn, TIDAC_pll_addr, 0x00, 0x01);
331	m = S3InTiIndReg(pScrn, TIDAC_pll_memory_data) & 0x7f;
332	S3OutTiIndReg(pScrn, TIDAC_pll_addr, 0x00, 0x02);
333	p = S3InTiIndReg(pScrn, TIDAC_pll_memory_data) & 0x03;
334	mcc = S3InTiIndReg(pScrn, TIDAC_clock_ctrl);
335	if (mcc & 0x08)
336		mcc = (mcc & 0x07) * 2 + 2;
337	else
338		mcc = 1;
339
340	mclk = ((1431818 * ((m+2) * 8)) / (n+2) / (1 << p) / mcc + 50) / 100;
341	pS3->mclk = mclk;
342     	xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f MHz\n",
343           	   mclk / 1000.0);
344
345	outb(vgaCRIndex, 0x5c);
346	outb(vgaCRReg, cr5c);
347}
348
349
350static void S3TiDACCalcNMP(long freq, int *calc_n, int *calc_m, int *calc_p)
351{
352	double ffreq;
353	int n, m, p;
354	int best_n=32, best_m=32;
355	double diff, mindiff;
356
357	if (freq < FREQ_MIN)
358		ffreq = FREQ_MIN / 1000.0;
359	else if (freq > FREQ_MAX)
360		ffreq = FREQ_MAX / 1000.0;
361	else
362		ffreq = freq / 1000.0;
363
364	for (p=0; p<4 && ffreq<110.0; p++)
365		ffreq *= 2;
366#if FREQ_MIN < 110000/8
367	if (p == 4) {
368		ffreq /= 2;
369		p--;
370	}
371#endif
372
373	ffreq /= TI_REF_FREQ;
374
375	mindiff = ffreq;
376
377	for (n=1; n<=(int)(TI_REF_FREQ/0.5 - 2); n++) {
378		m = (int)(ffreq * (n+2) / 8.0 + 0.5) - 2;
379		if (m < 1)
380			m = 1;
381		else if (m > 127)
382			m = 127;
383
384		diff = ((m+2) * 8) / (n+2.0) - ffreq;
385		if (diff < 0)
386			diff = -diff;
387
388		if (diff < mindiff) {
389			mindiff = diff;
390			best_n = n;
391			best_m = m;
392		}
393	}
394
395	*calc_n = best_n;
396   	*calc_m = best_m;
397   	*calc_p = p;
398}
399
400
401static void S3TiDACProgramClock(ScrnInfoPtr pScrn, int clk,
402				unsigned char n, unsigned char m,
403				unsigned char p)
404{
405	S3OutTiIndReg(pScrn, TIDAC_pll_addr, 0x00, 0x00);
406
407	if (clk != TIDAC_pll_memory_data) {
408		S3OutTiIndReg(pScrn, TIDAC_pll_pixel_data, 0x00, n);
409		S3OutTiIndReg(pScrn, TIDAC_pll_pixel_data, 0x00, m);
410		S3OutTiIndReg(pScrn, TIDAC_pll_pixel_data, 0x00, p | 0x08);
411
412		S3OutTiIndReg(pScrn, TIDAC_pll_loop_data, 0x00, 0x01);
413		S3OutTiIndReg(pScrn, TIDAC_pll_loop_data, 0x00, 0x01);
414		S3OutTiIndReg(pScrn, TIDAC_pll_loop_data, 0x00, p > 0 ? p : 1);
415		S3OutTiIndReg(pScrn, TIDAC_misc_ctrl, 0x00, 0x80 | 0x40 | 0x04);
416
417		S3OutTiIndReg(pScrn, TIDAC_clock_select, 0x00, 0x05);
418	} else {
419		S3OutTiIndReg(pScrn, TIDAC_pll_memory_data, 0x00, n);
420		S3OutTiIndReg(pScrn, TIDAC_pll_memory_data, 0x00, m);
421		S3OutTiIndReg(pScrn, TIDAC_pll_memory_data, 0x00, p | 0x80);
422	}
423}
424
425
426static void S3TiDACSetClock(ScrnInfoPtr pScrn, long freq, int clk)
427{
428	int m, n, p;
429
430	S3TiDACCalcNMP(freq, &n, &m, &p);
431
432	S3TiDACProgramClock(pScrn, clk, n, m, p);
433}
434
435
436
437void S3TiDAC_Init(ScrnInfoPtr pScrn, DisplayModePtr mode)
438{
439        S3Ptr pS3 = S3PTR(pScrn);
440        vgaHWPtr hwp = VGAHWPTR(pScrn);
441        vgaRegPtr pVga = &hwp->ModeReg;
442        int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
443	unsigned char tmp, tmp1, tmp2;
444
445	S3TiDACSetClock(pScrn, mode->Clock, 2);
446	outb(vgaCRIndex, 0x42);
447	tmp = inb(vgaCRReg) & 0xf0;
448	outb(vgaCRReg, tmp | 0x02);
449	usleep(150000);
450
451	outb(0x3c4, 1);
452	tmp2 = inb(0x3c5);
453	outb(0x3c5, tmp2 | 0x20);	/* blank */
454
455	tmp = pVga->MiscOutReg;
456	pVga->MiscOutReg |= 0xc0;
457	tmp1 = 0x00;
458	if (!(tmp & 0x80))
459		tmp1 |= 0x02;
460      	if (!(tmp & 0x40))
461		tmp1 |= 0x01;
462
463	S3OutTiIndReg(pScrn, TIDAC_general_ctrl, 0x00, tmp1);
464	S3OutTiIndReg(pScrn, 0x0e, 0x00, 0x00);
465
466	/* XXX do 3020 input_clock_sel */
467
468	outb(vgaCRIndex, 0x65);
469	/* XXX 3025 */
470	outb(vgaCRReg, 0x00);
471
472	/* XXX 3025 */
473	outb(vgaCRIndex, 0x40);
474	outb(vgaCRReg, 0x11);
475	outb(vgaCRIndex, 0x55);
476	outb(vgaCRReg, 0x00);
477
478	if (pScrn->bitsPerPixel > 8)
479		S3OutTiIndReg(pScrn, TIDAC_auxiliary_ctrl, 0x00, 0x00);
480	else
481		S3OutTiIndReg(pScrn, TIDAC_auxiliary_ctrl, 0x00, 0x01);
482
483	switch (pScrn->depth) {
484	case 8:
485		S3OutTiIndReg(pScrn, TIDAC_output_clock_select, 0x00,
486			      0x4b);
487		outb(vgaCRIndex, 0x66);
488		tmp = inb(vgaCRReg);
489		if (mode->Clock > 80000)
490			outb(vgaCRReg, (tmp & 0xf8) | 0x02);
491		else
492			outb(vgaCRReg, (tmp & 0xf8) | 0x03);
493		break;
494	case 16:
495		S3OutTiIndReg(pScrn, TIDAC_output_clock_select, 0x00,
496			      0x4a);
497		outb(vgaCRIndex, 0x66);
498		tmp = inb(vgaCRReg);
499		if (mode->Clock > 80000)
500			outb(vgaCRReg, (tmp & 0xf8) | 0x01);
501		else
502			outb(vgaCRReg, (tmp & 0xf8) | 0x02);
503		break;
504	case 24:
505		S3OutTiIndReg(pScrn, TIDAC_output_clock_select, 0x00,
506			      (0x40 | 0x08 | 0x01));
507		outb(vgaCRIndex, 0x66);
508		tmp = inb(vgaCRReg);
509		if (mode->Clock > 80000)
510			outb(vgaCRReg, (tmp & 0xf8) | 0x00);
511		else
512			outb(vgaCRReg, (tmp & 0xf8) | 0x01);
513		break;
514	}
515
516	outb(vgaCRIndex, 0x58);
517	tmp = inb(vgaCRReg);
518	outb(vgaCRReg, (tmp & 0xbf) | 0x40);
519
520	switch(pScrn->depth) {
521	case 8:
522		S3OutTiIndReg(pScrn, TIDAC_true_color_ctrl, 0x00, 0x80);
523		S3OutTiIndReg(pScrn, TIDAC_multiplex_ctrl, 0x00, 0x1c);
524		break;
525	case 15:
526		S3OutTiIndReg(pScrn, TIDAC_true_color_ctrl, 0x00, 0x4c);
527		S3OutTiIndReg(pScrn, TIDAC_multiplex_ctrl, 0x00, 0x04);
528		S3OutTiIndReg(pScrn, TIDAC_key_ctrl, 0x00, 0x01);
529		break;
530	case 16:
531		S3OutTiIndReg(pScrn, TIDAC_true_color_ctrl, 0x00, 0x4d);
532		S3OutTiIndReg(pScrn, TIDAC_multiplex_ctrl, 0x00, 0x04);
533		S3OutTiIndReg(pScrn, TIDAC_key_ctrl, 0x00, 0x01);
534		break;
535	case 24:
536		S3OutTiIndReg(pScrn, TIDAC_true_color_ctrl, 0x00, 0x4e);
537		S3OutTiIndReg(pScrn, TIDAC_multiplex_ctrl, 0x00, 0x04);
538		S3OutTiIndReg(pScrn, TIDAC_key_ctrl, 0x00, 0x01);
539		break;
540	}
541
542	S3OutTiIndReg(pScrn, TIDAC_general_io_ctrl, 0x00, 0x1f);
543	S3OutTiIndReg(pScrn, TIDAC_general_io_data, 0x00, 0x01);
544	S3OutTiIndReg(pScrn, TIDAC_general_io_ctrl, 0x00, 0x00);
545	S3OutTiIndReg(pScrn, TIDAC_misc_ctrl, 0xf0, (0x04 | 0x08));
546
547	outb(vgaCRIndex, 0x6d);
548	if (pS3->s3Bpp == 1)
549		if (mode->Clock > 80000)
550			outb(vgaCRReg, 0x02);
551		else
552			outb(vgaCRReg, 0x03);
553	else if (pS3->s3Bpp == 2)
554		if (mode->Clock > 80000)
555			outb(vgaCRReg, 0x00);
556		else
557			outb(vgaCRReg, 0x01);
558	else
559		outb(vgaCRReg, 0x00);
560
561	S3OutTiIndReg(pScrn, TIDAC_sense_test, 0x00, 0x00);
562
563	if (pS3->s3Bpp > 1)
564	{
565		int j;
566
567		outb(0x3c6, 0xff);
568		outb(0x3c8, 0x00);
569		for(j=0; j<768; j++) {
570			outb(0x3c9, j);
571			outb(0x3c9, j);
572			outb(0x3c9, j);
573		}
574	}
575
576	outb(0x3c4, 1);
577	outb(0x3c5, tmp2);	/* unblank */
578}
579
580
581void S3TiLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indicies, LOCO *colors,
582		     VisualPtr pVisual)
583{
584	int i;
585
586	outb(0x3c6, 0xff);
587	outb(0x3c8, 0x00);
588
589	for (i=0; i<768; i++) {
590		outb(0x3c9, i);
591		usleep(1000);
592		outb(0x3c9, i);
593		usleep(1000);
594		outb(0x3c9, i);
595		usleep(1000);
596	}
597}
598
599
600
601/* hardware cursor */
602
603static void S3TiSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg)
604{
605        S3Ptr pS3 = S3PTR(pScrn);
606        int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
607
608        /* unlock sys regs */
609        outb(vgaCRIndex, 0x39);
610        outb(vgaCRReg, 0xa5);
611
612	S3OutTiIndReg(pScrn, TIDAC_cursor_color_0_red, 0x00,
613		      (bg & 0x00FF0000) >> 16);
614	S3OutTiIndReg(pScrn, TIDAC_cursor_color_0_green, 0x00,
615		      (bg & 0x0000FF00) >> 8);
616	S3OutTiIndReg(pScrn, TIDAC_cursor_color_0_blue, 0x00,
617		      (bg & 0x000000FF));
618
619	S3OutTiIndReg(pScrn, TIDAC_cursor_color_1_red, 0x00,
620		      (fg & 0x00FF0000) >> 16);
621	S3OutTiIndReg(pScrn, TIDAC_cursor_color_1_green, 0x00,
622		      (fg & 0x0000FF00) >> 8);
623	S3OutTiIndReg(pScrn, TIDAC_cursor_color_1_blue, 0x00,
624		      (fg & 0x000000FF));
625
626}
627
628
629static void S3TiSetCursorPosition(ScrnInfoPtr pScrn, int x, int y)
630{
631        S3Ptr pS3 = S3PTR(pScrn);
632        int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
633
634        /* unlock sys regs */
635        outb(vgaCRIndex, 0x39);
636        outb(vgaCRReg, 0xa5);
637
638	S3OutTiIndReg(pScrn, TIDAC_cursor_x_low, 0x00, x & 0xff);
639	S3OutTiIndReg(pScrn, TIDAC_cursor_x_high, 0x00, (x >> 8) & 0x0f);
640	S3OutTiIndReg(pScrn, TIDAC_cursor_y_low, 0x00, y & 0xff);
641	S3OutTiIndReg(pScrn, TIDAC_cursor_y_high, 0x00, (y >> 8) & 0x0f);
642}
643
644
645static void S3TiHideCursor(ScrnInfoPtr pScrn)
646{
647        S3Ptr pS3 = S3PTR(pScrn);
648        int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
649
650        /* unlock sys regs */
651        outb(vgaCRIndex, 0x39);
652        outb(vgaCRReg, 0xa5);
653
654	S3OutTiIndReg(pScrn, TIDAC_ind_curs_ctrl, (unsigned char)
655		      ~(0x40 | 0x10), 0x00);
656}
657
658
659static void S3TiShowCursor(ScrnInfoPtr pScrn)
660{
661        S3Ptr pS3 = S3PTR(pScrn);
662        int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
663        unsigned char tmp;
664
665        /* unlock sys regs */
666        outb(vgaCRIndex, 0x39);
667        outb(vgaCRReg, 0xa5);
668
669	outb(vgaCRIndex, 0x55);
670	tmp = inb(vgaCRReg) & 0xdf;
671	outb(vgaCRReg, tmp | 0x20);
672
673	outb(vgaCRIndex, 0x45);
674	tmp = inb(vgaCRReg) & 0xdf;
675	outb(vgaCRReg, tmp | 0x20);
676
677	S3OutTiIndReg(pScrn, TIDAC_ind_curs_ctrl, (unsigned char)
678		      ~(0x40 | 0x10), (0x40 | 0x10));
679}
680
681
682static void S3TiLoadCursorImage(ScrnInfoPtr pScrn, unsigned char *image)
683{
684        S3Ptr pS3 = S3PTR(pScrn);
685        int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
686        unsigned char tmp, tmp1;
687        register int i;
688#if 0
689	register unsigned char *mask = image + 1;
690#endif
691
692        /* unlock sys regs */
693        outb(vgaCRIndex, 0x39);
694        outb(vgaCRReg, 0xa5);
695
696	outb(vgaCRIndex, 0x55);
697	tmp = inb(vgaCRReg) & 0xfc;
698	outb(vgaCRReg, tmp | 0x01);
699	tmp1 = inb(TI_INDEX_REG);
700
701	outb(TI_INDEX_REG, TIDAC_cursor_ram_addr_low);
702	outb(TI_DATA_REG, 0x00);
703	outb(TI_INDEX_REG, TIDAC_cursor_ram_addr_high);
704	outb(TI_DATA_REG, 0x00);
705	outb(TI_INDEX_REG, TIDAC_cursor_ram_data);
706
707#if 0
708	for (i=0; i<512; i++, mask+=2)
709		outb(TI_DATA_REG, *mask);
710	for (i=0; i<512; i++, image+=2)
711		outb(TI_DATA_REG, *image);
712#else
713	for (i=0; i<1024; i++) {
714		outb(TI_DATA_REG, *image);
715		image++;
716	}
717#endif
718
719	outb(TI_INDEX_REG, tmp1);
720
721	outb(vgaCRIndex, 0x55);
722	outb(vgaCRReg, tmp);
723}
724
725
726
727static Bool S3TiUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs)
728{
729	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
730	S3Ptr pS3 = S3PTR(pScrn);
731	return (pS3->hwCursor);
732}
733
734
735
736Bool S3Ti_CursorInit(ScreenPtr pScreen)
737{
738        ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
739        S3Ptr pS3 = S3PTR(pScrn);
740        xf86CursorInfoPtr pCurs;
741
742        if (!(pCurs = pS3->pCurs = xf86CreateCursorInfoRec()))
743                return FALSE;
744
745        pCurs->MaxWidth = 64;
746        pCurs->MaxHeight = 64;
747        pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
748		       HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 |
749		       HARDWARE_CURSOR_NIBBLE_SWAPPED |
750		       HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
751
752        pCurs->SetCursorColors = S3TiSetCursorColors;
753        pCurs->SetCursorPosition = S3TiSetCursorPosition;
754        pCurs->LoadCursorImage = S3TiLoadCursorImage;
755        pCurs->HideCursor = S3TiHideCursor;
756        pCurs->ShowCursor = S3TiShowCursor;
757        pCurs->UseHWCursor = S3TiUseHWCursor;
758
759        return xf86InitCursor(pScreen, pCurs);
760}
761