1 /*
2  * Copyright 2005-2016 The OpenChrome Project
3  *                     [https://www.freedesktop.org/wiki/Openchrome]
4  * Copyright 2004-2005 The Unichrome Project  [unichrome.sf.net]
5  * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
6  * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sub license,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the
16  * next paragraph) shall be included in all copies or substantial portions
17  * of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  * DEALINGS IN THE SOFTWARE.
26  */
27 
28 /*
29  * via_analog.c
30  *
31  * Handles the initialization and management of analog VGA related
32  * resources.
33  *
34  */
35 
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39 
40 #include "via_driver.h"
41 #include <unistd.h>
42 
43 
44 /*
45  * Enables or disables analog VGA output by controlling DAC
46  * (Digital to Analog Converter) output state.
47  */
48 static void
viaAnalogOutput(ScrnInfoPtr pScrn,Bool outputState)49 viaAnalogOutput(ScrnInfoPtr pScrn, Bool outputState)
50 {
51     vgaHWPtr hwp = VGAHWPTR(pScrn);
52 
53     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
54                         "Entered viaAnalogOutput.\n"));
55 
56     /* This register controls analog VGA DAC output state. */
57     /* 3X5.47[2] - DACOFF Backdoor Register
58      *             0: DAC on
59      *             1: DAC off */
60     ViaCrtcMask(hwp, 0x47, outputState ? 0x00 : 0x04, 0x04);
61     xf86DrvMsg(pScrn->scrnIndex, X_INFO,
62                 "Analog VGA Output: %s\n",
63                 outputState ? "On" : "Off");
64 
65     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
66                         "Exiting viaAnalogOutput.\n"));
67 }
68 
69 /*
70  * Specifies IGA1 or IGA2 for analog VGA DAC source.
71  */
72 static void
viaAnalogSetDisplaySource(ScrnInfoPtr pScrn,CARD8 displaySource)73 viaAnalogSetDisplaySource(ScrnInfoPtr pScrn, CARD8 displaySource)
74 {
75     vgaHWPtr hwp = VGAHWPTR(pScrn);
76     CARD8 value = displaySource;
77 
78     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
79                         "Entered viaAnalogSetDisplaySource.\n"));
80 
81     ViaSeqMask(hwp, 0x16, value << 6, 0x40);
82     xf86DrvMsg(pScrn->scrnIndex, X_INFO,
83                 "Analog VGA Display Output Source: IGA%d\n",
84                 (value & 0x01) + 1);
85 
86     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
87                         "Exiting viaAnalogSetDisplaySource.\n"));
88 }
89 
90 /*
91  * Intializes analog VGA related registers.
92  */
93 static void
viaAnalogInit(ScrnInfoPtr pScrn)94 viaAnalogInit(ScrnInfoPtr pScrn)
95 {
96     vgaHWPtr hwp = VGAHWPTR(pScrn);
97     VIAPtr pVia = VIAPTR(pScrn);
98 
99     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
100                         "Entered viaAnalogInit.\n"));
101 
102     /* 3X5.37[7]   - DAC Power Save Control 1
103      *               0: Depend on Rx3X5.37[5:4] setting
104      *               1: DAC always goes into power save mode
105      * 3X5.37[6]   - DAC Power Down Control
106      *               0: Depend on Rx3X5.47[2] setting
107      *               1: DAC never goes to power down mode
108      * 3X5.37[5:4] - DAC Power Save Control 2
109      *               00: DAC never goes to power save mode
110      *               01: DAC goes to power save mode by line
111      *               10: DAC goes to power save mode by frame
112      *               11: DAC goes to power save mode by line and frame
113      * 3X5.37[3]   - DAC PEDESTAL Control
114      * 3X5.37[2:0] - DAC Factor
115      *               (Default: 100) */
116     ViaCrtcMask(hwp, 0x37, 0x04, 0xFF);
117 
118     switch (pVia->Chipset) {
119     case VIA_CX700:
120     case VIA_VX800:
121     case VIA_VX855:
122     case VIA_VX900:
123         /* 3C5.5E[0] - CRT DACOFF Setting
124          *             1: CRT DACOFF controlled by 3C5.01[5] */
125         ViaSeqMask(hwp, 0x5E, 0x01, 0x01);
126         break;
127     default:
128         break;
129     }
130 
131     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
132                         "Exiting viaAnalogInit.\n"));
133 }
134 
135 /*
136  * Sets the polarity of horizontal synchronization and vertical
137  * synchronization.
138  */
139 static void
viaAnalogSetSyncPolarity(ScrnInfoPtr pScrn,DisplayModePtr mode)140 viaAnalogSetSyncPolarity(ScrnInfoPtr pScrn, DisplayModePtr mode)
141 {
142     vgaHWPtr hwp = VGAHWPTR(pScrn);
143     CARD8 miscRegister;
144 
145     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
146                         "Entered viaAnalogSetSyncPolarity.\n"));
147 
148 /* Set certain bits of miscellaneous output register
149  * meant for IGA1. */
150     miscRegister = hwp->readMiscOut(hwp);
151     if (mode->Flags & V_NHSYNC) {
152         miscRegister |= 0x40;
153     } else {
154         miscRegister &= (~0x40);
155     }
156 
157     if (mode->Flags & V_NVSYNC) {
158         miscRegister |= 0x80;
159     } else {
160         miscRegister &= (~0x80);
161     }
162 
163     hwp->writeMiscOut(hwp, miscRegister);
164 
165     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
166                         "Exiting viaAnalogSetSyncPolarity.\n"));
167 }
168 
169 
170 static void
via_analog_create_resources(xf86OutputPtr output)171 via_analog_create_resources(xf86OutputPtr output)
172 {
173 }
174 
175 static void
via_analog_dpms(xf86OutputPtr output,int mode)176 via_analog_dpms(xf86OutputPtr output, int mode)
177 {
178     ScrnInfoPtr pScrn = output->scrn;
179 
180     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
181                         "Entered via_analog_dpms.\n"));
182 
183     switch (mode) {
184     case DPMSModeOn:
185         viaAnalogOutput(pScrn, TRUE);
186         break;
187     case DPMSModeStandby:
188     case DPMSModeSuspend:
189     case DPMSModeOff:
190         viaAnalogOutput(pScrn, FALSE);
191         break;
192     default:
193         break;
194     }
195 
196     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
197                         "Exiting via_analog_dpms.\n"));
198 }
199 
200 static void
via_analog_save(xf86OutputPtr output)201 via_analog_save(xf86OutputPtr output)
202 {
203 }
204 
205 static void
via_analog_restore(xf86OutputPtr output)206 via_analog_restore(xf86OutputPtr output)
207 {
208 }
209 
210 static int
via_analog_mode_valid(xf86OutputPtr output,DisplayModePtr pMode)211 via_analog_mode_valid(xf86OutputPtr output, DisplayModePtr pMode)
212 {
213     ScrnInfoPtr pScrn = output->scrn;
214 
215     if (!ViaModeDotClockTranslate(pScrn, pMode))
216         return MODE_NOCLOCK;
217     return MODE_OK;
218 }
219 
220 static Bool
via_analog_mode_fixup(xf86OutputPtr output,DisplayModePtr mode,DisplayModePtr adjusted_mode)221 via_analog_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
222                       DisplayModePtr adjusted_mode)
223 {
224     return TRUE;
225 }
226 
227 static void
via_analog_prepare(xf86OutputPtr output)228 via_analog_prepare(xf86OutputPtr output)
229 {
230     via_analog_dpms(output, DPMSModeOff);
231 }
232 
233 static void
via_analog_commit(xf86OutputPtr output)234 via_analog_commit(xf86OutputPtr output)
235 {
236     via_analog_dpms(output, DPMSModeOn);
237 }
238 
239 static void
via_analog_mode_set(xf86OutputPtr output,DisplayModePtr mode,DisplayModePtr adjusted_mode)240 via_analog_mode_set(xf86OutputPtr output, DisplayModePtr mode,
241                     DisplayModePtr adjusted_mode)
242 {
243     ScrnInfoPtr pScrn = output->scrn;
244     drmmode_crtc_private_ptr iga = output->crtc->driver_private;
245 
246     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
247                         "Entered via_analog_mode_set.\n"));
248 
249     if (output->crtc) {
250         viaAnalogInit(pScrn);
251         viaAnalogSetSyncPolarity(pScrn, adjusted_mode);
252         viaAnalogSetDisplaySource(pScrn, iga->index ? 0x01 : 0x00);
253     }
254 
255     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
256                         "Exiting via_analog_mode_set.\n"));
257 }
258 
259 static xf86OutputStatus
via_analog_detect(xf86OutputPtr output)260 via_analog_detect(xf86OutputPtr output)
261 {
262     xf86OutputStatus status = XF86OutputStatusDisconnected;
263     ScrnInfoPtr pScrn = output->scrn;
264     VIAPtr pVia = VIAPTR(pScrn);
265     xf86MonPtr mon;
266 
267     /* Probe I2C Bus 1 to see if a VGA monitor is connected. */
268     xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
269                 "Probing for a VGA monitor on I2C Bus 1.\n");
270     mon = xf86OutputGetEDID(output, pVia->pI2CBus1);
271     if (mon && (!mon->features.input_type)) {
272         xf86OutputSetEDID(output, mon);
273         status = XF86OutputStatusConnected;
274         xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
275                     "Detected a VGA monitor on I2C Bus 1.\n");
276     } else {
277         xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
278                     "Did not detect a VGA monitor on I2C Bus 1.\n");
279 
280         /* Probe I2C Bus 2 to see if a VGA monitor is connected. */
281         xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
282                     "Probing for a VGA monitor on I2C Bus 2.\n");
283         mon = xf86OutputGetEDID(output, pVia->pI2CBus2);
284         if (mon && (!mon->features.input_type)) {
285             xf86OutputSetEDID(output, mon);
286             status = XF86OutputStatusConnected;
287             xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
288                         "Detected a VGA monitor on I2C Bus 2.\n");
289         } else {
290             xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
291                         "Did not detect a VGA monitor on I2C Bus 2.\n");
292 
293             /* Perform manual detection of a VGA monitor since */
294             /* it was not detected via I2C buses. */
295             xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
296                         "Now perform manual detection of a VGA "
297                         "monitor.\n");
298             vgaHWPtr hwp = VGAHWPTR(pScrn);
299             CARD8 SR01 = hwp->readSeq(hwp, 0x01);
300             CARD8 SR40 = hwp->readSeq(hwp, 0x40);
301             CARD8 CR36 = hwp->readCrtc(hwp, 0x36);
302 
303             /* We have to power on the display to detect it */
304             ViaSeqMask(hwp, 0x01, 0x00, 0x20);
305             ViaCrtcMask(hwp, 0x36, 0x00, 0xF0);
306 
307             /* Wait for vblank */
308             usleep(16);
309 
310             /* Detect the load on pins */
311             ViaSeqMask(hwp, 0x40, 0x80, 0x80);
312 
313             if ((VIA_CX700 == pVia->Chipset) ||
314                 (VIA_VX800 == pVia->Chipset) ||
315                 (VIA_VX855 == pVia->Chipset) ||
316                 (VIA_VX900 == pVia->Chipset))
317                 ViaSeqMask(hwp, 0x40, 0x00, 0x80);
318 
319             if (ViaVgahwIn(hwp, 0x3C2) & 0x20) {
320                 status = XF86OutputStatusConnected;
321                 xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
322                             "Detected a VGA monitor using manual "
323                             "detection method.\n");
324             }
325 
326             if ((VIA_CX700 == pVia->Chipset) ||
327                 (VIA_VX800 == pVia->Chipset) ||
328                 (VIA_VX855 == pVia->Chipset) ||
329                 (VIA_VX900 == pVia->Chipset))
330                 ViaSeqMask(hwp, 0x40, 0x00, 0x80);
331 
332             /* Restore previous state */
333             hwp->writeSeq(hwp, 0x40, SR40);
334             hwp->writeSeq(hwp, 0x01, SR01);
335             hwp->writeCrtc(hwp, 0x36, CR36);
336         }
337     }
338 
339     return status;
340 }
341 
342 #ifdef RANDR_12_INTERFACE
343 static Bool
via_analog_set_property(xf86OutputPtr output,Atom property,RRPropertyValuePtr value)344 via_analog_set_property(xf86OutputPtr output, Atom property,
345                         RRPropertyValuePtr value)
346 {
347     return TRUE;
348 }
349 #endif
350 
351 #ifdef RANDR_13_INTERFACE
352 static Bool
via_analog_get_property(xf86OutputPtr output,Atom property)353 via_analog_get_property(xf86OutputPtr output, Atom property)
354 {
355     return FALSE;
356 }
357 #endif
358 
359 static void
via_analog_destroy(xf86OutputPtr output)360 via_analog_destroy(xf86OutputPtr output)
361 {
362 }
363 
364 static const xf86OutputFuncsRec via_analog_funcs = {
365     .create_resources   = via_analog_create_resources,
366     .dpms               = via_analog_dpms,
367     .save               = via_analog_save,
368     .restore            = via_analog_restore,
369     .mode_valid         = via_analog_mode_valid,
370     .mode_fixup         = via_analog_mode_fixup,
371     .prepare            = via_analog_prepare,
372     .commit             = via_analog_commit,
373     .mode_set           = via_analog_mode_set,
374     .detect             = via_analog_detect,
375     .get_modes          = xf86OutputGetEDIDModes,
376 #ifdef RANDR_12_INTERFACE
377     .set_property       = via_analog_set_property,
378 #endif
379 #ifdef RANDR_13_INTERFACE
380     .get_property       = via_analog_get_property,
381 #endif
382     .destroy            = via_analog_destroy,
383 };
384 
385 void
via_analog_init(ScrnInfoPtr pScrn)386 via_analog_init(ScrnInfoPtr pScrn)
387 {
388     VIAPtr pVia = VIAPTR(pScrn);
389     VIABIOSInfoPtr pBIOSInfo = pVia->pBIOSInfo;
390     xf86OutputPtr output = NULL;
391     char outputNameBuffer[32];
392 
393     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
394                         "Entered via_analog_init.\n"));
395 
396     if (!pVia->pI2CBus1 || !pVia->pI2CBus2) {
397         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
398                     "I2C Bus 1 or I2C Bus 2 does not exist.\n");
399         DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
400                             "Exiting via_analog_init.\n"));
401         return;
402     }
403 
404     /* The code to dynamically designate the output name for
405      * xrandr was borrowed from xf86-video-r128 DDX. */
406     sprintf(outputNameBuffer, "VGA-%d", (pVia->numberVGA + 1));
407     output = xf86OutputCreate(pScrn, &via_analog_funcs, outputNameBuffer);
408 
409     /* While there are two (2) display controllers registered with the
410      * X.Org Server, it is often desirable to fix the analog VGA output
411      * to IGA1 since LVDS FP (Flat Panel) typically prefers IGA2. (While
412      * it is not used at this point, only IGA2 contains panel resolution
413      * scaling functionality. IGA1 does not have this.)
414      * With this arrangement, DVI should end up getting assigned to IGA2
415      * since DVI can go to either display controller without limitations.
416      * This should be the case for TV as well. */
417     output->possible_crtcs = (1 << 0);
418 
419     output->possible_clones = 0;
420     output->interlaceAllowed = TRUE;
421     output->doubleScanAllowed = FALSE;
422     pBIOSInfo->analog = output;
423 
424     /* Increment the number of analog VGA connectors. */
425     pVia->numberVGA++;
426 
427     DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
428                         "Exiting via_analog_init.\n"));
429 }
430