1/*
2Copyright (C) 1994-1999 The XFree86 Project, Inc.  All Rights Reserved.
3Copyright (C) 2000 Silicon Motion, Inc.  All Rights Reserved.
4Copyright (C) 2008 Francisco Jerez. All Rights Reserved.
5
6Permission is hereby granted, free of charge, to any person obtaining a copy of
7this software and associated documentation files (the "Software"), to deal in
8the Software without restriction, including without limitation the rights to
9use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10of the Software, and to permit persons to whom the Software is furnished to do
11so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
18NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23Except as contained in this notice, the names of The XFree86 Project and
24Silicon Motion shall not be used in advertising or otherwise to promote the
25sale, use or other dealings in this Software without prior written
26authorization from The XFree86 Project or Silicon Motion.
27*/
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include "smi.h"
34#include "smi_crtc.h"
35#include "smilynx.h"
36
37static void
38SMILynx_CrtcVideoInit_crt(xf86CrtcPtr crtc)
39{
40    ScrnInfoPtr pScrn=crtc->scrn;
41    SMIPtr pSmi = SMIPTR(pScrn);
42    int pitch;
43
44    ENTER();
45
46    switch (pScrn->bitsPerPixel) {
47    case 8:
48	WRITE_VPR(pSmi, 0x00, 0x00000000);
49	break;
50    case 16:
51	WRITE_VPR(pSmi, 0x00, 0x00020000);
52	break;
53    case 24:
54	WRITE_VPR(pSmi, 0x00, 0x00040000);
55	break;
56    case 32:
57	WRITE_VPR(pSmi, 0x00, 0x00030000);
58	break;
59    }
60
61    pitch = (crtc->rotatedData? crtc->mode.HDisplay : pScrn->displayWidth) * pSmi->Bpp;
62    pitch = (pitch + 15) & ~15;
63
64    WRITE_VPR(pSmi, 0x10, (crtc->mode.HDisplay * pSmi->Bpp) >> 3 << 16 | pitch >> 3);
65
66    LEAVE();
67}
68
69static void
70SMILynx_CrtcVideoInit_lcd(xf86CrtcPtr crtc)
71{
72    ScrnInfoPtr pScrn=crtc->scrn;
73    SMIPtr pSmi = SMIPTR(pScrn);
74    SMIRegPtr mode = pSmi->mode;
75    CARD16 fifo_readoffset,fifo_writeoffset;
76
77    ENTER();
78
79    /* Set display depth */
80    if (pScrn->bitsPerPixel > 8)
81	mode->SR31 |= 0x40; /* 16 bpp */
82    else
83	mode->SR31 &= ~0x40; /* 8 bpp */
84
85    /* FIFO1/2 Read Offset*/
86    fifo_readoffset = (crtc->rotatedData? crtc->mode.HDisplay : pScrn->displayWidth) * pSmi->Bpp;
87    fifo_readoffset = ((fifo_readoffset + 15) & ~15) >> 3;
88
89    /* FIFO1 Read Offset */
90    mode->SR44 = fifo_readoffset & 0x000000FF;
91    /* FIFO2 Read Offset */
92    mode->SR4B = fifo_readoffset & 0x000000FF;
93
94    if(pSmi->Chipset == SMI_LYNX3DM){
95	/* FIFO1/2 Read Offset overflow */
96	mode->SR4C = (((fifo_readoffset & 0x00000300) >> 8) << 2) |
97	    (((fifo_readoffset & 0x00000300) >> 8) << 6);
98    }else{
99	/* FIFO1 Read Offset overflow */
100	mode->SR45 = (mode->SR45 & 0x3F) | ((fifo_readoffset & 0x00000300) >> 8) << 6;
101	/* FIFO2 Read Offset overflow */
102	mode->SR4C = (((fifo_readoffset & 0x00000300) >> 8) << 6);
103    }
104
105    /* FIFO Write Offset */
106    fifo_writeoffset = crtc->mode.HDisplay * pSmi->Bpp >> 3;
107    mode->SR48 = fifo_writeoffset & 0x000000FF;
108    mode->SR49 = (fifo_writeoffset & 0x00000300) >> 8;
109
110    /* set FIFO levels */
111    mode->SR4A = 0x41;
112
113    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x31, mode->SR31);
114    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x44, mode->SR44);
115    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x45, mode->SR45);
116    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x48, mode->SR48);
117    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x49, mode->SR49);
118    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x4A, mode->SR4A);
119    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x4B, mode->SR4B);
120    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x4C, mode->SR4C);
121
122    LEAVE();
123}
124
125static void
126SMI730_CrtcVideoInit(xf86CrtcPtr crtc)
127{
128    ScrnInfoPtr pScrn=crtc->scrn;
129    SMIPtr pSmi = SMIPTR(pScrn);
130    int pitch;
131
132    ENTER();
133
134    switch (pScrn->bitsPerPixel) {
135    case 8:
136	WRITE_VPR(pSmi, 0x00, 0x00000000);
137	WRITE_FPR(pSmi, FPR00, 0x00080000);
138	break;
139    case 16:
140	WRITE_VPR(pSmi, 0x00, 0x00020000);
141	WRITE_FPR(pSmi, FPR00, 0x000A0000);
142	break;
143    case 24:
144	WRITE_VPR(pSmi, 0x00, 0x00040000);
145	WRITE_FPR(pSmi, FPR00, 0x000C0000);
146	break;
147    case 32:
148	WRITE_VPR(pSmi, 0x00, 0x00030000);
149	WRITE_FPR(pSmi, FPR00, 0x000B0000);
150	break;
151    }
152
153    pitch = (crtc->rotatedData? crtc->mode.HDisplay : pScrn->displayWidth) * pSmi->Bpp;
154    pitch = (pitch + 15) & ~15;
155
156    WRITE_VPR(pSmi, 0x10, (crtc->mode.HDisplay * pSmi->Bpp) >> 3 << 16 | pitch >> 3);
157    WRITE_FPR(pSmi, FPR10, (crtc->mode.HDisplay * pSmi->Bpp) >> 3 << 16 | pitch >> 3);
158
159    LEAVE();
160}
161
162static void
163SMILynx_CrtcAdjustFrame(xf86CrtcPtr crtc, int x, int y)
164{
165    ScrnInfoPtr pScrn=crtc->scrn;
166    SMIPtr pSmi = SMIPTR(pScrn);
167    SMIRegPtr mode = pSmi->mode;
168    xf86CrtcConfigPtr crtcConf = XF86_CRTC_CONFIG_PTR(pScrn);
169    CARD32 Base;
170
171    ENTER();
172
173    if(crtc->rotatedData)
174	Base = (char*)crtc->rotatedData - (char*)pSmi->FBBase;
175    else
176	Base = pSmi->FBOffset + (x + y * pScrn->displayWidth) * pSmi->Bpp;
177
178
179    if (SMI_LYNX3D_SERIES(pSmi->Chipset) ||
180	     SMI_COUGAR_SERIES(pSmi->Chipset)) {
181	Base = (Base + 15) & ~15;
182	while ((Base % pSmi->Bpp) > 0) {
183	    Base -= 16;
184	}
185    } else {
186	Base = (Base + 7) & ~7;
187	while ((Base % pSmi->Bpp) > 0)
188	    Base -= 8;
189    }
190
191    Base >>= 3;
192
193    if(SMI_COUGAR_SERIES(pSmi->Chipset)){
194	WRITE_VPR(pSmi, 0x0C, Base);
195	WRITE_FPR(pSmi, FPR0C, Base);
196    }else{
197	if(pSmi->Dualhead && crtc == crtcConf->crtc[1]){
198	    /* LCD */
199
200	    /* FIFO1 read start address */
201	    mode->SR40 = Base & 0x000000FF;
202	    mode->SR41 = (Base & 0x0000FF00) >> 8;
203
204	    /* FIFO2 read start address */
205	    mode->SR42 = Base & 0x000000FF;
206	    mode->SR43 = (Base & 0x0000FF00) >> 8;
207
208	    /* FIFO1/2 read start address overflow */
209	    if(pSmi->Chipset == SMI_LYNX3DM)
210		mode->SR45 = (Base & 0x000F0000) >> 16 | (Base & 0x000F0000) >> 16 << 4;
211	    else
212		mode->SR45 = (mode->SR45 & 0xC0) |
213		    (Base & 0x00070000) >> 16 | (Base & 0x00070000) >> 16 << 3;
214
215	    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x40, mode->SR40);
216	    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x41, mode->SR41);
217	    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x42, mode->SR42);
218	    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x43, mode->SR43);
219	    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x45, mode->SR45);
220
221	}else{
222	    /* CRT or single head */
223	    WRITE_VPR(pSmi, 0x0C, Base);
224	}
225    }
226
227    LEAVE();
228}
229
230static Bool
231SMILynx_CrtcModeFixup(xf86CrtcPtr crtc,
232		      DisplayModePtr mode,
233		      DisplayModePtr adjusted_mode)
234{
235    ScrnInfoPtr pScrn=crtc->scrn;
236    SMIPtr pSmi = SMIPTR(pScrn);
237
238    ENTER();
239
240    if (pSmi->Chipset == SMI_LYNXEMplus) {
241	/* Adjust the pixel clock in case it is near one of the known
242	   stable frequencies (KHz) */
243	int stable_clocks[] = {46534,};
244	int epsilon = 3000;
245	int i;
246
247	for (i=0; i < sizeof(stable_clocks)/sizeof(int); i++) {
248	    if ( abs(mode->Clock - stable_clocks[i]) < epsilon) {
249		adjusted_mode->Clock = stable_clocks[i];
250		break;
251	    }
252	}
253    }
254
255    LEAVE(TRUE);
256}
257
258static void
259SMILynx_CrtcModeSet_vga(xf86CrtcPtr crtc,
260	    DisplayModePtr mode,
261	    DisplayModePtr adjusted_mode,
262	    int x, int y)
263{
264    ScrnInfoPtr pScrn=crtc->scrn;
265    SMIPtr pSmi = SMIPTR(pScrn);
266    SMIRegPtr reg = pSmi->mode;
267    vgaHWPtr hwp = VGAHWPTR(pScrn);
268    int vgaIOBase  = hwp->IOBase;
269    int vgaCRIndex = vgaIOBase + VGA_CRTC_INDEX_OFFSET;
270    int vgaCRData  = vgaIOBase + VGA_CRTC_DATA_OFFSET;
271    vgaRegPtr vganew = &hwp->ModeReg;
272
273    ENTER();
274
275    /* Initialize Video Processor Registers */
276
277    SMICRTC(crtc)->video_init(crtc);
278    SMILynx_CrtcAdjustFrame(crtc, x,y);
279
280
281    /* Program the PLL */
282
283    /* calculate vclk1 */
284    if (SMI_LYNX_SERIES(pSmi->Chipset)) {
285        SMI_CommonCalcClock(pScrn->scrnIndex, adjusted_mode->Clock,
286			1, 1, 63, 0, 3,
287                        pSmi->clockRange.minClock,
288                        pSmi->clockRange.maxClock,
289                        &reg->SR6C, &reg->SR6D);
290    } else {
291        SMI_CommonCalcClock(pScrn->scrnIndex, adjusted_mode->Clock,
292			1, 1, 63, 0, 1,
293                        pSmi->clockRange.minClock,
294                        pSmi->clockRange.maxClock,
295                        &reg->SR6C, &reg->SR6D);
296    }
297
298    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x6C, reg->SR6C);
299    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x6D, reg->SR6D);
300
301
302    /* Adjust mode timings */
303
304    if (!vgaHWInit(pScrn, mode)) {
305	LEAVE();
306    }
307
308    if ((mode->HDisplay == 640) && SMI_LYNXM_SERIES(pSmi->Chipset)) {
309	vganew->MiscOutReg &= ~0x0C;
310    } else {
311	vganew->MiscOutReg |= 0x0C;
312    }
313    vganew->MiscOutReg |= 0x20;
314
315    {
316	unsigned long HTotal=(mode->CrtcHTotal>>3)-5;
317	unsigned long HBlankEnd=(mode->CrtcHBlankEnd>>3)-1;
318	unsigned long VTotal=mode->CrtcVTotal-2;
319	unsigned long VDisplay=mode->CrtcVDisplay-1;
320	unsigned long VBlankStart=mode->CrtcVBlankStart-1;
321	unsigned long VBlankEnd=mode->CrtcVBlankEnd-1;
322	unsigned long VSyncStart=mode->CrtcVSyncStart;
323
324	/* Fix HBlankEnd/VBlankEnd */
325	if((mode->CrtcHBlankEnd >> 3) == (mode->CrtcHTotal >> 3)) HBlankEnd=0;
326	if(mode->CrtcVBlankEnd == mode->CrtcVTotal) VBlankEnd=0;
327
328	vganew->CRTC[3] = (vganew->CRTC[3] & ~0x1F) | (HBlankEnd & 0x1F);
329	vganew->CRTC[5] = (vganew->CRTC[5] & ~0x80) | (HBlankEnd & 0x20) >> 5 << 7;
330	vganew->CRTC[22] = VBlankEnd & 0xFF;
331
332	/* Write the overflow from several VGA registers */
333	reg->CR30 = (VTotal & 0x400) >> 10 << 3 |
334	    (VDisplay & 0x400) >> 10 << 2 |
335	    (VBlankStart & 0x400) >> 10 << 1 |
336	    (VSyncStart & 0x400) >> 10 << 0;
337
338	if(pSmi->Chipset == SMI_LYNX3DM)
339	    reg->CR30 |= (HTotal & 0x100) >> 8 << 6;
340
341	reg->CR33 = (HBlankEnd & 0xC0) >> 6 << 5 | (VBlankEnd & 0x300) >> 8 << 3;
342    }
343
344    vgaHWRestore(pScrn, vganew, VGA_SR_MODE);
345
346    VGAOUT8_INDEX(pSmi, vgaCRIndex, vgaCRData, 0x30, reg->CR30);
347    VGAOUT8_INDEX(pSmi, vgaCRIndex, vgaCRData, 0x33, reg->CR33);
348
349    LEAVE();
350}
351
352static void
353SMILynx_CrtcModeSet_crt(xf86CrtcPtr crtc,
354	    DisplayModePtr mode,
355	    DisplayModePtr adjusted_mode,
356	    int x, int y)
357{
358    ScrnInfoPtr pScrn=crtc->scrn;
359    SMIPtr pSmi = SMIPTR(pScrn);
360    SMIRegPtr reg = pSmi->mode;
361    vgaHWPtr hwp = VGAHWPTR(pScrn);
362    int vgaIOBase  = hwp->IOBase;
363    int	vgaCRIndex = vgaIOBase + VGA_CRTC_INDEX_OFFSET;
364    int	vgaCRData  = vgaIOBase + VGA_CRTC_DATA_OFFSET;
365    int i;
366
367    ENTER();
368
369    /* Initialize Video Processor Registers */
370
371    SMILynx_CrtcVideoInit_crt(crtc);
372    SMILynx_CrtcAdjustFrame(crtc, x,y);
373
374
375    /* Program the PLL */
376
377    /* calculate vclk1 */
378    if (SMI_LYNX_SERIES(pSmi->Chipset)) {
379        SMI_CommonCalcClock(pScrn->scrnIndex, adjusted_mode->Clock,
380			1, 1, 63, 0, 3,
381                        pSmi->clockRange.minClock,
382                        pSmi->clockRange.maxClock,
383                        &reg->SR6C, &reg->SR6D);
384    } else {
385        SMI_CommonCalcClock(pScrn->scrnIndex, adjusted_mode->Clock,
386			1, 1, 63, 0, 1,
387                        pSmi->clockRange.minClock,
388                        pSmi->clockRange.maxClock,
389                        &reg->SR6C, &reg->SR6D);
390    }
391
392    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x6C, reg->SR6C);
393    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x6D, reg->SR6D);
394
395
396    /* Adjust mode timings */
397    /* In virtual refresh mode, the CRT timings are controlled through
398       the shadow VGA registers */
399
400    {
401	unsigned long HTotal=(mode->CrtcHTotal>>3)-5;
402	unsigned long HDisplay=(mode->CrtcHDisplay>>3)-1;
403	unsigned long HBlankStart=(mode->CrtcHBlankStart>>3)-1;
404	unsigned long HBlankEnd=(mode->CrtcHBlankEnd>>3)-1;
405	unsigned long HSyncStart=mode->CrtcHSyncStart>>3;
406	unsigned long HSyncEnd=mode->CrtcHSyncEnd>>3;
407	unsigned long VTotal=mode->CrtcVTotal-2;
408	unsigned long VDisplay=mode->CrtcVDisplay-1;
409	unsigned long VBlankStart=mode->CrtcVBlankStart-1;
410	unsigned long VBlankEnd=mode->CrtcVBlankEnd-1;
411	unsigned long VSyncStart=mode->CrtcVSyncStart;
412	unsigned long VSyncEnd=mode->CrtcVSyncEnd;
413
414	/* Fix HBlankEnd/VBlankEnd */
415	if((mode->CrtcHBlankEnd >> 3) == (mode->CrtcHTotal >> 3)) HBlankEnd=0;
416	if(mode->CrtcVBlankEnd == mode->CrtcVTotal) VBlankEnd=0;
417
418	reg->CR40 [0x0] = HTotal & 0xFF;
419	reg->CR40 [0x1] = HBlankStart & 0xFF;
420	reg->CR40 [0x2] = HBlankEnd & 0x1F;
421	reg->CR40 [0x3] = HSyncStart & 0xFF;
422	reg->CR40 [0x4] = (HBlankEnd & 0x20) >> 5 << 7 |
423	    (HSyncEnd & 0x1F);
424	reg->CR40 [0x5] = VTotal & 0xFF;
425	reg->CR40 [0x6] = VBlankStart & 0xFF;
426	reg->CR40 [0x7] = VBlankEnd & 0xFF;
427	reg->CR40 [0x8] = VSyncStart & 0xFF;
428	reg->CR40 [0x9] = VSyncEnd & 0x0F;
429	reg->CR40 [0xA] = (VSyncStart & 0x200) >> 9 << 7 |
430	    (VDisplay & 0x200) >> 9 << 6 |
431	    (VTotal & 0x200) >> 9 << 5 |
432	    (VBlankStart & 0x100) >> 8 << 3 |
433	    (VSyncStart & 0x100) >> 8 << 2 |
434	    (VDisplay & 0x100) >> 8 << 1 |
435	    (VTotal & 0x100) >> 8 << 0;
436	reg->CR40 [0xB] = ((mode->Flags & V_NVSYNC)?1:0) << 7 |
437	    ((mode->Flags & V_NHSYNC)?1:0) << 6 |
438	    (VBlankStart & 0x200) >> 9 << 5;
439	reg->CR40 [0xC] = HDisplay & 0xFF;
440	reg->CR40 [0xD] = VDisplay & 0xFF;
441
442	reg->CR30 = (VTotal & 0x400) >> 10 << 3 |
443	    (VDisplay & 0x400) >> 10 << 2 |
444	    (VBlankStart & 0x400) >> 10 << 1 |
445	    (VSyncStart & 0x400) >> 10 << 0;
446
447	if(pSmi->Chipset == SMI_LYNX3DM)
448	    reg->CR30 |= (HTotal & 0x100) >> 8 << 6;
449
450	reg->CR33 = (HBlankEnd & 0xC0) >> 6 << 5 | (VBlankEnd & 0x300) >> 8 << 3;
451
452    }
453
454    /* Select primary set of shadow registers */
455    VGAOUT8_INDEX(pSmi, vgaCRIndex, vgaCRData, 0x9E, reg->CR90[0xE] & ~0x20);
456
457    for(i=0; i <= 0xD; i++)
458	VGAOUT8_INDEX(pSmi, vgaCRIndex, vgaCRData, 0x40 + i, reg->CR40[i]);
459
460    VGAOUT8_INDEX(pSmi, vgaCRIndex, vgaCRData, 0x30, reg->CR30);
461    VGAOUT8_INDEX(pSmi, vgaCRIndex, vgaCRData, 0x33, reg->CR33);
462
463    LEAVE();
464}
465
466static void
467SMILynx_CrtcModeSet_lcd(xf86CrtcPtr crtc,
468	    DisplayModePtr mode,
469	    DisplayModePtr adjusted_mode,
470	    int x, int y)
471{
472    ScrnInfoPtr pScrn=crtc->scrn;
473    SMIPtr pSmi = SMIPTR(pScrn);
474    SMIRegPtr reg = pSmi->mode;
475
476    ENTER();
477
478    /* Initialize the flat panel video processor */
479
480    SMILynx_CrtcVideoInit_lcd(crtc);
481    SMILynx_CrtcAdjustFrame(crtc,x,y);
482
483
484    /* Program the PLL */
485
486    /* calculate vclk2 */
487    if (SMI_LYNX_SERIES(pSmi->Chipset)) {
488        SMI_CommonCalcClock(pScrn->scrnIndex, adjusted_mode->Clock,
489			1, 1, 63, 0, 0,
490                        pSmi->clockRange.minClock,
491                        pSmi->clockRange.maxClock,
492                        &reg->SR6E, &reg->SR6F);
493    } else {
494        SMI_CommonCalcClock(pScrn->scrnIndex, adjusted_mode->Clock,
495			1, 1, 63, 0, 1,
496                        pSmi->clockRange.minClock,
497                        pSmi->clockRange.maxClock,
498                        &reg->SR6E, &reg->SR6F);
499    }
500
501    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x6E, reg->SR6E);
502    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x6F, reg->SR6F);
503
504
505    /* Adjust mode timings */
506    {
507	unsigned long HTotal=(mode->CrtcHTotal>>3)-1;
508	unsigned long HDisplay=(mode->CrtcHDisplay>>3)-1;
509	unsigned long HSyncStart=(mode->CrtcHSyncStart>>3);
510	unsigned long HSyncWidth=((mode->CrtcHSyncEnd - mode->CrtcHSyncStart) >> 3) - 1;
511	unsigned long VTotal=mode->CrtcVTotal-1;
512	unsigned long VDisplay=mode->CrtcVDisplay-1;
513	unsigned long VSyncStart=mode->CrtcVSyncStart-1;
514	unsigned long VSyncWidth=mode->CrtcVSyncEnd - mode->CrtcVSyncStart - 1;
515
516	reg->SR50 = (VTotal & 0x700) >> 8 << 1 |
517	    (HSyncStart & 0x100) >> 8 << 0;
518	reg->SR51 = (VSyncStart & 0x700) >> 8 << 5 |
519	    (VDisplay & 0x700) >> 8 << 2 |
520	    (HDisplay & 0x100) >> 8 << 1 |
521	    (HTotal & 0x100) >> 8 << 0;
522	reg->SR52 = HTotal & 0xFF;
523	reg->SR53 = HDisplay & 0xFF;
524	reg->SR54 = HSyncStart & 0xFF;
525	reg->SR55 = VTotal & 0xFF;
526	reg->SR56 = VDisplay & 0xFF;
527	reg->SR57 = VSyncStart & 0xFF;
528	reg->SR5A = (HSyncWidth & 0x1F) << 3 |
529	    (VSyncWidth & 0x07) << 0;
530
531	/* XXX - Why is the polarity hardcoded here? */
532	reg->SR32 &= ~0x18;
533	if (mode->HDisplay == 800) {
534	    reg->SR32 |= 0x18;
535	}
536	if ((mode->HDisplay == 1024) && SMI_LYNXM_SERIES(pSmi->Chipset)) {
537	    reg->SR32 |= 0x18;
538	}
539    }
540
541    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x32, reg->SR32);
542    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x50, reg->SR50);
543    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x51, reg->SR51);
544    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x52, reg->SR52);
545    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x53, reg->SR53);
546    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x54, reg->SR54);
547    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x55, reg->SR55);
548    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x56, reg->SR56);
549    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x57, reg->SR57);
550    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x5A, reg->SR5A);
551
552    LEAVE();
553}
554
555static void
556SMILynx_CrtcModeSet_bios(xf86CrtcPtr crtc,
557	    DisplayModePtr mode,
558	    DisplayModePtr adjusted_mode,
559	    int x, int y)
560{
561    ScrnInfoPtr pScrn=crtc->scrn;
562    SMIPtr pSmi = SMIPTR(pScrn);
563    SMIRegPtr reg = pSmi->mode;
564    int i;
565    CARD8 tmp;
566
567    ENTER();
568#ifdef USE_INT10
569    /* Find the INT 10 mode number */
570    {
571	static struct {
572	    int x, y, bpp;
573	    CARD16 mode;
574	} modeTable[] =
575	    {
576		{  640,  480,  8, 0x50 },
577		{  640,  480, 16, 0x52 },
578		{  640,  480, 24, 0x53 },
579		{  640,  480, 32, 0x54 },
580		{  800,  480,  8, 0x4A },
581		{  800,  480, 16, 0x4C },
582		{  800,  480, 24, 0x4D },
583		{  800,  600,  8, 0x55 },
584		{  800,  600, 16, 0x57 },
585		{  800,  600, 24, 0x58 },
586		{  800,  600, 32, 0x59 },
587		{ 1024,  768,  8, 0x60 },
588		{ 1024,  768, 16, 0x62 },
589		{ 1024,  768, 24, 0x63 },
590		{ 1024,  768, 32, 0x64 },
591		{ 1280, 1024,  8, 0x65 },
592		{ 1280, 1024, 16, 0x67 },
593		{ 1280, 1024, 24, 0x68 },
594		{ 1280, 1024, 32, 0x69 },
595	    };
596
597	reg->mode = 0;
598	for (i = 0; i < sizeof(modeTable) / sizeof(modeTable[0]); i++) {
599	    if ((modeTable[i].x == mode->HDisplay) &&
600		(modeTable[i].y == mode->VDisplay) &&
601		(modeTable[i].bpp == pScrn->bitsPerPixel)) {
602		reg->mode = modeTable[i].mode;
603		break;
604	    }
605	}
606    }
607#endif
608    if(!reg->mode){
609	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "SMILynx_CrtcModeSet_bios: Not a known BIOS mode: "
610		   "falling back to direct modesetting.\n");
611	SMILynx_CrtcModeSet_vga(crtc,mode,adjusted_mode,x,y);
612	LEAVE();
613    }
614#ifdef USE_INT10
615    pSmi->pInt10->num = 0x10;
616    pSmi->pInt10->ax = reg->mode | 0x80;
617    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Setting mode 0x%02X\n",
618	       reg->mode);
619    xf86ExecX86int10(pSmi->pInt10);
620
621    /* Enable linear mode. */
622    outb(pSmi->PIOBase + VGA_SEQ_INDEX, 0x18);
623    tmp = inb(pSmi->PIOBase + VGA_SEQ_DATA);
624    outb(pSmi->PIOBase + VGA_SEQ_DATA, tmp | 0x01);
625
626    /* Enable DPR/VPR registers. */
627    tmp = VGAIN8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x21);
628    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x21, tmp & ~0x03);
629
630
631    /* Initialize Video Processor Registers */
632
633    SMICRTC(crtc)->video_init(crtc);
634    SMILynx_CrtcAdjustFrame(crtc, x,y);
635#endif
636    LEAVE();
637}
638
639static void
640SMILynx_CrtcLoadLUT_crt(xf86CrtcPtr crtc)
641{
642    ScrnInfoPtr pScrn = crtc->scrn;
643    SMIPtr pSmi = SMIPTR(pScrn);
644    SMIRegPtr mode = pSmi->mode;
645    SMICrtcPrivatePtr crtcPriv = SMICRTC(crtc);
646    int i;
647
648    ENTER();
649
650    /* Write CRT RAM only */
651    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX,VGA_SEQ_DATA,0x66,(mode->SR66 & ~0x30) | 0x20);
652
653    for(i=0;i<256;i++){
654	VGAOUT8(pSmi, VGA_DAC_WRITE_ADDR, i);
655	VGAOUT8(pSmi, VGA_DAC_DATA, crtcPriv->lut_r[i] >> 8);
656	VGAOUT8(pSmi, VGA_DAC_DATA, crtcPriv->lut_g[i] >> 8);
657	VGAOUT8(pSmi, VGA_DAC_DATA, crtcPriv->lut_b[i] >> 8);
658    }
659
660    LEAVE();
661}
662
663static void
664SMILynx_CrtcLoadLUT_lcd(xf86CrtcPtr crtc)
665{
666    ENTER();
667
668    /* XXX - Is it possible to load LCD LUT in Virtual Refresh mode? */
669
670    LEAVE();
671}
672
673static void
674SMILynx_CrtcSetCursorColors_crt (xf86CrtcPtr crtc, int bg, int fg)
675{
676    ScrnInfoPtr pScrn = crtc->scrn;
677    SMIPtr pSmi = SMIPTR(pScrn);
678    CARD8 packedFG,packedBG;
679
680    ENTER();
681
682    /* Pack the true color into 8 bit */
683    packedFG = (fg & 0xE00000) >> 16 |
684	(fg & 0x00E000) >> 11 |
685	(fg & 0x0000C0) >> 6;
686    packedBG = (bg & 0xE00000) >> 16 |
687	(bg & 0x00E000) >> 11 |
688	(bg & 0x0000C0) >> 6;
689
690    /* Program the colors */
691    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x8C, packedFG);
692    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x8D, packedBG);
693
694    /* Program FPR copy when on the 730 */
695    if (pSmi->Chipset == SMI_COUGAR3DR) {
696	CARD32 fpr15c;
697
698	fpr15c  = READ_FPR(pSmi, FPR15C) & FPR15C_MASK_HWCADDREN;
699	fpr15c |= packedFG;
700	fpr15c |= packedBG << 8;
701	WRITE_FPR(pSmi, FPR15C, fpr15c);
702    }
703
704    LEAVE();
705}
706
707static void
708SMILynx_CrtcSetCursorPosition_crt (xf86CrtcPtr crtc, int x, int y)
709{
710    ScrnInfoPtr pScrn = crtc->scrn;
711    SMIPtr pSmi = SMIPTR(pScrn);
712
713    ENTER();
714
715    if (x >= 0) {
716	VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x88,
717		      x & 0xFF);
718	VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x89,
719		      (x >> 8) & 0x07);
720    }
721    else {
722	VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x88,
723		      (-x) & (SMILYNX_MAX_CURSOR - 1));
724	VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x89,
725		      0x08);
726    }
727
728    if (y >= 0) {
729	VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x8A,
730		      y & 0xFF);
731	VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x8B,
732		      (y >> 8) & 0x07);
733    }
734    else {
735	VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x8A,
736			  (-y) & (SMILYNX_MAX_CURSOR - 1));
737	VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA,
738		      0x8B, 0x08);
739    }
740
741    /* Program FPR copy when on the 730 */
742    if (pSmi->Chipset == SMI_COUGAR3DR) {
743	CARD32 fpr158;
744
745	if (x >= 0)
746	    fpr158 = (x & FPR158_MASK_MAXBITS) << 16;
747	else
748	    fpr158 = ((-x & FPR158_MASK_MAXBITS) |
749		      FPR158_MASK_BOUNDARY) << 16;
750
751	if (y >= 0)
752	    fpr158 |= y & FPR158_MASK_MAXBITS;
753	else
754	    fpr158 |= (-y & FPR158_MASK_MAXBITS) | FPR158_MASK_BOUNDARY;
755
756	/* Program combined coordinates */
757	WRITE_FPR(pSmi, FPR158, fpr158);
758    }
759
760    LEAVE();
761}
762
763static void
764SMILynx_CrtcShowCursor_crt (xf86CrtcPtr crtc)
765{
766    ScrnInfoPtr pScrn = crtc->scrn;
767    SMIPtr pSmi = SMIPTR(pScrn);
768    char tmp;
769
770    ENTER();
771
772    /* Show cursor */
773    tmp = VGAIN8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x81);
774    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x81, tmp | 0x80);
775
776    /* Program FPR copy when on the 730 */
777    if (pSmi->Chipset == SMI_COUGAR3DR) {
778	CARD32 fpr15c;
779
780	/* turn on the top bit */
781	fpr15c  = READ_FPR(pSmi, FPR15C);
782	fpr15c |= FPR15C_MASK_HWCENABLE;
783	WRITE_FPR(pSmi, FPR15C, fpr15c);
784    }
785
786    LEAVE();
787}
788
789static void
790SMILynx_CrtcHideCursor_crt (xf86CrtcPtr crtc)
791{
792    ScrnInfoPtr pScrn = crtc->scrn;
793    SMIPtr pSmi = SMIPTR(pScrn);
794    char tmp;
795
796    ENTER();
797
798    /* Hide cursor */
799    tmp = VGAIN8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x81);
800    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x81, tmp & ~0x80);
801
802    /* Program FPR copy when on the 730 */
803    if (pSmi->Chipset == SMI_COUGAR3DR) {
804	CARD32 fpr15c;
805
806	/* turn off the top bit */
807	fpr15c  = READ_FPR(pSmi, FPR15C);
808	fpr15c &= ~FPR15C_MASK_HWCENABLE;
809	WRITE_FPR(pSmi, FPR15C, fpr15c);
810    }
811
812
813    LEAVE();
814}
815
816static void
817SMILynx_CrtcLoadCursorImage_crt (xf86CrtcPtr crtc, CARD8 *image)
818{
819    ScrnInfoPtr pScrn = crtc->scrn;
820    SMIPtr pSmi = SMIPTR(pScrn);
821    CARD8 tmp;
822    int i;
823    CARD8* dst;
824
825    ENTER();
826
827    /* Load storage location. */
828    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x80,
829		  pSmi->FBCursorOffset / 2048);
830    tmp = VGAIN8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x81) & 0x80;
831    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x81,
832		  tmp | ((pSmi->FBCursorOffset / 2048) >> 8));
833
834    /* Program FPR copy when on the 730 */
835    if (pSmi->Chipset == SMI_COUGAR3DR) {
836	CARD32 fpr15c;
837
838	/* put address in upper word, and disable the cursor */
839	fpr15c  = READ_FPR(pSmi, FPR15C) & FPR15C_MASK_HWCCOLORS;
840	fpr15c |= (pSmi->FBCursorOffset / 2048) << 16;
841	WRITE_FPR(pSmi, FPR15C, fpr15c);
842    }
843
844    /* Copy cursor image to framebuffer storage */
845    dst = pSmi->FBBase + pSmi->FBCursorOffset;
846    for(i=0; i < (SMILYNX_MAX_CURSOR * SMILYNX_MAX_CURSOR >> 2); i++){
847	*(dst++) = image[i];
848	if((i & 0x3) == 0x3) dst+=4;
849    }
850
851    LEAVE();
852}
853
854static void
855SMILynx_CrtcDPMS_crt(xf86CrtcPtr crtc, int mode)
856{
857    ScrnInfoPtr pScrn = crtc->scrn;
858    SMIPtr pSmi = SMIPTR(pScrn);
859    SMIRegPtr reg = pSmi->mode;
860    vgaHWPtr hwp = VGAHWPTR(pScrn);
861
862    ENTER();
863
864    if(mode == DPMSModeOff)
865	reg->SR21 |= 0x88; /* Disable DAC and color palette RAM */
866    else
867	reg->SR21 &= ~0x88; /* Enable DAC and color palette RAM */
868
869    /* Wait for vertical retrace */
870    while (hwp->readST01(hwp) & 0x8) ;
871    while (!(hwp->readST01(hwp) & 0x8)) ;
872
873    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x21, reg->SR21);
874
875    if(mode == DPMSModeOn){
876	/* Reload the LUT */
877	SMILynx_CrtcLoadLUT_crt(crtc);
878    }
879
880    LEAVE();
881}
882
883static void
884SMILynx_CrtcDPMS_lcd(xf86CrtcPtr crtc, int mode)
885{
886    ScrnInfoPtr pScrn = crtc->scrn;
887    SMIPtr pSmi = SMIPTR(pScrn);
888    SMIRegPtr reg = pSmi->mode;
889    vgaHWPtr hwp = VGAHWPTR(pScrn);
890
891    ENTER();
892
893    if(mode == DPMSModeOff)
894	reg->SR31 &= ~0x80; /* Disable Virtual Refresh */
895    else
896	reg->SR31 |= 0x80; /* Enable Virtual Refresh */
897
898    /* Wait for vertical retrace */
899    while (hwp->readST01(hwp) & 0x8) ;
900    while (!(hwp->readST01(hwp) & 0x8)) ;
901
902    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x31, reg->SR31);
903
904    LEAVE();
905}
906
907Bool
908SMILynx_CrtcPreInit(ScrnInfoPtr pScrn)
909{
910    SMIPtr pSmi = SMIPTR(pScrn);
911    xf86CrtcPtr crtc;
912    xf86CrtcFuncsPtr crtcFuncs;
913    SMICrtcPrivatePtr crtcPriv;
914
915    ENTER();
916
917    if(pSmi->Chipset == SMI_COUGAR3DR){
918	/* XXX - Looking at the datasheet, it seems trivial to add
919	   dualhead support for this chip... Little more than
920	   splitting the WRITE_FPR/WRITE_VPR calls in separate
921	   functions. Has someone access to this hardware? */
922
923	SMI_CrtcFuncsInit_base(&crtcFuncs, &crtcPriv);
924
925	if(pSmi->useBIOS){
926	    crtcFuncs->mode_set = SMILynx_CrtcModeSet_bios;
927	}else{
928	    crtcFuncs->dpms = SMILynx_CrtcDPMS_crt;
929	    crtcFuncs->mode_set = SMILynx_CrtcModeSet_vga;
930	}
931
932	crtcFuncs->mode_fixup = SMILynx_CrtcModeFixup;
933	crtcPriv->adjust_frame = SMILynx_CrtcAdjustFrame;
934	crtcPriv->video_init = SMI730_CrtcVideoInit;
935	crtcPriv->load_lut = SMILynx_CrtcLoadLUT_crt;
936
937	if(pSmi->HwCursor){
938	    crtcFuncs->set_cursor_colors = SMILynx_CrtcSetCursorColors_crt;
939	    crtcFuncs->set_cursor_position = SMILynx_CrtcSetCursorPosition_crt;
940	    crtcFuncs->show_cursor = SMILynx_CrtcShowCursor_crt;
941	    crtcFuncs->hide_cursor = SMILynx_CrtcHideCursor_crt;
942	    crtcFuncs->load_cursor_image = SMILynx_CrtcLoadCursorImage_crt;
943	}
944
945	if(! (crtc = xf86CrtcCreate(pScrn,crtcFuncs)))
946	    LEAVE(FALSE);
947	crtc->driver_private = crtcPriv;
948    }else{
949	/* CRTC0 can drive both outputs when virtual refresh is
950	   disabled, and only the VGA output with virtual refresh
951	   enabled. */
952	SMI_CrtcFuncsInit_base(&crtcFuncs, &crtcPriv);
953
954	if(pSmi->useBIOS){
955	    crtcFuncs->mode_set = SMILynx_CrtcModeSet_bios;
956	}else{
957	    crtcFuncs->dpms = SMILynx_CrtcDPMS_crt;
958
959	    if(pSmi->Dualhead){
960		/* The standard VGA CRTC registers get locked in
961		   virtual refresh mode. */
962		crtcFuncs->mode_set = SMILynx_CrtcModeSet_crt;
963
964	    }else{
965		crtcFuncs->mode_set = SMILynx_CrtcModeSet_vga;
966	    }
967	}
968
969	crtcFuncs->mode_fixup = SMILynx_CrtcModeFixup;
970	crtcPriv->adjust_frame = SMILynx_CrtcAdjustFrame;
971	crtcPriv->video_init = SMILynx_CrtcVideoInit_crt;
972	crtcPriv->load_lut = SMILynx_CrtcLoadLUT_crt;
973
974	if(pSmi->HwCursor){
975	    crtcFuncs->set_cursor_colors = SMILynx_CrtcSetCursorColors_crt;
976	    crtcFuncs->set_cursor_position = SMILynx_CrtcSetCursorPosition_crt;
977	    crtcFuncs->show_cursor = SMILynx_CrtcShowCursor_crt;
978	    crtcFuncs->hide_cursor = SMILynx_CrtcHideCursor_crt;
979	    crtcFuncs->load_cursor_image = SMILynx_CrtcLoadCursorImage_crt;
980	}
981
982	if(! (crtc = xf86CrtcCreate(pScrn,crtcFuncs)))
983	    LEAVE(FALSE);
984	crtc->driver_private = crtcPriv;
985
986	if(pSmi->Dualhead){
987	    /* CRTC1 drives LCD when enabled. */
988	    SMI_CrtcFuncsInit_base(&crtcFuncs, &crtcPriv);
989	    crtcFuncs->mode_set = SMILynx_CrtcModeSet_lcd;
990	    crtcFuncs->mode_fixup = SMILynx_CrtcModeFixup;
991	    crtcFuncs->dpms = SMILynx_CrtcDPMS_lcd;
992	    crtcPriv->adjust_frame = SMILynx_CrtcAdjustFrame;
993	    crtcPriv->video_init = SMILynx_CrtcVideoInit_lcd;
994	    crtcPriv->load_lut = SMILynx_CrtcLoadLUT_lcd;
995
996	    if(! (crtc = xf86CrtcCreate(pScrn,crtcFuncs)))
997		LEAVE(FALSE);
998	    crtc->driver_private = crtcPriv;
999	}
1000    }
1001
1002    LEAVE(TRUE);
1003}
1004
1005