1
2#ifdef HAVE_CONFIG_H
3#include "config.h"
4#endif
5
6#include "savage_driver.h"
7#include "savage_vbe.h"
8
9#define iabs(a)	((int)(a)>0?(a):(-(a)))
10
11#if X_BYTE_ORDER == X_LITTLE_ENDIAN
12#define B_O16(x)  (x)
13#define B_O32(x)  (x)
14#else
15#define B_O16(x)  ((((x) & 0xff) << 8) | (((x) & 0xff) >> 8))
16#define B_O32(x)  ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) \
17                  | (((x) & 0xff0000) >> 8) | (((x) & 0xff000000) >> 24))
18#endif
19#define L_ADD(x)  (B_O32(x) & 0xffff) + ((B_O32(x) >> 12) & 0xffff00)
20
21static int SavageGetDevice( SavagePtr psav );
22/*static int SavageGetTVType( SavagePtr psav );*/
23void SavageSetVESAModeCrtc1( SavagePtr psav, int n, int Refresh );
24void SavageSetVESAModeCrtc2( SavagePtr psav, int n, int Refresh );
25
26static void
27SavageClearVM86Regs( xf86Int10InfoPtr pInt )
28{
29    pInt->ax = 0;
30    pInt->bx = 0;
31    pInt->cx = 0;
32    pInt->dx = 0;
33    pInt->si = 0;
34    pInt->di = 0;
35    pInt->es = 0xc000;
36    pInt->num = 0x10;
37}
38
39void
40SavageSetTextMode( SavagePtr psav )
41{
42    /* Restore display device if changed. */
43    if( psav->iDevInfo != psav->iDevInfoPrim ) {
44	SavageClearVM86Regs( psav->pVbe->pInt10 );
45	psav->pVbe->pInt10->ax = 0x4f14;
46	psav->pVbe->pInt10->bx = 0x0003;
47	psav->pVbe->pInt10->cx = psav->iDevInfoPrim;
48	xf86ExecX86int10( psav->pVbe->pInt10 );
49    }
50
51    SavageClearVM86Regs( psav->pVbe->pInt10 );
52
53    psav->pVbe->pInt10->ax = 0x83;
54
55    xf86ExecX86int10( psav->pVbe->pInt10 );
56}
57
58void
59SavageSetVESAModeCrtc1(SavagePtr psav, int n, int refresh)
60{
61    unsigned char byte;
62
63    xf86Msg(X_INFO,"SavageSetVESAModeCrtc1:mode=0x%x,refresh=%dHZ\n",n,refresh);
64
65    SavageClearVM86Regs(psav->pVbe->pInt10);
66
67    /* set active displays. */
68    psav->pVbe->pInt10->ax = S3_EXTBIOS_INFO;
69    psav->pVbe->pInt10->bx = S3_SET_ACTIVE_DISP;
70    if (psav->TvOn)
71    	psav->pVbe->pInt10->cx = 0x87; /* lcd, tv, crt, duoview */
72    else
73    	psav->pVbe->pInt10->cx = 0x83; /* lcd, crt, duoview */
74    xf86ExecX86int10(psav->pVbe->pInt10);
75
76    SavageClearVM86Regs(psav->pVbe->pInt10);
77
78    /* Establish the refresh rate for this mode. */
79    psav->pVbe->pInt10->ax = S3_EXTBIOS_INFO;
80    psav->pVbe->pInt10->bx = S3_SET_REFRESH;
81    psav->pVbe->pInt10->cx = n & 0x1ff;
82    psav->pVbe->pInt10->di = refresh & 0xffff;
83    xf86ExecX86int10(psav->pVbe->pInt10);
84
85    /* SR01:turn off screen */
86    OUTREG8 (SEQ_ADDRESS_REG,0x01);
87    byte = INREG8(SEQ_DATA_REG) | 0x20;
88    OUTREG8(SEQ_DATA_REG,byte);
89
90    psav->pVbe->pInt10->ax = BIOS_SET_VBE_MODE;
91    psav->pVbe->pInt10->bx = n;
92    xf86ExecX86int10(psav->pVbe->pInt10);
93
94}
95
96void
97SavageSetVESAModeCrtc2( SavagePtr psav, int n, int refresh )
98{
99
100    xf86Msg(X_INFO,"SavageSetVESAModeCrtc2:mode=0x%x,refresh=%dHZ\n",n,refresh);
101
102    SavageClearVM86Regs(psav->pVbe->pInt10);
103
104    UnLockExtRegs();
105
106    psav->pVbe->pInt10->ax = S3_EXTBIOS_INFO;
107    psav->pVbe->pInt10->bx = S3_ALT_SET_ACTIVE_DISP;
108    if (psav->TvOn)
109    	psav->pVbe->pInt10->cx = 0x87; /* lcd, tv, crt, duoview */
110    else
111    	psav->pVbe->pInt10->cx = 0x83; /* lcd, crt, duoview */
112    psav->pVbe->pInt10->dx = n & 0x1ff;
113    psav->pVbe->pInt10->di = refresh & 0xffff;
114    xf86ExecX86int10(psav->pVbe->pInt10);
115
116}
117
118void
119SavageSetVESAMode( SavagePtr psav, int n, int Refresh )
120{
121    int iDevInfo;
122    static int iCount = 0;
123
124    if (psav->IsSecondary) {
125        SavageSetVESAModeCrtc2(psav, n, Refresh);
126	return;
127    }
128    if (psav->IsPrimary) {
129        SavageSetVESAModeCrtc1(psav, n, Refresh);
130	return;
131    }
132
133    /* Get current display device status. */
134
135    iDevInfo = SavageGetDevice(psav);
136    psav->iDevInfo = iDevInfo;
137    if( !iCount++ )
138	psav->iDevInfoPrim = psav->iDevInfo;
139    if( psav->CrtOnly )
140	psav->iDevInfo = CRT_ACTIVE;
141    if( psav->TvOn )
142	psav->iDevInfo = TV_ACTIVE;
143
144    /* Establish the refresh rate for this mode. */
145
146    SavageClearVM86Regs( psav->pVbe->pInt10 );
147    psav->pVbe->pInt10->ax = 0x4f14;	/* S3 extensions */
148    psav->pVbe->pInt10->bx = 0x0001;	/* Set default refresh rate */
149    psav->pVbe->pInt10->cx = n & 0x3fff;
150    psav->pVbe->pInt10->di = Refresh & 0xffff;
151
152    xf86ExecX86int10( psav->pVbe->pInt10 );
153
154    /* Set TV type if TV is on. */
155    if( psav->TvOn ) {
156	SavageClearVM86Regs( psav->pVbe->pInt10 );
157	psav->pVbe->pInt10->ax = 0x4f14;	/* S3 extensions */
158	psav->pVbe->pInt10->bx = 0x0007;	/* TV extensions */
159	psav->pVbe->pInt10->cx = psav->PAL ? 0x08 : 0x04;
160	psav->pVbe->pInt10->dx = 0x0c;
161	xf86ExecX86int10( psav->pVbe->pInt10 );
162    }
163
164    /* Manipulate output device set. */
165    if( psav->iDevInfo != iDevInfo ) {
166	SavageClearVM86Regs( psav->pVbe->pInt10 );
167	psav->pVbe->pInt10->ax = 0x4f14;	/* S3 extensions */
168	psav->pVbe->pInt10->bx = 0x0003;	/* set active devices */
169	psav->pVbe->pInt10->cx = psav->iDevInfo;
170	xf86ExecX86int10( psav->pVbe->pInt10 );
171
172	/* Re-fetch actual device set. */
173	psav->iDevInfo = SavageGetDevice( psav );
174	iDevInfo = psav->iDevInfo;
175	psav->CrtOnly = (iDevInfo == 1);
176	psav->TvOn = !!(iDevInfo & 4);
177    }
178
179    /* Now, make this mode current. */
180
181    if( xf86LoaderCheckSymbol( "VBESetVBEMode" ) )
182    {
183	if( !VBESetVBEMode( psav->pVbe, n, NULL ) )
184	{
185	    ErrorF("Set video mode failed\n");
186	}
187    }
188}
189
190void
191SavageSetPanelEnabled( SavagePtr psav, Bool active )
192{
193    int iDevInfo;
194    if( !psav->PanelX )
195	return; /* no panel */
196    iDevInfo = SavageGetDevice( psav );
197    if( active )
198	iDevInfo |= LCD_ACTIVE;
199    else
200	iDevInfo &= ~LCD_ACTIVE;
201    SavageClearVM86Regs( psav->pVbe->pInt10 );
202    psav->pVbe->pInt10->ax = 0x4f14;	/* S3 extensions */
203    psav->pVbe->pInt10->bx = 0x0003;	/* set active devices */
204    psav->pVbe->pInt10->cx = iDevInfo;
205    xf86ExecX86int10( psav->pVbe->pInt10 );
206}
207
208/* Function to get supported device list. */
209
210static int SavageGetDevice( SavagePtr psav )
211{
212    SavageClearVM86Regs( psav->pVbe->pInt10 );
213    psav->pVbe->pInt10->ax = 0x4f14;	/* S3 extensions */
214    psav->pVbe->pInt10->bx = 0x0103;	/* get active devices */
215
216    xf86ExecX86int10( psav->pVbe->pInt10 );
217
218    return ((psav->pVbe->pInt10->cx) & 0xf);
219}
220
221
222void
223SavageFreeBIOSModeTable( SavagePtr psav, SavageModeTablePtr* ppTable )
224{
225    int i;
226    SavageModeEntryPtr pMode = (*ppTable)->Modes;
227
228    for( i = (*ppTable)->NumModes; i--; )
229    {
230	if( pMode->RefreshRate )
231	{
232	    free( pMode->RefreshRate );
233	    pMode->RefreshRate = NULL;
234	}
235	pMode++;
236    }
237
238    free( *ppTable );
239}
240
241
242SavageModeTablePtr
243SavageGetBIOSModeTable( SavagePtr psav, int iDepth )
244{
245    VbeInfoBlock *vbe;
246    int nModes;
247    SavageModeTablePtr pTable;
248
249    if( !psav->pVbe )
250	return 0;
251
252    if (!(vbe = VBEGetVBEInfo(psav->pVbe)))
253	return 0;
254
255    nModes = SavageGetBIOSModes( psav, vbe, iDepth, NULL );
256
257    pTable = (SavageModeTablePtr)
258	calloc( 1, sizeof(SavageModeTableRec) +
259		    (nModes-1) * sizeof(SavageModeEntry) );
260    if( pTable ) {
261	pTable->NumModes = nModes;
262	SavageGetBIOSModes( psav, vbe, iDepth, pTable->Modes );
263    }
264
265    VBEFreeVBEInfo(vbe);
266
267    return pTable;
268}
269
270
271unsigned short
272SavageGetBIOSModes(
273    SavagePtr psav,
274    VbeInfoBlock *vbe,
275    int iDepth,
276    SavageModeEntryPtr s3vModeTable )
277{
278    unsigned short iModeCount = 0;
279    unsigned short int *mode_list;
280    pointer vbeLinear = NULL;
281    int vbeReal;
282    struct vbe_mode_info_block * vmib;
283
284    vbeLinear = xf86Int10AllocPages( psav->pVbe->pInt10, 1, &vbeReal );
285    if( !vbeLinear )
286    {
287	ErrorF( "Cannot allocate scratch page in real mode memory." );
288	return 0;
289    }
290    vmib = (struct vbe_mode_info_block *) vbeLinear;
291
292    for (mode_list = vbe->VideoModePtr; *mode_list != 0xffff; mode_list++) {
293
294	/*
295	 * This is a HACK to work around what I believe is a BUG in the
296	 * Toshiba Satellite BIOSes in 08/2000 and 09/2000.  The BIOS
297	 * table for 1024x600 says it has six refresh rates, when in fact
298	 * it only has 3.  When I ask for rate #4, the BIOS goes into an
299	 * infinite loop until the user interrupts it, usually by pressing
300	 * Ctrl-Alt-F1.  For now, we'll just punt everything with a VESA
301	 * number greater than or equal to 0200.
302	 *
303	 * This also prevents some strange and unusual results seen with
304	 * the later ProSavage/PM133 BIOSes directly from S3/VIA.
305	 */
306	if( *mode_list >= 0x0200 )
307	    continue;
308
309	SavageClearVM86Regs( psav->pVbe->pInt10 );
310
311	psav->pVbe->pInt10->ax = 0x4f01;
312	psav->pVbe->pInt10->cx = *mode_list;
313	psav->pVbe->pInt10->es = SEG_ADDR(vbeReal);
314	psav->pVbe->pInt10->di = SEG_OFF(vbeReal);
315	psav->pVbe->pInt10->num = 0x10;
316
317	xf86ExecX86int10( psav->pVbe->pInt10 );
318
319	if(
320	   (vmib->bits_per_pixel == iDepth) &&
321	   (
322	      (vmib->memory_model == VBE_MODEL_256) ||
323	      (vmib->memory_model == VBE_MODEL_PACKED) ||
324	      (vmib->memory_model == VBE_MODEL_RGB)
325	   )
326	)
327	{
328	    /* This mode is a match. */
329
330	    iModeCount++;
331
332	    /* If we're supposed to fetch information, do it now. */
333
334	    if( s3vModeTable )
335	    {
336	        int iRefresh = 0;
337
338		s3vModeTable->Width = vmib->x_resolution;
339		s3vModeTable->Height = vmib->y_resolution;
340		s3vModeTable->VesaMode = *mode_list;
341
342		/* Query the refresh rates at this mode. */
343
344		psav->pVbe->pInt10->cx = *mode_list;
345		psav->pVbe->pInt10->dx = 0;
346
347		do
348		{
349		    if( (iRefresh % 8) == 0 )
350		    {
351			if( s3vModeTable->RefreshRate )
352			{
353			    s3vModeTable->RefreshRate = (unsigned char *)
354				realloc(
355				    s3vModeTable->RefreshRate,
356				    (iRefresh+8) * sizeof(unsigned char)
357				);
358			}
359			else
360			{
361			    s3vModeTable->RefreshRate = (unsigned char *)
362				calloc(
363				    sizeof(unsigned char),
364				    (iRefresh+8)
365				);
366			}
367		    }
368
369		    psav->pVbe->pInt10->ax = 0x4f14;	/* S3 extended functions */
370		    psav->pVbe->pInt10->bx = 0x0201;	/* query refresh rates */
371		    psav->pVbe->pInt10->num = 0x10;
372		    xf86ExecX86int10( psav->pVbe->pInt10 );
373
374		    s3vModeTable->RefreshRate[iRefresh++] = psav->pVbe->pInt10->di;
375		}
376		while( psav->pVbe->pInt10->dx );
377
378		s3vModeTable->RefreshCount = iRefresh;
379
380	    	s3vModeTable++;
381	    }
382	}
383    }
384
385    xf86Int10FreePages( psav->pVbe->pInt10, vbeLinear, 1 );
386
387    return iModeCount;
388}
389
390ModeStatus SavageMatchBiosMode(ScrnInfoPtr pScrn,int width,int height,int refresh,
391                              unsigned int *vesaMode,unsigned int *newRefresh)
392{
393    SavageModeEntryPtr pmt;
394    Bool found = FALSE;
395    SavagePtr psav = SAVPTR(pScrn);
396    int i,j;
397    unsigned int chosenVesaMode = 0;
398    unsigned int chosenRefresh = 0;
399
400    /* Scan through our BIOS list to locate the closest valid mode. */
401
402    /*
403     * If we ever break 4GHz clocks on video boards, we'll need to
404     * change this.
405     * refresh = (mode->Clock * 1000) / (mode->HTotal * mode->VTotal);
406     * now we use VRefresh directly,instead of by calculating from dot clock
407     */
408
409    for( i = 0, pmt = psav->ModeTable->Modes;
410	i < psav->ModeTable->NumModes;
411	i++, pmt++ )
412    {
413	if( (pmt->Width == width) &&
414	    (pmt->Height == height) )
415	{
416	    int jDelta = 99;
417	    int jBest = 0;
418
419	    /* We have an acceptable mode.  Find a refresh rate. */
420	    chosenVesaMode = pmt->VesaMode;
421            if (vesaMode)
422                *vesaMode = chosenVesaMode;
423	    for( j = 0; j < pmt->RefreshCount; j++ )
424	    {
425		if( pmt->RefreshRate[j] == refresh )
426		{
427		    /* Exact match. */
428		    jBest = j;
429		    break;
430		}
431		else if( iabs(pmt->RefreshRate[j] - refresh) < jDelta )
432		{
433		    jDelta = iabs(pmt->RefreshRate[j] - refresh);
434		    jBest = j;
435		}
436	    }
437	    chosenRefresh = pmt->RefreshRate[jBest];
438            if (newRefresh)
439                *newRefresh = chosenRefresh;
440            found = TRUE;
441	    break;
442	}
443    }
444
445    if( found ) {
446	/* Success: we found a match in the BIOS. */
447	xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
448		  "Chose mode %x at %dHz.\n", chosenVesaMode, chosenRefresh );
449        return MODE_OK;
450    } else {
451	xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
452		  "No suitable BIOS mode found for %dx%d %dHz.\n",
453		  width, height, refresh);
454        return MODE_NOMODE;
455    }
456}
457