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 */
48static void
49viaAnalogOutput(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 */
72static void
73viaAnalogSetDisplaySource(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 */
93static void
94viaAnalogInit(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 */
139static void
140viaAnalogSetSyncPolarity(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
170static void
171via_analog_create_resources(xf86OutputPtr output)
172{
173}
174
175static void
176via_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
200static void
201via_analog_save(xf86OutputPtr output)
202{
203}
204
205static void
206via_analog_restore(xf86OutputPtr output)
207{
208}
209
210static int
211via_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
220static Bool
221via_analog_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
222                      DisplayModePtr adjusted_mode)
223{
224    return TRUE;
225}
226
227static void
228via_analog_prepare(xf86OutputPtr output)
229{
230    via_analog_dpms(output, DPMSModeOff);
231}
232
233static void
234via_analog_commit(xf86OutputPtr output)
235{
236    via_analog_dpms(output, DPMSModeOn);
237}
238
239static void
240via_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
259static xf86OutputStatus
260via_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
343static Bool
344via_analog_set_property(xf86OutputPtr output, Atom property,
345                        RRPropertyValuePtr value)
346{
347    return TRUE;
348}
349#endif
350
351#ifdef RANDR_13_INTERFACE
352static Bool
353via_analog_get_property(xf86OutputPtr output, Atom property)
354{
355    return FALSE;
356}
357#endif
358
359static void
360via_analog_destroy(xf86OutputPtr output)
361{
362}
363
364static 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
385void
386via_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