s3_IBMRGB.c revision b27e1915
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, fdacspeed, 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	fdacspeed = dacspeed / 1e3;
176	ffref = fref / 1e3;
177
178	ffreq /= ffref;
179	ffreq *= 16;
180	mindiff = ffreq;
181
182	if (freq <= dacspeed/4)
183		min_df = 0;
184	else if (freq <= dacspeed/2)
185		min_df = 1;
186	else
187		min_df = 2;
188
189	for (df=0; df<4; df++) {
190		ffreq /= 2;
191		mindiff /= 2;
192		if (df < min_df)
193			continue;
194
195		if (df < 3)
196			max_n = fref / 1000 / 2;
197		else
198			max_n = fref / 1000;
199		if (max_n > 31)
200			max_n = 31;
201
202		for (n=2; n <= max_n; n++) {
203			m = (int)(ffreq * n + 0.5) - 65;
204			if (m < 0)
205				m = 0;
206			else if (m > 63)
207				m = 63;
208			diff = (m+65.0)/n-ffreq;
209			if (diff < 0)
210				diff = -diff;
211			if (diff < mindiff) {
212				mindiff = diff;
213				best_n = n;
214				best_m = m;
215				best_df = df;
216			}
217		}
218	}
219
220	S3ProgramIBMRGBClock(pScrn, clk, best_m, best_n, best_df);
221}
222
223void S3IBMRGB_Restore(ScrnInfoPtr pScrn)
224{
225	S3Ptr pS3 = S3PTR(pScrn);
226	S3RegPtr restore = &pS3->SavedRegs;
227	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
228	int i;
229
230	for(i=0; i<0x100; i++)
231		S3OutIBMRGBIndReg(pScrn, i, 0, restore->dacregs[i]);
232
233	outb(vgaCRIndex, 0x22);
234	outb(vgaCRReg, restore->dacregs[0x100]);
235}
236
237
238void S3IBMRGB_Save(ScrnInfoPtr pScrn)
239{
240	S3Ptr pS3 = S3PTR(pScrn);
241	S3RegPtr save = &pS3->SavedRegs;
242	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
243	int i;
244
245	for (i=0; i<0x100; i++)
246		save->dacregs[i] = S3InIBMRGBIndReg(pScrn, i);
247
248	outb(vgaCRIndex, 0x22);
249	save->dacregs[0x100] = inb(vgaCRReg);
250}
251
252
253void S3IBMRGB_PreInit(ScrnInfoPtr pScrn)
254{
255	S3Ptr pS3 = S3PTR(pScrn);
256	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
257	unsigned char cr55, tmp;
258
259	outb(vgaCRIndex, 0x43);
260	tmp = inb(vgaCRReg);
261	outb(vgaCRReg, tmp & ~0x02);
262
263	outb(vgaCRIndex, 0x55);
264	cr55 = inb(vgaCRReg);
265	outb(vgaCRReg, (cr55 & ~0x03) | 0x01);	/* set rs2 */
266
267	tmp = inb(IBMRGB_INDEX_CONTROL);
268	outb(IBMRGB_INDEX_CONTROL, tmp & ~1);
269	outb(IBMRGB_INDEX_HIGH, 0);
270
271	outb(vgaCRIndex, 0x55);
272	outb(vgaCRReg, cr55 & ~0x03);
273
274	{
275		int m, n, df, mclk=0;
276
277		m = S3InIBMRGBIndReg(pScrn, IBMRGB_sysclk_vco_div);
278		n = S3InIBMRGBIndReg(pScrn, IBMRGB_sysclk_ref_div) & 0x1f;
279		df = m >> 6;
280		m &= 0x3f;
281		if (!n) {
282			m = 0;
283			n = 1;
284		}
285		mclk = ((pS3->RefClock*100 * (m+65)) / n / (8 >> df) + 50) / 100;
286		pS3->mclk = mclk;
287		xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f MHz\n",
288			   mclk / 1000.0);
289	}
290}
291
292
293void S3IBMRGB_Init(ScrnInfoPtr pScrn, DisplayModePtr mode)
294{
295	S3Ptr pS3 = S3PTR(pScrn);
296	unsigned char tmp, blank;
297	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
298
299	S3IBMRGBSetClock(pScrn, mode->Clock, 2, pS3->MaxClock,
300			 pS3->RefClock);
301
302	outb(0x3c4, 1);
303	blank = inb(0x3c5);
304	outb(0x3c5, blank | 0x20);
305
306	S3OutIBMRGBIndReg(pScrn, IBMRGB_misc_clock, 0xf0, 0x03);
307	S3OutIBMRGBIndReg(pScrn, IBMRGB_sync, 0, 0);
308	S3OutIBMRGBIndReg(pScrn, IBMRGB_hsync_pos, 0, 0);
309	S3OutIBMRGBIndReg(pScrn, IBMRGB_pwr_mgmt, 0, 0);
310	S3OutIBMRGBIndReg(pScrn, IBMRGB_dac_op, ~8, 0);
311	S3OutIBMRGBIndReg(pScrn, IBMRGB_dac_op, ~2, 2);
312	S3OutIBMRGBIndReg(pScrn, IBMRGB_pal_ctrl, 0, 0);
313	S3OutIBMRGBIndReg(pScrn, IBMRGB_misc1, ~0x43, 1);
314	S3OutIBMRGBIndReg(pScrn, IBMRGB_misc2, 0, 0x47);
315
316	outb(vgaCRIndex, 0x22);
317	tmp = inb(vgaCRReg);
318	if (pS3->s3Bpp == 1)
319		outb(vgaCRReg, tmp | 8);
320	else
321		outb(vgaCRReg, tmp & ~8);
322
323	outb(vgaCRIndex, 0x65);
324	outb(vgaCRReg, 0x00);	/* ! 528 */
325
326	outb(vgaCRIndex, 0x40);
327	outb(vgaCRReg, 0x11);
328	outb(vgaCRIndex, 0x55);
329	outb(vgaCRReg, 0x00);
330
331	switch (pScrn->depth) {
332	case 8:
333		S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 3);
334		S3OutIBMRGBIndReg(pScrn, IBMRGB_8bpp, 0, 0);
335		break;
336	case 15:
337		S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 4);
338		S3OutIBMRGBIndReg(pScrn, IBMRGB_16bpp, 0, 0xc0);
339		break;
340	case 16:
341		S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 4);
342		S3OutIBMRGBIndReg(pScrn, IBMRGB_16bpp, 0, 0xc2);
343		break;
344	}
345
346	outb(vgaCRIndex, 0x66);
347	tmp = inb(vgaCRReg) & 0xf8;
348	outb(vgaCRReg, tmp);
349
350	outb(vgaCRIndex, 0x58);
351	tmp = (inb(vgaCRReg) & 0xbf) | 0x40;
352	outb(vgaCRReg, tmp);
353
354	outb(vgaCRIndex, 0x67);
355	outb(vgaCRReg, 0x11);
356
357	switch (pScrn->bitsPerPixel) {
358	case 8:
359		tmp = 0x21;
360		break;
361	case 16:
362		tmp = 0x10;
363		break;
364	}
365	outb(vgaCRIndex, 0x6d);
366	outb(vgaCRReg, tmp);
367
368	outb(0x3c4, 1);
369	outb(0x3c5, blank);
370}
371
372
373/* hardware cursor */
374
375static void S3IBMRGBSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg)
376{
377	S3Ptr pS3 = S3PTR(pScrn);
378	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
379	unsigned char tmp;
380
381	/* unlock sys regs */
382	outb(vgaCRIndex, 0x39);
383	outb(vgaCRReg, 0xa5);
384
385	outb(vgaCRIndex, 0x55);
386	tmp = inb(vgaCRReg) & 0xfc;
387	outb(vgaCRReg, tmp | 0x01);
388
389	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_r);
390	outb(IBMRGB_INDEX_DATA, (bg & 0x00ff0000) >> 16);
391	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_g);
392	outb(IBMRGB_INDEX_DATA, (bg & 0x0000ff00) >> 8);
393	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_b);
394	outb(IBMRGB_INDEX_DATA, (bg & 0x000000ff));
395	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_r);
396	outb(IBMRGB_INDEX_DATA, (fg & 0x00ff0000) >> 16);
397	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_g);
398	outb(IBMRGB_INDEX_DATA, (fg & 0x0000ff00) >> 8);
399	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_b);
400	outb(IBMRGB_INDEX_DATA, (fg & 0x000000ff));
401
402	outb(vgaCRReg, tmp);
403}
404
405
406static void S3IBMRGBSetCursorPosition(ScrnInfoPtr pScrn, int x, int y)
407{
408	S3Ptr pS3 = S3PTR(pScrn);
409	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
410	unsigned char tmp;
411
412	/* unlock sys regs */
413	outb(vgaCRIndex, 0x39);
414	outb(vgaCRReg, 0xa5);
415
416	outb(vgaCRIndex, 0x55);
417	tmp = inb(vgaCRReg) & 0xfc;
418	outb(vgaCRReg, tmp | 0x01);
419
420	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xl);
421	outb(IBMRGB_INDEX_DATA, x);
422	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xh);
423	outb(IBMRGB_INDEX_DATA, x >> 8);
424	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yl);
425	outb(IBMRGB_INDEX_DATA, y);
426	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yh);
427	outb(IBMRGB_INDEX_DATA, y >> 8);
428
429	outb(vgaCRReg, tmp);
430}
431
432
433static void S3IBMRGBHideCursor(ScrnInfoPtr pScrn)
434{
435	S3Ptr pS3 = S3PTR(pScrn);
436	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
437
438	/* unlock sys regs */
439	outb(vgaCRIndex, 0x39);
440	outb(vgaCRReg, 0xa5);
441
442	S3OutIBMRGBIndReg(pScrn, IBMRGB_curs, ~3, 0x00);
443}
444
445
446static void S3IBMRGBShowCursor(ScrnInfoPtr pScrn)
447{
448	S3Ptr pS3 = S3PTR(pScrn);
449	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
450	unsigned char tmp;
451
452	/* unlock sys regs */
453	outb(vgaCRIndex, 0x39);
454	outb(vgaCRReg, 0xa5);
455
456	outb(vgaCRIndex, 0x55);
457	tmp = (inb(vgaCRReg) & 0xdf) | 0x20;
458	outb(vgaCRReg, tmp);
459
460	outb(vgaCRIndex, 0x45);
461	tmp = inb(vgaCRReg) & ~0x20;
462	outb(vgaCRReg, tmp);
463
464	S3OutIBMRGBIndReg(pScrn, IBMRGB_curs, 0, 0x27);
465}
466
467
468static void S3IBMRGBLoadCursorImage(ScrnInfoPtr pScrn, unsigned char *image)
469{
470	S3Ptr pS3 = S3PTR(pScrn);
471	int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
472	unsigned char tmp, tmp2;
473	register int i;
474
475	/* unlock sys regs */
476	outb(vgaCRIndex, 0x39);
477	outb(vgaCRReg, 0xa5);
478
479	outb(vgaCRIndex, 0x55);
480	tmp = inb(vgaCRReg) & 0xfc;
481	outb(vgaCRReg, tmp | 0x01);
482
483	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_hot_x);
484	outb(IBMRGB_INDEX_DATA, 0);
485	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_hot_y);
486	outb(IBMRGB_INDEX_DATA, 0);
487	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xl);
488	outb(IBMRGB_INDEX_DATA, 0xff);
489	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xh);
490	outb(IBMRGB_INDEX_DATA, 0x7f);
491	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yl);
492	outb(IBMRGB_INDEX_DATA, 0xff);
493	outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yh);
494	outb(IBMRGB_INDEX_DATA, 0x7f);
495
496	tmp2 = inb(IBMRGB_INDEX_CONTROL) & 0xfe;
497	outb(IBMRGB_INDEX_CONTROL, tmp2 | 1);	/* enable auto increment */
498
499	outb(IBMRGB_INDEX_HIGH, (unsigned char) (IBMRGB_curs_array >> 8));
500	outb(IBMRGB_INDEX_LOW, (unsigned char) (IBMRGB_curs_array));
501
502	for (i=0; i<1024; i++)
503		outb(IBMRGB_INDEX_DATA, *image++);
504
505	outb(IBMRGB_INDEX_HIGH, 0);
506	outb(IBMRGB_INDEX_CONTROL, tmp2);	/* disable auto increment */
507	outb(vgaCRIndex, 0x55);
508	outb(vgaCRReg, tmp);
509}
510
511
512static Bool S3IBMRGBUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs)
513{
514	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
515	S3Ptr pS3 = S3PTR(pScrn);
516	return (pS3->hwCursor);
517}
518
519
520Bool S3IBMRGB_CursorInit(ScreenPtr pScreen)
521{
522	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
523	S3Ptr pS3 = S3PTR(pScrn);
524	xf86CursorInfoPtr pCurs;
525
526	if (!(pCurs = pS3->pCurs = xf86CreateCursorInfoRec()))
527		return FALSE;
528
529	pCurs->MaxWidth = 64;
530	pCurs->MaxHeight = 64;
531	pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
532		       HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 |
533		       HARDWARE_CURSOR_AND_SOURCE_WITH_MASK |
534		       HARDWARE_CURSOR_NIBBLE_SWAPPED |
535		       HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
536
537	pCurs->SetCursorColors = S3IBMRGBSetCursorColors;
538	pCurs->SetCursorPosition = S3IBMRGBSetCursorPosition;
539	pCurs->LoadCursorImage = S3IBMRGBLoadCursorImage;
540	pCurs->HideCursor = S3IBMRGBHideCursor;
541	pCurs->ShowCursor = S3IBMRGBShowCursor;
542	pCurs->UseHWCursor = S3IBMRGBUseHWCursor;
543
544	return xf86InitCursor(pScreen, pCurs);
545}
546
547
548