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 "IBM.h"
38
39#include "s3.h"
40
41
42#define IBMRGB_WRITE_ADDR           0x3C8   /* CR55 low bit == 0 */
43#define IBMRGB_RAMDAC_DATA          0x3C9   /* CR55 low bit == 0 */
44#define IBMRGB_PIXEL_MASK           0x3C6   /* CR55 low bit == 0 */
45#define IBMRGB_READ_ADDR            0x3C7   /* CR55 low bit == 0 */
46#define IBMRGB_INDEX_LOW            0x3C8   /* CR55 low bit == 1 */
47#define IBMRGB_INDEX_HIGH           0x3C9   /* CR55 low bit == 1 */
48#define IBMRGB_INDEX_DATA           0x3C6   /* CR55 low bit == 1 */
49#define IBMRGB_INDEX_CONTROL        0x3C7   /* CR55 low bit == 1 */
50
51
52static void S3OutIBMRGBIndReg(ScrnInfoPtr pScrn, CARD32 reg,
53		       unsigned char mask, unsigned char data)
54{
55	S3Ptr pS3 = S3PTR(pScrn);
56	unsigned char tmp, tmp2 = 0x00;
57	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
58
59	outb(vgaCRIndex, 0x55);
60	tmp = inb(vgaCRReg) & 0xfc;
61	outb(vgaCRReg, tmp | 0x01);
62
63	outb(IBMRGB_INDEX_LOW, reg);
64
65	if (mask != 0x00)
66		tmp2 = inb(IBMRGB_INDEX_DATA) & mask;
67	outb(IBMRGB_INDEX_DATA, tmp2 | data);
68
69	outb(vgaCRIndex, 0x55);
70	outb(vgaCRReg, tmp);
71}
72
73
74static unsigned char S3InIBMRGBIndReg(ScrnInfoPtr pScrn, CARD32 reg)
75{
76	S3Ptr pS3 = S3PTR(pScrn);
77	unsigned char tmp, ret;
78	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
79
80	outb(vgaCRIndex, 0x55);
81	tmp = inb(vgaCRReg) & 0xfc;
82	outb(vgaCRReg, tmp | 0x01);
83
84	outb(IBMRGB_INDEX_LOW, reg);
85	ret = inb(IBMRGB_INDEX_DATA);
86
87	outb(vgaCRIndex, 0x55);
88	outb(vgaCRReg, tmp);
89
90	return ret;
91}
92
93
94static void S3IBMWriteAddress(ScrnInfoPtr pScrn, CARD32 index)
95{
96	outb(IBMRGB_WRITE_ADDR, index);
97}
98
99static void S3IBMWriteData(ScrnInfoPtr pScrn, unsigned char data)
100{
101	outb(IBMRGB_INDEX_DATA, data);
102}
103
104static void S3IBMReadAddress(ScrnInfoPtr pScrn, CARD32 index)
105{
106	outb(IBMRGB_READ_ADDR, index);
107}
108
109static unsigned char S3IBMReadData(ScrnInfoPtr pScrn)
110{
111	return inb(IBMRGB_RAMDAC_DATA);
112}
113
114
115Bool S3ProbeIBMramdac(ScrnInfoPtr pScrn)
116{
117	S3Ptr pS3 = S3PTR(pScrn);
118
119	if (pS3->Chipset != PCI_CHIP_968)
120		return FALSE;
121
122	pS3->RamDacRec = RamDacCreateInfoRec();
123	pS3->RamDacRec->ReadDAC = S3InIBMRGBIndReg;
124	pS3->RamDacRec->WriteDAC = S3OutIBMRGBIndReg;
125	pS3->RamDacRec->ReadAddress = S3IBMReadAddress;
126	pS3->RamDacRec->WriteAddress = S3IBMWriteAddress;
127	pS3->RamDacRec->ReadData = S3IBMReadData;
128	pS3->RamDacRec->WriteData = S3IBMWriteData;
129	pS3->RamDacRec->LoadPalette = NULL;
130
131	if (!RamDacInit(pScrn, pS3->RamDacRec)) {
132		RamDacDestroyInfoRec(pS3->RamDacRec);
133		xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RamDacInit failed\n");
134		return FALSE;
135	}
136
137	pS3->RamDac = IBMramdacProbe(pScrn, S3IBMRamdacs);
138	if (pS3->RamDac)
139		return TRUE;
140
141	return FALSE;
142}
143
144static void S3ProgramIBMRGBClock(ScrnInfoPtr pScrn, int clk, unsigned char m,
145			  unsigned char n, unsigned char df)
146{
147	S3OutIBMRGBIndReg(pScrn, IBMRGB_misc_clock, ~1, 1);
148
149	S3OutIBMRGBIndReg(pScrn, IBMRGB_m0+2*clk, 0, (df<<6)|(m&0x3f));
150	S3OutIBMRGBIndReg(pScrn, IBMRGB_n0+2*clk, 0, n);
151
152	S3OutIBMRGBIndReg(pScrn, IBMRGB_pll_ctrl2, 0xf0, clk);
153	S3OutIBMRGBIndReg(pScrn, IBMRGB_pll_ctrl1, 0xf8, 3);
154}
155
156
157static void S3IBMRGBSetClock(ScrnInfoPtr pScrn, long freq, int clk, long dacspeed,
158		      long fref)
159{
160	volatile double ffreq, ffref;
161	volatile int df, n, m, max_n, min_df;
162	volatile int best_m=69, best_n=17, best_df=0;
163	volatile double diff, mindiff;
164
165#define FREQ_MIN	16250
166#define FREQ_MAX	dacspeed
167
168	if (freq < FREQ_MIN)
169		ffreq = FREQ_MIN / 1000.0;
170	else if (freq > FREQ_MAX)
171		ffreq = FREQ_MAX / 1000.0;
172	else
173		ffreq = freq / 1000.0;
174
175	ffref = fref / 1e3;
176
177	ffreq /= ffref;
178	ffreq *= 16;
179	mindiff = ffreq;
180
181	if (freq <= dacspeed/4)
182		min_df = 0;
183	else if (freq <= dacspeed/2)
184		min_df = 1;
185	else
186		min_df = 2;
187
188	for (df=0; df<4; df++) {
189		ffreq /= 2;
190		mindiff /= 2;
191		if (df < min_df)
192			continue;
193
194		if (df < 3)
195			max_n = fref / 1000 / 2;
196		else
197			max_n = fref / 1000;
198		if (max_n > 31)
199			max_n = 31;
200
201		for (n=2; n <= max_n; n++) {
202			m = (int)(ffreq * n + 0.5) - 65;
203			if (m < 0)
204				m = 0;
205			else if (m > 63)
206				m = 63;
207			diff = (m+65.0)/n-ffreq;
208			if (diff < 0)
209				diff = -diff;
210			if (diff < mindiff) {
211				mindiff = diff;
212				best_n = n;
213				best_m = m;
214				best_df = df;
215			}
216		}
217	}
218
219	S3ProgramIBMRGBClock(pScrn, clk, best_m, best_n, best_df);
220}
221
222void S3IBMRGB_Restore(ScrnInfoPtr pScrn)
223{
224	S3Ptr pS3 = S3PTR(pScrn);
225	S3RegPtr restore = &pS3->SavedRegs;
226	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
227	int i;
228
229	for(i=0; i<0x100; i++)
230		S3OutIBMRGBIndReg(pScrn, i, 0, restore->dacregs[i]);
231
232	outb(vgaCRIndex, 0x22);
233	outb(vgaCRReg, restore->dacregs[0x100]);
234}
235
236
237void S3IBMRGB_Save(ScrnInfoPtr pScrn)
238{
239	S3Ptr pS3 = S3PTR(pScrn);
240	S3RegPtr save = &pS3->SavedRegs;
241	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
242	int i;
243
244	for (i=0; i<0x100; i++)
245		save->dacregs[i] = S3InIBMRGBIndReg(pScrn, i);
246
247	outb(vgaCRIndex, 0x22);
248	save->dacregs[0x100] = inb(vgaCRReg);
249}
250
251
252void S3IBMRGB_PreInit(ScrnInfoPtr pScrn)
253{
254	S3Ptr pS3 = S3PTR(pScrn);
255	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
256	unsigned char cr55, tmp;
257
258	outb(vgaCRIndex, 0x43);
259	tmp = inb(vgaCRReg);
260	outb(vgaCRReg, tmp & ~0x02);
261
262	outb(vgaCRIndex, 0x55);
263	cr55 = inb(vgaCRReg);
264	outb(vgaCRReg, (cr55 & ~0x03) | 0x01);	/* set rs2 */
265
266	tmp = inb(IBMRGB_INDEX_CONTROL);
267	outb(IBMRGB_INDEX_CONTROL, tmp & ~1);
268	outb(IBMRGB_INDEX_HIGH, 0);
269
270	outb(vgaCRIndex, 0x55);
271	outb(vgaCRReg, cr55 & ~0x03);
272
273	{
274		int m, n, df, mclk=0;
275
276		m = S3InIBMRGBIndReg(pScrn, IBMRGB_sysclk_vco_div);
277		n = S3InIBMRGBIndReg(pScrn, IBMRGB_sysclk_ref_div) & 0x1f;
278		df = m >> 6;
279		m &= 0x3f;
280		if (!n) {
281			m = 0;
282			n = 1;
283		}
284		mclk = ((pS3->RefClock*100 * (m+65)) / n / (8 >> df) + 50) / 100;
285		pS3->mclk = mclk;
286		xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f MHz\n",
287			   mclk / 1000.0);
288	}
289}
290
291
292void S3IBMRGB_Init(ScrnInfoPtr pScrn, DisplayModePtr mode)
293{
294	S3Ptr pS3 = S3PTR(pScrn);
295	unsigned char tmp, blank;
296	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
297
298	S3IBMRGBSetClock(pScrn, mode->Clock, 2, pS3->MaxClock,
299			 pS3->RefClock);
300
301	outb(0x3c4, 1);
302	blank = inb(0x3c5);
303	outb(0x3c5, blank | 0x20);
304
305	S3OutIBMRGBIndReg(pScrn, IBMRGB_misc_clock, 0xf0, 0x03);
306	S3OutIBMRGBIndReg(pScrn, IBMRGB_sync, 0, 0);
307	S3OutIBMRGBIndReg(pScrn, IBMRGB_hsync_pos, 0, 0);
308	S3OutIBMRGBIndReg(pScrn, IBMRGB_pwr_mgmt, 0, 0);
309	S3OutIBMRGBIndReg(pScrn, IBMRGB_dac_op, ~8, 0);
310	S3OutIBMRGBIndReg(pScrn, IBMRGB_dac_op, ~2, 2);
311	S3OutIBMRGBIndReg(pScrn, IBMRGB_pal_ctrl, 0, 0);
312	S3OutIBMRGBIndReg(pScrn, IBMRGB_misc1, ~0x43, 1);
313	S3OutIBMRGBIndReg(pScrn, IBMRGB_misc2, 0, 0x47);
314
315	outb(vgaCRIndex, 0x22);
316	tmp = inb(vgaCRReg);
317	if (pS3->s3Bpp == 1)
318		outb(vgaCRReg, tmp | 8);
319	else
320		outb(vgaCRReg, tmp & ~8);
321
322	outb(vgaCRIndex, 0x65);
323	outb(vgaCRReg, 0x00);	/* ! 528 */
324
325	outb(vgaCRIndex, 0x40);
326	outb(vgaCRReg, 0x11);
327	outb(vgaCRIndex, 0x55);
328	outb(vgaCRReg, 0x00);
329
330	switch (pScrn->depth) {
331	case 8:
332		S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 3);
333		S3OutIBMRGBIndReg(pScrn, IBMRGB_8bpp, 0, 0);
334		break;
335	case 15:
336		S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 4);
337		S3OutIBMRGBIndReg(pScrn, IBMRGB_16bpp, 0, 0xc0);
338		break;
339	case 16:
340		S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 4);
341		S3OutIBMRGBIndReg(pScrn, IBMRGB_16bpp, 0, 0xc2);
342		break;
343	}
344
345	outb(vgaCRIndex, 0x66);
346	tmp = inb(vgaCRReg) & 0xf8;
347	outb(vgaCRReg, tmp);
348
349	outb(vgaCRIndex, 0x58);
350	tmp = (inb(vgaCRReg) & 0xbf) | 0x40;
351	outb(vgaCRReg, tmp);
352
353	outb(vgaCRIndex, 0x67);
354	outb(vgaCRReg, 0x11);
355
356	switch (pScrn->bitsPerPixel) {
357	case 8:
358		tmp = 0x21;
359		break;
360	case 16:
361		tmp = 0x10;
362		break;
363	}
364	outb(vgaCRIndex, 0x6d);
365	outb(vgaCRReg, tmp);
366
367	outb(0x3c4, 1);
368	outb(0x3c5, blank);
369}
370
371
372/* hardware cursor */
373
374static void S3IBMRGBSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg)
375{
376	S3Ptr pS3 = S3PTR(pScrn);
377	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
378	unsigned char tmp;
379
380	/* unlock sys regs */
381	outb(vgaCRIndex, 0x39);
382	outb(vgaCRReg, 0xa5);
383
384	outb(vgaCRIndex, 0x55);
385	tmp = inb(vgaCRReg) & 0xfc;
386	outb(vgaCRReg, tmp | 0x01);
387
388	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_r);
389	outb(IBMRGB_INDEX_DATA, (bg & 0x00ff0000) >> 16);
390	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_g);
391	outb(IBMRGB_INDEX_DATA, (bg & 0x0000ff00) >> 8);
392	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_b);
393	outb(IBMRGB_INDEX_DATA, (bg & 0x000000ff));
394	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_r);
395	outb(IBMRGB_INDEX_DATA, (fg & 0x00ff0000) >> 16);
396	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_g);
397	outb(IBMRGB_INDEX_DATA, (fg & 0x0000ff00) >> 8);
398	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_b);
399	outb(IBMRGB_INDEX_DATA, (fg & 0x000000ff));
400
401	outb(vgaCRReg, tmp);
402}
403
404
405static void S3IBMRGBSetCursorPosition(ScrnInfoPtr pScrn, int x, int y)
406{
407	S3Ptr pS3 = S3PTR(pScrn);
408	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
409	unsigned char tmp;
410
411	/* unlock sys regs */
412	outb(vgaCRIndex, 0x39);
413	outb(vgaCRReg, 0xa5);
414
415	outb(vgaCRIndex, 0x55);
416	tmp = inb(vgaCRReg) & 0xfc;
417	outb(vgaCRReg, tmp | 0x01);
418
419	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xl);
420	outb(IBMRGB_INDEX_DATA, x);
421	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xh);
422	outb(IBMRGB_INDEX_DATA, x >> 8);
423	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yl);
424	outb(IBMRGB_INDEX_DATA, y);
425	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yh);
426	outb(IBMRGB_INDEX_DATA, y >> 8);
427
428	outb(vgaCRReg, tmp);
429}
430
431
432static void S3IBMRGBHideCursor(ScrnInfoPtr pScrn)
433{
434	S3Ptr pS3 = S3PTR(pScrn);
435	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
436
437	/* unlock sys regs */
438	outb(vgaCRIndex, 0x39);
439	outb(vgaCRReg, 0xa5);
440
441	S3OutIBMRGBIndReg(pScrn, IBMRGB_curs, ~3, 0x00);
442}
443
444
445static void S3IBMRGBShowCursor(ScrnInfoPtr pScrn)
446{
447	S3Ptr pS3 = S3PTR(pScrn);
448	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
449	unsigned char tmp;
450
451	/* unlock sys regs */
452	outb(vgaCRIndex, 0x39);
453	outb(vgaCRReg, 0xa5);
454
455	outb(vgaCRIndex, 0x55);
456	tmp = (inb(vgaCRReg) & 0xdf) | 0x20;
457	outb(vgaCRReg, tmp);
458
459	outb(vgaCRIndex, 0x45);
460	tmp = inb(vgaCRReg) & ~0x20;
461	outb(vgaCRReg, tmp);
462
463	S3OutIBMRGBIndReg(pScrn, IBMRGB_curs, 0, 0x27);
464}
465
466
467static void S3IBMRGBLoadCursorImage(ScrnInfoPtr pScrn, unsigned char *image)
468{
469	S3Ptr pS3 = S3PTR(pScrn);
470	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
471	unsigned char tmp, tmp2;
472	register int i;
473
474	/* unlock sys regs */
475	outb(vgaCRIndex, 0x39);
476	outb(vgaCRReg, 0xa5);
477
478	outb(vgaCRIndex, 0x55);
479	tmp = inb(vgaCRReg) & 0xfc;
480	outb(vgaCRReg, tmp | 0x01);
481
482	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_hot_x);
483	outb(IBMRGB_INDEX_DATA, 0);
484	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_hot_y);
485	outb(IBMRGB_INDEX_DATA, 0);
486	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xl);
487	outb(IBMRGB_INDEX_DATA, 0xff);
488	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xh);
489	outb(IBMRGB_INDEX_DATA, 0x7f);
490	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yl);
491	outb(IBMRGB_INDEX_DATA, 0xff);
492	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yh);
493	outb(IBMRGB_INDEX_DATA, 0x7f);
494
495	tmp2 = inb(IBMRGB_INDEX_CONTROL) & 0xfe;
496	outb(IBMRGB_INDEX_CONTROL, tmp2 | 1);	/* enable auto increment */
497
498	outb(IBMRGB_INDEX_HIGH, (unsigned char) (IBMRGB_curs_array >> 8));
499	outb(IBMRGB_INDEX_LOW, (unsigned char) (IBMRGB_curs_array));
500
501	for (i=0; i<1024; i++)
502		outb(IBMRGB_INDEX_DATA, *image++);
503
504	outb(IBMRGB_INDEX_HIGH, 0);
505	outb(IBMRGB_INDEX_CONTROL, tmp2);	/* disable auto increment */
506	outb(vgaCRIndex, 0x55);
507	outb(vgaCRReg, tmp);
508}
509
510
511static Bool S3IBMRGBUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs)
512{
513	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
514	S3Ptr pS3 = S3PTR(pScrn);
515	return (pS3->hwCursor);
516}
517
518
519Bool S3IBMRGB_CursorInit(ScreenPtr pScreen)
520{
521	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
522	S3Ptr pS3 = S3PTR(pScrn);
523	xf86CursorInfoPtr pCurs;
524
525	if (!(pCurs = pS3->pCurs = xf86CreateCursorInfoRec()))
526		return FALSE;
527
528	pCurs->MaxWidth = 64;
529	pCurs->MaxHeight = 64;
530	pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
531		       HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 |
532		       HARDWARE_CURSOR_AND_SOURCE_WITH_MASK |
533		       HARDWARE_CURSOR_NIBBLE_SWAPPED |
534		       HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
535
536	pCurs->SetCursorColors = S3IBMRGBSetCursorColors;
537	pCurs->SetCursorPosition = S3IBMRGBSetCursorPosition;
538	pCurs->LoadCursorImage = S3IBMRGBLoadCursorImage;
539	pCurs->HideCursor = S3IBMRGBHideCursor;
540	pCurs->ShowCursor = S3IBMRGBShowCursor;
541	pCurs->UseHWCursor = S3IBMRGBUseHWCursor;
542
543	return xf86InitCursor(pScreen, pCurs);
544}
545
546
547