1340e3fbdSmrg/*
2340e3fbdSmrg *      Copyright 2001  Ani Joshi <ajoshi@unixbox.com>
3340e3fbdSmrg *
4340e3fbdSmrg *      XFree86 4.x driver for S3 chipsets
5340e3fbdSmrg *
6340e3fbdSmrg *
7340e3fbdSmrg * Permission to use, copy, modify, distribute, and sell this software and its
8340e3fbdSmrg * documentation for any purpose is hereby granted without fee, provided that
9340e3fbdSmrg * the above copyright notice appear in all copies and that both that copyright
10340e3fbdSmrg * notice and this permission notice appear in supporting documentation and
11340e3fbdSmrg * that the name of Ani Joshi not be used in advertising or
12340e3fbdSmrg * publicity pertaining to distribution of the software without specific,
13340e3fbdSmrg * written prior permission.  Ani Joshi makes no representations
14340e3fbdSmrg * about the suitability of this software for any purpose.  It is provided
15340e3fbdSmrg * "as-is" without express or implied warranty.
16340e3fbdSmrg *
17340e3fbdSmrg * ANI JOSHI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18340e3fbdSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19340e3fbdSmrg * EVENT SHALL ANI JOSHI BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20340e3fbdSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21340e3fbdSmrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22340e3fbdSmrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23340e3fbdSmrg * PERFORMANCE OF THIS SOFTWARE.
24340e3fbdSmrg *
25340e3fbdSmrg *
26340e3fbdSmrg */
27340e3fbdSmrg
28340e3fbdSmrg#ifdef HAVE_CONFIG_H
29340e3fbdSmrg#include "config.h"
30340e3fbdSmrg#endif
31340e3fbdSmrg
32340e3fbdSmrg#include "xf86.h"
33340e3fbdSmrg#include "xf86_OSproc.h"
34340e3fbdSmrg
35340e3fbdSmrg#include "compiler.h"
36340e3fbdSmrg
37340e3fbdSmrg#include "IBM.h"
38340e3fbdSmrg
39340e3fbdSmrg#include "s3.h"
40340e3fbdSmrg
41340e3fbdSmrg
42340e3fbdSmrg#define IBMRGB_WRITE_ADDR           0x3C8   /* CR55 low bit == 0 */
43340e3fbdSmrg#define IBMRGB_RAMDAC_DATA          0x3C9   /* CR55 low bit == 0 */
44340e3fbdSmrg#define IBMRGB_PIXEL_MASK           0x3C6   /* CR55 low bit == 0 */
45340e3fbdSmrg#define IBMRGB_READ_ADDR            0x3C7   /* CR55 low bit == 0 */
46340e3fbdSmrg#define IBMRGB_INDEX_LOW            0x3C8   /* CR55 low bit == 1 */
47340e3fbdSmrg#define IBMRGB_INDEX_HIGH           0x3C9   /* CR55 low bit == 1 */
48340e3fbdSmrg#define IBMRGB_INDEX_DATA           0x3C6   /* CR55 low bit == 1 */
49340e3fbdSmrg#define IBMRGB_INDEX_CONTROL        0x3C7   /* CR55 low bit == 1 */
50340e3fbdSmrg
51340e3fbdSmrg
52340e3fbdSmrgstatic void S3OutIBMRGBIndReg(ScrnInfoPtr pScrn, CARD32 reg,
53340e3fbdSmrg		       unsigned char mask, unsigned char data)
54340e3fbdSmrg{
55340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
56340e3fbdSmrg	unsigned char tmp, tmp2 = 0x00;
57340e3fbdSmrg	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
58340e3fbdSmrg
59340e3fbdSmrg	outb(vgaCRIndex, 0x55);
60340e3fbdSmrg	tmp = inb(vgaCRReg) & 0xfc;
61340e3fbdSmrg	outb(vgaCRReg, tmp | 0x01);
62340e3fbdSmrg
63340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, reg);
64340e3fbdSmrg
65340e3fbdSmrg	if (mask != 0x00)
66340e3fbdSmrg		tmp2 = inb(IBMRGB_INDEX_DATA) & mask;
67340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, tmp2 | data);
68340e3fbdSmrg
69340e3fbdSmrg	outb(vgaCRIndex, 0x55);
70340e3fbdSmrg	outb(vgaCRReg, tmp);
71340e3fbdSmrg}
72340e3fbdSmrg
73340e3fbdSmrg
74340e3fbdSmrgstatic unsigned char S3InIBMRGBIndReg(ScrnInfoPtr pScrn, CARD32 reg)
75340e3fbdSmrg{
76340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
77340e3fbdSmrg	unsigned char tmp, ret;
78340e3fbdSmrg	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
79340e3fbdSmrg
80340e3fbdSmrg	outb(vgaCRIndex, 0x55);
81340e3fbdSmrg	tmp = inb(vgaCRReg) & 0xfc;
82340e3fbdSmrg	outb(vgaCRReg, tmp | 0x01);
83340e3fbdSmrg
84340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, reg);
85340e3fbdSmrg	ret = inb(IBMRGB_INDEX_DATA);
86340e3fbdSmrg
87340e3fbdSmrg	outb(vgaCRIndex, 0x55);
88340e3fbdSmrg	outb(vgaCRReg, tmp);
89340e3fbdSmrg
90340e3fbdSmrg	return ret;
91340e3fbdSmrg}
92340e3fbdSmrg
93340e3fbdSmrg
94340e3fbdSmrgstatic void S3IBMWriteAddress(ScrnInfoPtr pScrn, CARD32 index)
95340e3fbdSmrg{
96340e3fbdSmrg	outb(IBMRGB_WRITE_ADDR, index);
97340e3fbdSmrg}
98340e3fbdSmrg
99340e3fbdSmrgstatic void S3IBMWriteData(ScrnInfoPtr pScrn, unsigned char data)
100340e3fbdSmrg{
101340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, data);
102340e3fbdSmrg}
103340e3fbdSmrg
104340e3fbdSmrgstatic void S3IBMReadAddress(ScrnInfoPtr pScrn, CARD32 index)
105340e3fbdSmrg{
106340e3fbdSmrg	outb(IBMRGB_READ_ADDR, index);
107340e3fbdSmrg}
108340e3fbdSmrg
109340e3fbdSmrgstatic unsigned char S3IBMReadData(ScrnInfoPtr pScrn)
110340e3fbdSmrg{
111340e3fbdSmrg	return inb(IBMRGB_RAMDAC_DATA);
112340e3fbdSmrg}
113340e3fbdSmrg
114340e3fbdSmrg
115340e3fbdSmrgBool S3ProbeIBMramdac(ScrnInfoPtr pScrn)
116340e3fbdSmrg{
117340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
118340e3fbdSmrg
119340e3fbdSmrg	if (pS3->Chipset != PCI_CHIP_968)
120340e3fbdSmrg		return FALSE;
121340e3fbdSmrg
122340e3fbdSmrg	pS3->RamDacRec = RamDacCreateInfoRec();
123340e3fbdSmrg	pS3->RamDacRec->ReadDAC = S3InIBMRGBIndReg;
124340e3fbdSmrg	pS3->RamDacRec->WriteDAC = S3OutIBMRGBIndReg;
125340e3fbdSmrg	pS3->RamDacRec->ReadAddress = S3IBMReadAddress;
126340e3fbdSmrg	pS3->RamDacRec->WriteAddress = S3IBMWriteAddress;
127340e3fbdSmrg	pS3->RamDacRec->ReadData = S3IBMReadData;
128340e3fbdSmrg	pS3->RamDacRec->WriteData = S3IBMWriteData;
129340e3fbdSmrg	pS3->RamDacRec->LoadPalette = NULL;
130340e3fbdSmrg
131340e3fbdSmrg	if (!RamDacInit(pScrn, pS3->RamDacRec)) {
132340e3fbdSmrg		RamDacDestroyInfoRec(pS3->RamDacRec);
133340e3fbdSmrg		xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RamDacInit failed\n");
134340e3fbdSmrg		return FALSE;
135340e3fbdSmrg	}
136340e3fbdSmrg
137340e3fbdSmrg	pS3->RamDac = IBMramdacProbe(pScrn, S3IBMRamdacs);
138340e3fbdSmrg	if (pS3->RamDac)
139340e3fbdSmrg		return TRUE;
140340e3fbdSmrg
141340e3fbdSmrg	return FALSE;
142340e3fbdSmrg}
143340e3fbdSmrg
144340e3fbdSmrgstatic void S3ProgramIBMRGBClock(ScrnInfoPtr pScrn, int clk, unsigned char m,
145340e3fbdSmrg			  unsigned char n, unsigned char df)
146340e3fbdSmrg{
147340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_misc_clock, ~1, 1);
148340e3fbdSmrg
149340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_m0+2*clk, 0, (df<<6)|(m&0x3f));
150340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_n0+2*clk, 0, n);
151340e3fbdSmrg
152340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_pll_ctrl2, 0xf0, clk);
153340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_pll_ctrl1, 0xf8, 3);
154340e3fbdSmrg}
155340e3fbdSmrg
156340e3fbdSmrg
157340e3fbdSmrgstatic void S3IBMRGBSetClock(ScrnInfoPtr pScrn, long freq, int clk, long dacspeed,
158340e3fbdSmrg		      long fref)
159340e3fbdSmrg{
1601d3211a3Smrg	volatile double ffreq, ffref;
161340e3fbdSmrg	volatile int df, n, m, max_n, min_df;
162340e3fbdSmrg	volatile int best_m=69, best_n=17, best_df=0;
163340e3fbdSmrg	volatile double diff, mindiff;
164340e3fbdSmrg
165340e3fbdSmrg#define FREQ_MIN	16250
166340e3fbdSmrg#define FREQ_MAX	dacspeed
167340e3fbdSmrg
168340e3fbdSmrg	if (freq < FREQ_MIN)
169340e3fbdSmrg		ffreq = FREQ_MIN / 1000.0;
170340e3fbdSmrg	else if (freq > FREQ_MAX)
171340e3fbdSmrg		ffreq = FREQ_MAX / 1000.0;
172340e3fbdSmrg	else
173340e3fbdSmrg		ffreq = freq / 1000.0;
174340e3fbdSmrg
175340e3fbdSmrg	ffref = fref / 1e3;
176340e3fbdSmrg
177340e3fbdSmrg	ffreq /= ffref;
178340e3fbdSmrg	ffreq *= 16;
179340e3fbdSmrg	mindiff = ffreq;
180340e3fbdSmrg
181340e3fbdSmrg	if (freq <= dacspeed/4)
182340e3fbdSmrg		min_df = 0;
183340e3fbdSmrg	else if (freq <= dacspeed/2)
184340e3fbdSmrg		min_df = 1;
185340e3fbdSmrg	else
186340e3fbdSmrg		min_df = 2;
187340e3fbdSmrg
188340e3fbdSmrg	for (df=0; df<4; df++) {
189340e3fbdSmrg		ffreq /= 2;
190340e3fbdSmrg		mindiff /= 2;
191340e3fbdSmrg		if (df < min_df)
192340e3fbdSmrg			continue;
193340e3fbdSmrg
194340e3fbdSmrg		if (df < 3)
195340e3fbdSmrg			max_n = fref / 1000 / 2;
196340e3fbdSmrg		else
197340e3fbdSmrg			max_n = fref / 1000;
198340e3fbdSmrg		if (max_n > 31)
199340e3fbdSmrg			max_n = 31;
200340e3fbdSmrg
201340e3fbdSmrg		for (n=2; n <= max_n; n++) {
202340e3fbdSmrg			m = (int)(ffreq * n + 0.5) - 65;
203340e3fbdSmrg			if (m < 0)
204340e3fbdSmrg				m = 0;
205340e3fbdSmrg			else if (m > 63)
206340e3fbdSmrg				m = 63;
207340e3fbdSmrg			diff = (m+65.0)/n-ffreq;
208340e3fbdSmrg			if (diff < 0)
209340e3fbdSmrg				diff = -diff;
210340e3fbdSmrg			if (diff < mindiff) {
211340e3fbdSmrg				mindiff = diff;
212340e3fbdSmrg				best_n = n;
213340e3fbdSmrg				best_m = m;
214340e3fbdSmrg				best_df = df;
215340e3fbdSmrg			}
216340e3fbdSmrg		}
217340e3fbdSmrg	}
218340e3fbdSmrg
219340e3fbdSmrg	S3ProgramIBMRGBClock(pScrn, clk, best_m, best_n, best_df);
220340e3fbdSmrg}
221340e3fbdSmrg
222340e3fbdSmrgvoid S3IBMRGB_Restore(ScrnInfoPtr pScrn)
223340e3fbdSmrg{
224340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
225340e3fbdSmrg	S3RegPtr restore = &pS3->SavedRegs;
226340e3fbdSmrg	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
227340e3fbdSmrg	int i;
228340e3fbdSmrg
229340e3fbdSmrg	for(i=0; i<0x100; i++)
230340e3fbdSmrg		S3OutIBMRGBIndReg(pScrn, i, 0, restore->dacregs[i]);
231340e3fbdSmrg
232340e3fbdSmrg	outb(vgaCRIndex, 0x22);
233340e3fbdSmrg	outb(vgaCRReg, restore->dacregs[0x100]);
234340e3fbdSmrg}
235340e3fbdSmrg
236340e3fbdSmrg
237340e3fbdSmrgvoid S3IBMRGB_Save(ScrnInfoPtr pScrn)
238340e3fbdSmrg{
239340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
240340e3fbdSmrg	S3RegPtr save = &pS3->SavedRegs;
241340e3fbdSmrg	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
242340e3fbdSmrg	int i;
243340e3fbdSmrg
244340e3fbdSmrg	for (i=0; i<0x100; i++)
245340e3fbdSmrg		save->dacregs[i] = S3InIBMRGBIndReg(pScrn, i);
246340e3fbdSmrg
247340e3fbdSmrg	outb(vgaCRIndex, 0x22);
248340e3fbdSmrg	save->dacregs[0x100] = inb(vgaCRReg);
249340e3fbdSmrg}
250340e3fbdSmrg
251340e3fbdSmrg
252340e3fbdSmrgvoid S3IBMRGB_PreInit(ScrnInfoPtr pScrn)
253340e3fbdSmrg{
254340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
255340e3fbdSmrg	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
256340e3fbdSmrg	unsigned char cr55, tmp;
257340e3fbdSmrg
258340e3fbdSmrg	outb(vgaCRIndex, 0x43);
259340e3fbdSmrg	tmp = inb(vgaCRReg);
260340e3fbdSmrg	outb(vgaCRReg, tmp & ~0x02);
261340e3fbdSmrg
262340e3fbdSmrg	outb(vgaCRIndex, 0x55);
263340e3fbdSmrg	cr55 = inb(vgaCRReg);
264340e3fbdSmrg	outb(vgaCRReg, (cr55 & ~0x03) | 0x01);	/* set rs2 */
265340e3fbdSmrg
266340e3fbdSmrg	tmp = inb(IBMRGB_INDEX_CONTROL);
267340e3fbdSmrg	outb(IBMRGB_INDEX_CONTROL, tmp & ~1);
268340e3fbdSmrg	outb(IBMRGB_INDEX_HIGH, 0);
269340e3fbdSmrg
270340e3fbdSmrg	outb(vgaCRIndex, 0x55);
271340e3fbdSmrg	outb(vgaCRReg, cr55 & ~0x03);
272340e3fbdSmrg
273340e3fbdSmrg	{
274340e3fbdSmrg		int m, n, df, mclk=0;
275340e3fbdSmrg
276340e3fbdSmrg		m = S3InIBMRGBIndReg(pScrn, IBMRGB_sysclk_vco_div);
277340e3fbdSmrg		n = S3InIBMRGBIndReg(pScrn, IBMRGB_sysclk_ref_div) & 0x1f;
278340e3fbdSmrg		df = m >> 6;
279340e3fbdSmrg		m &= 0x3f;
280340e3fbdSmrg		if (!n) {
281340e3fbdSmrg			m = 0;
282340e3fbdSmrg			n = 1;
283340e3fbdSmrg		}
284340e3fbdSmrg		mclk = ((pS3->RefClock*100 * (m+65)) / n / (8 >> df) + 50) / 100;
285340e3fbdSmrg		pS3->mclk = mclk;
286340e3fbdSmrg		xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f MHz\n",
287340e3fbdSmrg			   mclk / 1000.0);
288340e3fbdSmrg	}
289340e3fbdSmrg}
290340e3fbdSmrg
291340e3fbdSmrg
292340e3fbdSmrgvoid S3IBMRGB_Init(ScrnInfoPtr pScrn, DisplayModePtr mode)
293340e3fbdSmrg{
294340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
295340e3fbdSmrg	unsigned char tmp, blank;
296340e3fbdSmrg	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
297340e3fbdSmrg
298340e3fbdSmrg	S3IBMRGBSetClock(pScrn, mode->Clock, 2, pS3->MaxClock,
299340e3fbdSmrg			 pS3->RefClock);
300340e3fbdSmrg
301340e3fbdSmrg	outb(0x3c4, 1);
302340e3fbdSmrg	blank = inb(0x3c5);
303340e3fbdSmrg	outb(0x3c5, blank | 0x20);
304340e3fbdSmrg
305340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_misc_clock, 0xf0, 0x03);
306340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_sync, 0, 0);
307340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_hsync_pos, 0, 0);
308340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_pwr_mgmt, 0, 0);
309340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_dac_op, ~8, 0);
310340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_dac_op, ~2, 2);
311340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_pal_ctrl, 0, 0);
312340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_misc1, ~0x43, 1);
313340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_misc2, 0, 0x47);
314340e3fbdSmrg
315340e3fbdSmrg	outb(vgaCRIndex, 0x22);
316340e3fbdSmrg	tmp = inb(vgaCRReg);
317340e3fbdSmrg	if (pS3->s3Bpp == 1)
318340e3fbdSmrg		outb(vgaCRReg, tmp | 8);
319340e3fbdSmrg	else
320340e3fbdSmrg		outb(vgaCRReg, tmp & ~8);
321340e3fbdSmrg
322340e3fbdSmrg	outb(vgaCRIndex, 0x65);
323340e3fbdSmrg	outb(vgaCRReg, 0x00);	/* ! 528 */
324340e3fbdSmrg
325340e3fbdSmrg	outb(vgaCRIndex, 0x40);
326340e3fbdSmrg	outb(vgaCRReg, 0x11);
327340e3fbdSmrg	outb(vgaCRIndex, 0x55);
328340e3fbdSmrg	outb(vgaCRReg, 0x00);
329340e3fbdSmrg
330340e3fbdSmrg	switch (pScrn->depth) {
331340e3fbdSmrg	case 8:
332340e3fbdSmrg		S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 3);
333340e3fbdSmrg		S3OutIBMRGBIndReg(pScrn, IBMRGB_8bpp, 0, 0);
334340e3fbdSmrg		break;
335340e3fbdSmrg	case 15:
336340e3fbdSmrg		S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 4);
337340e3fbdSmrg		S3OutIBMRGBIndReg(pScrn, IBMRGB_16bpp, 0, 0xc0);
338340e3fbdSmrg		break;
339340e3fbdSmrg	case 16:
340340e3fbdSmrg		S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 4);
341340e3fbdSmrg		S3OutIBMRGBIndReg(pScrn, IBMRGB_16bpp, 0, 0xc2);
342340e3fbdSmrg		break;
343340e3fbdSmrg	}
344340e3fbdSmrg
345340e3fbdSmrg	outb(vgaCRIndex, 0x66);
346340e3fbdSmrg	tmp = inb(vgaCRReg) & 0xf8;
347340e3fbdSmrg	outb(vgaCRReg, tmp);
348340e3fbdSmrg
349340e3fbdSmrg	outb(vgaCRIndex, 0x58);
350340e3fbdSmrg	tmp = (inb(vgaCRReg) & 0xbf) | 0x40;
351340e3fbdSmrg	outb(vgaCRReg, tmp);
352340e3fbdSmrg
353340e3fbdSmrg	outb(vgaCRIndex, 0x67);
354340e3fbdSmrg	outb(vgaCRReg, 0x11);
355340e3fbdSmrg
356340e3fbdSmrg	switch (pScrn->bitsPerPixel) {
357340e3fbdSmrg	case 8:
358340e3fbdSmrg		tmp = 0x21;
359340e3fbdSmrg		break;
360340e3fbdSmrg	case 16:
361340e3fbdSmrg		tmp = 0x10;
362340e3fbdSmrg		break;
363340e3fbdSmrg	}
364340e3fbdSmrg	outb(vgaCRIndex, 0x6d);
365340e3fbdSmrg	outb(vgaCRReg, tmp);
366340e3fbdSmrg
367340e3fbdSmrg	outb(0x3c4, 1);
368340e3fbdSmrg	outb(0x3c5, blank);
369340e3fbdSmrg}
370340e3fbdSmrg
371340e3fbdSmrg
372340e3fbdSmrg/* hardware cursor */
373340e3fbdSmrg
374340e3fbdSmrgstatic void S3IBMRGBSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg)
375340e3fbdSmrg{
376340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
377340e3fbdSmrg	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
378340e3fbdSmrg	unsigned char tmp;
379340e3fbdSmrg
380340e3fbdSmrg	/* unlock sys regs */
381340e3fbdSmrg	outb(vgaCRIndex, 0x39);
382340e3fbdSmrg	outb(vgaCRReg, 0xa5);
383340e3fbdSmrg
384340e3fbdSmrg	outb(vgaCRIndex, 0x55);
385340e3fbdSmrg	tmp = inb(vgaCRReg) & 0xfc;
386340e3fbdSmrg	outb(vgaCRReg, tmp | 0x01);
387340e3fbdSmrg
388340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_r);
389340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, (bg & 0x00ff0000) >> 16);
390340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_g);
391340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, (bg & 0x0000ff00) >> 8);
392340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_b);
393340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, (bg & 0x000000ff));
394340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_r);
395340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, (fg & 0x00ff0000) >> 16);
396340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_g);
397340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, (fg & 0x0000ff00) >> 8);
398340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_b);
399340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, (fg & 0x000000ff));
400340e3fbdSmrg
401340e3fbdSmrg	outb(vgaCRReg, tmp);
402340e3fbdSmrg}
403340e3fbdSmrg
404340e3fbdSmrg
405340e3fbdSmrgstatic void S3IBMRGBSetCursorPosition(ScrnInfoPtr pScrn, int x, int y)
406340e3fbdSmrg{
407340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
408340e3fbdSmrg	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
409340e3fbdSmrg	unsigned char tmp;
410340e3fbdSmrg
411340e3fbdSmrg	/* unlock sys regs */
412340e3fbdSmrg	outb(vgaCRIndex, 0x39);
413340e3fbdSmrg	outb(vgaCRReg, 0xa5);
414340e3fbdSmrg
415340e3fbdSmrg	outb(vgaCRIndex, 0x55);
416340e3fbdSmrg	tmp = inb(vgaCRReg) & 0xfc;
417340e3fbdSmrg	outb(vgaCRReg, tmp | 0x01);
418340e3fbdSmrg
419340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xl);
420340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, x);
421340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xh);
422340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, x >> 8);
423340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yl);
424340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, y);
425340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yh);
426340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, y >> 8);
427340e3fbdSmrg
428340e3fbdSmrg	outb(vgaCRReg, tmp);
429340e3fbdSmrg}
430340e3fbdSmrg
431340e3fbdSmrg
432340e3fbdSmrgstatic void S3IBMRGBHideCursor(ScrnInfoPtr pScrn)
433340e3fbdSmrg{
434340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
435340e3fbdSmrg	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
436340e3fbdSmrg
437340e3fbdSmrg	/* unlock sys regs */
438340e3fbdSmrg	outb(vgaCRIndex, 0x39);
439340e3fbdSmrg	outb(vgaCRReg, 0xa5);
440340e3fbdSmrg
441340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_curs, ~3, 0x00);
442340e3fbdSmrg}
443340e3fbdSmrg
444340e3fbdSmrg
445340e3fbdSmrgstatic void S3IBMRGBShowCursor(ScrnInfoPtr pScrn)
446340e3fbdSmrg{
447340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
448340e3fbdSmrg	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
449340e3fbdSmrg	unsigned char tmp;
450340e3fbdSmrg
451340e3fbdSmrg	/* unlock sys regs */
452340e3fbdSmrg	outb(vgaCRIndex, 0x39);
453340e3fbdSmrg	outb(vgaCRReg, 0xa5);
454340e3fbdSmrg
455340e3fbdSmrg	outb(vgaCRIndex, 0x55);
456340e3fbdSmrg	tmp = (inb(vgaCRReg) & 0xdf) | 0x20;
457340e3fbdSmrg	outb(vgaCRReg, tmp);
458340e3fbdSmrg
459340e3fbdSmrg	outb(vgaCRIndex, 0x45);
460340e3fbdSmrg	tmp = inb(vgaCRReg) & ~0x20;
461340e3fbdSmrg	outb(vgaCRReg, tmp);
462340e3fbdSmrg
463340e3fbdSmrg	S3OutIBMRGBIndReg(pScrn, IBMRGB_curs, 0, 0x27);
464340e3fbdSmrg}
465340e3fbdSmrg
466340e3fbdSmrg
467340e3fbdSmrgstatic void S3IBMRGBLoadCursorImage(ScrnInfoPtr pScrn, unsigned char *image)
468340e3fbdSmrg{
469340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
470340e3fbdSmrg	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
471340e3fbdSmrg	unsigned char tmp, tmp2;
472340e3fbdSmrg	register int i;
473340e3fbdSmrg
474340e3fbdSmrg	/* unlock sys regs */
475340e3fbdSmrg	outb(vgaCRIndex, 0x39);
476340e3fbdSmrg	outb(vgaCRReg, 0xa5);
477340e3fbdSmrg
478340e3fbdSmrg	outb(vgaCRIndex, 0x55);
479340e3fbdSmrg	tmp = inb(vgaCRReg) & 0xfc;
480340e3fbdSmrg	outb(vgaCRReg, tmp | 0x01);
481340e3fbdSmrg
482340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_hot_x);
483340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, 0);
484340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_hot_y);
485340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, 0);
486340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xl);
487340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, 0xff);
488340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xh);
489340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, 0x7f);
490340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yl);
491340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, 0xff);
492340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yh);
493340e3fbdSmrg	outb(IBMRGB_INDEX_DATA, 0x7f);
494340e3fbdSmrg
495340e3fbdSmrg	tmp2 = inb(IBMRGB_INDEX_CONTROL) & 0xfe;
496340e3fbdSmrg	outb(IBMRGB_INDEX_CONTROL, tmp2 | 1);	/* enable auto increment */
497340e3fbdSmrg
498340e3fbdSmrg	outb(IBMRGB_INDEX_HIGH, (unsigned char) (IBMRGB_curs_array >> 8));
499340e3fbdSmrg	outb(IBMRGB_INDEX_LOW, (unsigned char) (IBMRGB_curs_array));
500340e3fbdSmrg
501340e3fbdSmrg	for (i=0; i<1024; i++)
502340e3fbdSmrg		outb(IBMRGB_INDEX_DATA, *image++);
503340e3fbdSmrg
504340e3fbdSmrg	outb(IBMRGB_INDEX_HIGH, 0);
505340e3fbdSmrg	outb(IBMRGB_INDEX_CONTROL, tmp2);	/* disable auto increment */
506340e3fbdSmrg	outb(vgaCRIndex, 0x55);
507340e3fbdSmrg	outb(vgaCRReg, tmp);
508340e3fbdSmrg}
509340e3fbdSmrg
510340e3fbdSmrg
511340e3fbdSmrgstatic Bool S3IBMRGBUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs)
512340e3fbdSmrg{
513b27e1915Smrg	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
514340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
515340e3fbdSmrg	return (pS3->hwCursor);
516340e3fbdSmrg}
517340e3fbdSmrg
518340e3fbdSmrg
519340e3fbdSmrgBool S3IBMRGB_CursorInit(ScreenPtr pScreen)
520340e3fbdSmrg{
521b27e1915Smrg	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
522340e3fbdSmrg	S3Ptr pS3 = S3PTR(pScrn);
523340e3fbdSmrg	xf86CursorInfoPtr pCurs;
524340e3fbdSmrg
525340e3fbdSmrg	if (!(pCurs = pS3->pCurs = xf86CreateCursorInfoRec()))
526340e3fbdSmrg		return FALSE;
527340e3fbdSmrg
528340e3fbdSmrg	pCurs->MaxWidth = 64;
529340e3fbdSmrg	pCurs->MaxHeight = 64;
530340e3fbdSmrg	pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
531340e3fbdSmrg		       HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 |
532340e3fbdSmrg		       HARDWARE_CURSOR_AND_SOURCE_WITH_MASK |
533340e3fbdSmrg		       HARDWARE_CURSOR_NIBBLE_SWAPPED |
534340e3fbdSmrg		       HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
535340e3fbdSmrg
536340e3fbdSmrg	pCurs->SetCursorColors = S3IBMRGBSetCursorColors;
537340e3fbdSmrg	pCurs->SetCursorPosition = S3IBMRGBSetCursorPosition;
538340e3fbdSmrg	pCurs->LoadCursorImage = S3IBMRGBLoadCursorImage;
539340e3fbdSmrg	pCurs->HideCursor = S3IBMRGBHideCursor;
540340e3fbdSmrg	pCurs->ShowCursor = S3IBMRGBShowCursor;
541340e3fbdSmrg	pCurs->UseHWCursor = S3IBMRGBUseHWCursor;
542340e3fbdSmrg
543340e3fbdSmrg	return xf86InitCursor(pScreen, pCurs);
544340e3fbdSmrg}
545340e3fbdSmrg
546340e3fbdSmrg
547