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_OutputDPMS_crt(xf86OutputPtr output, int mode)
39{
40    ScrnInfoPtr pScrn = output->scrn;
41    SMIPtr pSmi = SMIPTR(pScrn);
42    SMIRegPtr reg = pSmi->mode;
43    vgaHWPtr hwp = VGAHWPTR(pScrn);
44
45    ENTER();
46
47    switch (mode) {
48    case DPMSModeOn:
49	reg->SR31 |= 0x02; /* Enable CRT display*/
50	reg->SR22 = (reg->SR22 & ~0x30) | 0x00; /* Set DPMS state*/
51	break;
52    case DPMSModeStandby:
53	reg->SR31 |= 0x02; /* Enable CRT display*/
54	reg->SR22 = (reg->SR22 & ~0x30) | 0x10; /* Set DPMS state*/
55	break;
56    case DPMSModeSuspend:
57	reg->SR31 |= 0x02; /* Enable CRT display*/
58	reg->SR22 = (reg->SR22 & ~0x30) | 0x20; /* Set DPMS state*/
59	break;
60    case DPMSModeOff:
61	reg->SR31 &= ~0x02; /* Disable CRT display*/
62	reg->SR22 = (reg->SR22 & ~0x30) | 0x30; /* Set DPMS state*/
63	break;
64    }
65
66    /* Wait for vertical retrace */
67
68    while (hwp->readST01(hwp) & 0x8) ;
69    while (!(hwp->readST01(hwp) & 0x8)) ;
70
71    /* Write the registers */
72    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x22, reg->SR22);
73    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x31, reg->SR31);
74
75    LEAVE();
76
77}
78
79static void
80SMILynx_OutputDPMS_lcd(xf86OutputPtr output, int mode)
81{
82    ScrnInfoPtr pScrn = output->scrn;
83    SMIPtr pSmi = SMIPTR(pScrn);
84    SMIRegPtr reg = pSmi->mode;
85    xf86CrtcConfigPtr crtcConf = XF86_CRTC_CONFIG_PTR(pScrn);
86
87    ENTER();
88
89    switch (mode) {
90    case DPMSModeOn:
91	if(pSmi->Dualhead &&
92	   output->crtc == crtcConf->crtc[1]){
93	    /* Virtual Refresh is enabled */
94
95	    reg->SR21 &= ~0x10; /* Enable LCD framebuffer read operation and DSTN dithering engine */
96	}else{
97	    if(pSmi->lcd == 2){
98		/* LCD is DSTN */
99
100		reg->SR21 &= ~0x10; /* Enable LCD framebuffer read operation and DSTN dithering engine */
101		reg->SR21 &= ~0x20; /* Enable LCD framebuffer write operation */
102	    }
103	}
104
105	reg->SR31 |= 0x01; /* Enable LCD display*/
106	break;
107    case DPMSModeStandby:
108    case DPMSModeSuspend:
109    case DPMSModeOff:
110	reg->SR21 |= 0x30; /* Disable LCD framebuffer r/w operation */
111	reg->SR31 &= ~0x01; /* Disable LCD display*/
112	break;
113    }
114
115    /* Write the registers */
116    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x21, reg->SR21);
117    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x31, reg->SR31);
118
119    LEAVE();
120
121
122}
123
124static void
125SMILynx_OutputDPMS_bios(xf86OutputPtr output, int mode)
126{
127#ifdef USE_INT10
128    ScrnInfoPtr pScrn = output->scrn;
129    SMIPtr pSmi = SMIPTR(pScrn);
130
131    ENTER();
132
133    pSmi->pInt10->ax = 0x4F10;
134    switch (mode) {
135    case DPMSModeOn:
136	pSmi->pInt10->bx = 0x0001;
137	break;
138    case DPMSModeStandby:
139	pSmi->pInt10->bx = 0x0101;
140	break;
141    case DPMSModeSuspend:
142	pSmi->pInt10->bx = 0x0201;
143	break;
144    case DPMSModeOff:
145	pSmi->pInt10->bx = 0x0401;
146	break;
147    }
148    pSmi->pInt10->cx = 0x0000;
149    pSmi->pInt10->num = 0x10;
150    xf86ExecX86int10(pSmi->pInt10);
151#endif
152    LEAVE();
153}
154
155
156static DisplayModePtr
157SMILynx_OutputGetModes_crt(xf86OutputPtr output)
158{
159    ScrnInfoPtr pScrn = output->scrn;
160    SMIPtr pSmi = SMIPTR(pScrn);
161    xf86MonPtr pMon = NULL;
162
163    ENTER();
164
165    if(xf86LoaderCheckSymbol("xf86PrintEDID")){ /* Ensure the DDC module is loaded*/
166	/* Try VBE */
167	if(pSmi->pVbe){
168	    pMon = vbeDoEDID(pSmi->pVbe, NULL);
169	    if ( pMon != NULL &&
170		 (pMon->rawData[0] == 0x00) &&
171		 (pMon->rawData[1] == 0xFF) &&
172		 (pMon->rawData[2] == 0xFF) &&
173		 (pMon->rawData[3] == 0xFF) &&
174		 (pMon->rawData[4] == 0xFF) &&
175		 (pMon->rawData[5] == 0xFF) &&
176		 (pMon->rawData[6] == 0xFF) &&
177		 (pMon->rawData[7] == 0x00)) {
178		xf86OutputSetEDID(output,pMon);
179		LEAVE(xf86OutputGetEDIDModes(output));
180	    }
181	}
182
183	/* Try DDC2 */
184	if(pSmi->I2C){
185	    pMon=xf86OutputGetEDID(output,pSmi->I2C);
186	    if(pMon){
187		xf86OutputSetEDID(output,pMon);
188		LEAVE(xf86OutputGetEDIDModes(output));
189	    }
190	}
191
192	/* Try DDC1 */
193	pMon=SMILynx_ddc1(pScrn);
194	if(pMon){
195	    xf86OutputSetEDID(output,pMon);
196	    LEAVE(xf86OutputGetEDIDModes(output));
197	}
198    }
199
200    LEAVE(NULL);
201}
202
203static xf86OutputStatus
204SMILynx_OutputDetect_crt(xf86OutputPtr output)
205{
206    SMIPtr pSmi = SMIPTR(output->scrn);
207    SMIRegPtr mode = pSmi->mode;
208    vgaHWPtr hwp = VGAHWPTR(output->scrn);
209    CARD8 SR7D;
210    Bool status;
211
212    ENTER();
213
214    SR7D = VGAIN8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x7D);
215
216    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x21, mode->SR21 & ~0x88); /* Enable DAC and color palette RAM */
217    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x7B, 0x40); /* "TV and RAMDAC Testing Power", Green component */
218    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x7D, SR7D | 0x10); /* Enable monitor detect */
219
220    /* Wait for vertical retrace */
221    while (!(hwp->readST01(hwp) & 0x8)) ;
222    while (hwp->readST01(hwp) & 0x8) ;
223
224    status = VGAIN8(pSmi, 0x3C2) & 0x10;
225
226    /* Restore previous state */
227    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x21, mode->SR21);
228    VGAOUT8_INDEX(pSmi, VGA_SEQ_INDEX, VGA_SEQ_DATA, 0x7D, SR7D);
229
230    if(status)
231	LEAVE(XF86OutputStatusConnected);
232    else
233	LEAVE(XF86OutputStatusDisconnected);
234}
235
236Bool
237SMILynx_OutputPreInit(ScrnInfoPtr pScrn)
238{
239    SMIPtr pSmi = SMIPTR(pScrn);
240    xf86OutputPtr output;
241    xf86OutputFuncsPtr outputFuncs;
242
243    ENTER();
244
245    if(pSmi->Chipset == SMI_COUGAR3DR){
246	/* Output 0 is LCD */
247	SMI_OutputFuncsInit_base(&outputFuncs);
248
249	if(pSmi->useBIOS)
250	    outputFuncs->dpms = SMILynx_OutputDPMS_bios;
251	else
252	    outputFuncs->dpms = SMILynx_OutputDPMS_lcd;
253
254	outputFuncs->get_modes = SMI_OutputGetModes_native;
255	outputFuncs->detect = SMI_OutputDetect_lcd;
256
257	if(! (output = xf86OutputCreate(pScrn,outputFuncs,"LVDS")))
258	    LEAVE(FALSE);
259
260	output->possible_crtcs = 1 << 0;
261	output->possible_clones = 0;
262	output->interlaceAllowed = FALSE;
263	output->doubleScanAllowed = FALSE;
264    }else{
265	/* Output 0 is LCD */
266	SMI_OutputFuncsInit_base(&outputFuncs);
267
268	if(pSmi->useBIOS)
269	    outputFuncs->dpms = SMILynx_OutputDPMS_bios;
270	else
271	    outputFuncs->dpms = SMILynx_OutputDPMS_lcd;
272
273	outputFuncs->get_modes = SMI_OutputGetModes_native;
274	outputFuncs->detect = SMI_OutputDetect_lcd;
275
276	if(! (output = xf86OutputCreate(pScrn,outputFuncs,"LVDS")))
277	    LEAVE(FALSE);
278
279	output->interlaceAllowed = FALSE;
280	output->doubleScanAllowed = FALSE;
281	output->possible_crtcs = (1 << 0) | (1 << 1);
282	output->possible_clones = 1 << 1;
283
284	if(pSmi->Dualhead){
285	    /* Output 1 is CRT */
286	    SMI_OutputFuncsInit_base(&outputFuncs);
287	    outputFuncs->dpms = SMILynx_OutputDPMS_crt;
288	    outputFuncs->get_modes = SMILynx_OutputGetModes_crt;
289
290	    if(pSmi->Chipset == SMI_LYNX3DM)
291		outputFuncs->detect = SMILynx_OutputDetect_crt;
292
293	    if(! (output = xf86OutputCreate(pScrn,outputFuncs,"VGA")))
294		LEAVE(FALSE);
295
296	    output->interlaceAllowed = FALSE;
297	    output->doubleScanAllowed = FALSE;
298
299	    output->possible_crtcs = 1 << 0;
300	    output->possible_clones = 1 << 0;
301	}
302    }
303
304    LEAVE(TRUE);
305}
306
307