via_fp.c revision 983b4bf2
1/*
2 * Copyright 2007-2015 The Openchrome Project
3 *                     [https://www.freedesktop.org/wiki/Openchrome]
4 * Copyright 1998-2007 VIA Technologies, Inc. All Rights Reserved.
5 * Copyright 2001-2007 S3 Graphics, Inc. All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sub license,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 */
26
27/*
28 * Integrated LVDS power management functions.
29 */
30
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
35#include "via_driver.h"
36#include "via_mode.h"
37#include <unistd.h>
38
39/*
40 * Option handling.
41 */
42enum ViaPanelOpts {
43    OPTION_CENTER
44};
45
46static OptionInfoRec ViaPanelOptions[] =
47{
48    {OPTION_CENTER,     "Center",       OPTV_BOOLEAN,   {0},    FALSE},
49    {-1,                NULL,           OPTV_NONE,      {0},    FALSE}
50};
51
52/* These table values were copied from lcd.c of VIA Frame
53 * Buffer device driver. */
54/* {int Width, int Height, bool useDualEdge, bool useDithering}; */
55static ViaPanelModeRec ViaPanelNativeModes[] = {
56    { 640,  480, FALSE,  TRUE},
57    { 800,  600, FALSE,  TRUE},
58    {1024,  768, FALSE,  TRUE},
59    {1280,  768, FALSE,  TRUE},
60    {1280, 1024,  TRUE,  TRUE},
61    {1400, 1050,  TRUE,  TRUE},
62    {1600, 1200,  TRUE,  TRUE},
63    {1280,  800, FALSE,  TRUE},
64    { 800,  480, FALSE,  TRUE},
65    {1024,  768,  TRUE,  TRUE},
66    {1024,  768, FALSE, FALSE},
67    {1024,  768,  TRUE, FALSE},
68    {1280,  768, FALSE, FALSE},
69    {1280, 1024,  TRUE, FALSE},
70    {1400, 1050,  TRUE, FALSE},
71    {1600, 1200,  TRUE, FALSE},
72    {1366,  768, FALSE, FALSE},
73    {1024,  600, FALSE,  TRUE},
74    {1280,  768,  TRUE,  TRUE},
75    {1280,  800, FALSE,  TRUE},
76    {1360,  768, FALSE, FALSE},
77    {1280,  768,  TRUE, FALSE},
78    { 480,  640, FALSE,  TRUE},
79    {1200,  900, FALSE, FALSE}};
80
81#define MODEPREFIX(name) NULL, NULL, name, 0, M_T_DRIVER | M_T_DEFAULT
82#define MODESUFFIX 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,FALSE,FALSE,0,NULL,0,0.0,0.0
83
84static DisplayModeRec OLPCMode = {
85    MODEPREFIX("1200x900"),
86    57275, 1200, 1208, 1216, 1240, 0,
87    900,  905,  908,  912, 0,
88    V_NHSYNC | V_NVSYNC, MODESUFFIX
89};
90
91/*
92	1. Formula:
93		2^13 X 0.0698uSec [1/14.318MHz] = 8192 X 0.0698uSec =572.1uSec
94		Timer = Counter x 572 uSec
95	2. Note:
96		0.0698 uSec is too small to compute for hardware. So we multiply a
97		reference value(2^13) to make it big enough to compute for hardware.
98	3. Note:
99		The meaning of the TD0~TD3 are count of the clock.
100		TD(sec) = (sec)/(per clock) x (count of clocks)
101*/
102
103#define TD0 200
104#define TD1 25
105#define TD2 0
106#define TD3 25
107
108/*
109 * Sets CX700 or later single chipset's LVDS1 I/O pad state.
110 */
111void
112viaLVDS1SetIOPadSetting(ScrnInfoPtr pScrn, CARD8 ioPadState)
113{
114    vgaHWPtr hwp = VGAHWPTR(pScrn);
115
116    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
117                        "Entered viaLVDS1SetIOPadSetting.\n"));
118
119    /* Set LVDS1 I/O pad state. */
120    /* 3C5.2A[1:0] - LVDS1 I/O Pad Control */
121    ViaSeqMask(hwp, 0x2A, ioPadState, 0x03);
122    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
123                "LVDS1 I/O Pad State: %d\n",
124                (ioPadState & 0x03));
125
126    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
127                        "Exiting viaLVDS1SetIOPadSetting.\n"));
128}
129
130/*
131 * Sets IGA1 or IGA2 as the display output source for VIA Technologies
132 * Chrome IGP LVDS1 integrated LVDS transmitter.
133 */
134static void
135viaLVDS1SetDisplaySource(ScrnInfoPtr pScrn, CARD8 displaySource)
136{
137    vgaHWPtr hwp = VGAHWPTR(pScrn);
138    CARD8 temp = displaySource;
139
140    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
141                        "Entered viaLVDS1SetDisplaySource.\n"));
142
143    /* Set LVDS1 integrated LVDS transmitter display output source. */
144    /* 3X5.99[4] - LVDS Channel 1 Data Source Selection
145     *             0: Primary Display
146     *             1: Secondary Display */
147    ViaCrtcMask(hwp, 0x99, temp << 4, 0x10);
148    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
149                "LVDS1 Integrated LVDS Transmitter Display Output "
150                "Source: IGA%d\n",
151                (temp & 0x01) + 1);
152
153    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
154                        "Exiting viaLVDS1SetDisplaySource.\n"));
155}
156
157/*
158 * Sets LVDS1 (LVDS Channel 1) integrated LVDS transmitter format.
159 */
160static void
161viaLVDS1SetFormat(ScrnInfoPtr pScrn, CARD8 format)
162{
163    vgaHWPtr hwp = VGAHWPTR(pScrn);
164
165    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
166                        "Entered viaLVDS1SetFormat.\n"));
167
168    /* Set LVDS1 format. */
169    /* 3X5.D2[1] - LVDS Channel 1 Format Selection
170     *             0: SPWG Mode
171     *             1: OPENLDI Mode */
172    ViaCrtcMask(hwp, 0xD2, format << 1, 0x02);
173    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
174                "LVDS1 Format: %s\n",
175                (format & 0x01) ? "OPENLDI" : "SPWG");
176
177    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
178                        "Exiting viaLVDS1SetFormat.\n"));
179}
180
181/*
182 * Sets CX700 or later single chipset's LVDS2 I/O pad state.
183 */
184static void
185viaLVDS2SetIOPadSetting(ScrnInfoPtr pScrn, CARD8 ioPadState)
186{
187    vgaHWPtr hwp = VGAHWPTR(pScrn);
188
189    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
190                        "Entered viaLVDS2SetIOPadSetting.\n"));
191
192    /* Set LVDS2 I/O pad state. */
193    /* 3C5.2A[3:2] - LVDS2 I/O Pad Control */
194    ViaSeqMask(hwp, 0x2A, ioPadState << 2, 0x0C);
195    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
196                "LVDS2 I/O Pad State: %d\n",
197                (ioPadState & 0x03));
198
199    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
200                        "Exiting viaLVDS2SetIOPadSetting.\n"));
201}
202
203/*
204 * Sets IGA1 or IGA2 as the display output source for VIA Technologies
205 * Chrome IGP LVDS2 integrated LVDS transmitter.
206 */
207static void
208viaLVDS2SetDisplaySource(ScrnInfoPtr pScrn, CARD8 displaySource)
209{
210    vgaHWPtr hwp = VGAHWPTR(pScrn);
211    CARD8 temp = displaySource;
212
213    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
214                        "Entered viaLVDS2SetDisplaySource.\n"));
215
216    /* Set LVDS2 integrated LVDS transmitter display output source. */
217    /* 3X5.97[4] - LVDS Channel 2 Data Source Selection
218     *             0: Primary Display
219     *             1: Secondary Display */
220    ViaCrtcMask(hwp, 0x97, temp << 4, 0x10);
221    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
222                "LVDS2 Integrated LVDS Transmitter Display Output "
223                "Source: IGA%d\n",
224                (temp & 0x01) + 1);
225
226    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
227                        "Exiting viaLVDS2SetDisplaySource.\n"));
228}
229
230/*
231 * Sets LVDS2 (LVDS Channel 2) integrated LVDS transmitter delay tap.
232 */
233static void
234viaLVDS2SetDelayTap(ScrnInfoPtr pScrn, CARD8 delayTap)
235{
236    vgaHWPtr hwp = VGAHWPTR(pScrn);
237
238    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
239                        "Entered viaLVDS2SetDelayTap.\n"));
240
241    /* Set LVDS2 delay tap. */
242    /* 3X5.97[3:0] - LVDS2 Delay Tap */
243    ViaCrtcMask(hwp, 0x97, delayTap, 0x0F);
244
245    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
246                "LVDS2 Delay Tap: %d\n",
247                (delayTap & 0x0F));
248
249    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
250                        "Exiting viaLVDS2SetDelayTap.\n"));
251}
252
253/*
254 * Sets LVDS2 (LVDS Channel 2) integrated LVDS transmitter format.
255 */
256static void
257viaLVDS2SetFormat(ScrnInfoPtr pScrn, CARD8 format)
258{
259    vgaHWPtr hwp = VGAHWPTR(pScrn);
260
261    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
262                        "Entered viaLVDS2SetFormat.\n"));
263
264    /* Set LVDS2 format. */
265    /* 3X5.D2[0] - LVDS Channel 2 Format Selection
266     *             0: SPWG Mode
267     *             1: OPENLDI Mode */
268    ViaCrtcMask(hwp, 0xD2, format, 0x01);
269    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
270                "LVDS2 Format: %s\n",
271                (format & 0x01) ? "OPENLDI" : "SPWG");
272
273    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
274                        "Exiting viaLVDS2SetFormat.\n"));
275}
276
277/*
278 * Sets IGA1 or IGA2 as the display output source for VIA Technologies
279 * Chrome IGP DFP (Digital Flat Panel) High interface.
280 */
281static void
282viaDFPHighSetDisplaySource(ScrnInfoPtr pScrn, CARD8 displaySource)
283{
284    vgaHWPtr hwp = VGAHWPTR(pScrn);
285    CARD8 temp = displaySource;
286
287    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
288                        "Entered viaDFPHighSetDisplaySource.\n"));
289
290    /* Set DFP High display output source. */
291    /* 3X5.97[4] - DFP High Data Source Selection
292     *             0: Primary Display
293     *             1: Secondary Display */
294    ViaCrtcMask(hwp, 0x97, temp << 4, 0x10);
295    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
296                "DFP High Display Output Source: IGA%d\n",
297                (temp & 0x01) + 1);
298
299    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
300                        "Exiting viaDFPHighSetDisplaySource.\n"));
301}
302
303/*
304 * Sets DFP (Digital Flat Panel) Low interface delay tap.
305 */
306static void
307viaDFPLowSetDelayTap(ScrnInfoPtr pScrn, CARD8 delayTap)
308{
309    vgaHWPtr hwp = VGAHWPTR(pScrn);
310
311    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
312                        "Entered viaDFPLowSetDelayTap.\n"));
313
314    /* Set DFP Low interface delay tap. */
315    /* 3X5.99[3:0] - DFP Low Delay Tap */
316    ViaCrtcMask(hwp, 0x99, delayTap, 0x0F);
317    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
318                "DFP Low Delay Tap: %d\n",
319                (delayTap & 0x0F));
320
321    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
322                        "Exiting viaDFPLowSetDelayTap.\n"));
323}
324
325/*
326 * Sets DFP (Digital Flat Panel) High interface delay tap.
327 */
328static void
329viaDFPHighSetDelayTap(ScrnInfoPtr pScrn, CARD8 delayTap)
330{
331    vgaHWPtr hwp = VGAHWPTR(pScrn);
332
333    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
334                        "Entered viaDFPHighSetDelayTap.\n"));
335
336    /* Set DFP High interface delay tap. */
337    /* 3X5.97[3:0] - DFP High Delay Tap */
338    ViaCrtcMask(hwp, 0x97, delayTap, 0x0F);
339    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
340                "DFP High Delay Tap: %d\n",
341                (delayTap & 0x0F));
342
343    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
344                        "Exiting viaDFPHighSetDelayTap.\n"));
345}
346
347/*
348 * Turns LVDS2 output color dithering on or off. (18-bit color display vs.
349 * 24-bit color display)
350 */
351static void
352viaLVDS2SetDithering(ScrnInfoPtr pScrn, CARD8 ditheringStatus)
353{
354    vgaHWPtr hwp = VGAHWPTR(pScrn);
355
356    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
357                        "Entered viaLVDS2SetDithering.\n"));
358
359    /* Set LVDS2 output color dithering bit. */
360    /* 3X5.D4[6] - LVDS Channel 2 Output Bits
361     *             0: 24 bits (dithering off)
362     *             1: 18 bits (dithering on) */
363    ViaCrtcMask(hwp, 0xD4, ditheringStatus ? 0x40 : 0x00, 0x40);
364
365    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
366                "LVDS2 Output Color Dithering: %s\n",
367                ditheringStatus ? "On (18 bit)" : "Off (24 bit)");
368
369    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
370                        "Exiting viaLVDS2SetDithering.\n"));
371}
372
373/*
374 * Sets output format of LVDS2 to rotation or sequential mode.
375 */
376static void
377viaLVDS2SetOutputFormat(ScrnInfoPtr pScrn, CARD8 outputFormat)
378{
379    vgaHWPtr hwp = VGAHWPTR(pScrn);
380
381    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
382                        "Entered viaLVDS2SetOutputFormat.\n"));
383
384    /* Set LVDS2 output format. */
385    /* 3X5.D4[7] - LVDS Channel 2 Output Format
386     *             0: Rotation
387     *             1: Sequential */
388    ViaCrtcMask(hwp, 0xD4, outputFormat ? 0x80 : 0x00, 0x80);
389
390    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
391                "LVDS2 Output Format: %s\n",
392                outputFormat ? "Sequential" : "Rotation");
393
394    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
395                        "Exiting viaLVDS2SetOutputFormat.\n"));
396}
397
398/*
399 * Sets PCIe based 2 chip chipset's pin multiplexed DVP0 I/O pad state.
400 */
401static void
402viaDVP0PCIeSetIOPadSetting(ScrnInfoPtr pScrn, CARD8 ioPadState)
403{
404    vgaHWPtr hwp = VGAHWPTR(pScrn);
405
406    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
407                        "Entered viaDVP0PCIeSetIOPadSetting.\n"));
408
409    /* Set pin multiplexed DVP1 I/O pad state. */
410    /* 3C5.2A[3:2] - DVP0 I/O Pad Control */
411    ViaSeqMask(hwp, 0x2A, ioPadState << 2, 0x0C);
412    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
413                "DVP0 I/O Pad State: %d\n",
414                (ioPadState & 0x03));
415
416    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
417                        "Exiting viaDVP0PCIeSetIOPadSetting.\n"));
418}
419
420/*
421 * Sets PCIe based 2 chip chipset's pin multiplexed DVP1 I/O pad state.
422 */
423static void
424viaDVP1PCIeSetIOPadSetting(ScrnInfoPtr pScrn, CARD8 ioPadState)
425{
426    vgaHWPtr hwp = VGAHWPTR(pScrn);
427
428    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
429                        "Entered viaDVP1PCIeSetIOPadSetting.\n"));
430
431    /* Set pin multiplexed DVP0 I/O pad state. */
432    /* 3C5.2A[1:0] - DVP1 I/O Pad Control */
433    ViaSeqMask(hwp, 0x2A, ioPadState, 0x03);
434    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
435                "DVP1 I/O Pad State: %d\n",
436                (ioPadState & 0x03));
437
438    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
439                        "Exiting viaDVP1PCIeSetIOPadSetting.\n"));
440}
441
442static void
443viaFPIOPadSetting(ScrnInfoPtr pScrn, Bool ioPadOn)
444{
445    vgaHWPtr hwp = VGAHWPTR(pScrn);
446    VIAPtr pVia = VIAPTR(pScrn);
447    CARD8 sr12, sr13, sr5a;
448
449    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
450                        "Entered viaFPIOPadSetting.\n"));
451
452    if ((pVia->Chipset == VIA_CX700)
453        || (pVia->Chipset == VIA_VX800)
454        || (pVia->Chipset == VIA_VX855)
455        || (pVia->Chipset == VIA_VX900)) {
456
457        sr5a = hwp->readSeq(hwp, 0x5A);
458        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
459                            "SR5A: 0x%02X\n", sr5a));
460        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
461                            "Setting 3C5.5A[0] to 0.\n"));
462        ViaSeqMask(hwp, 0x5A, sr5a & 0xFE, 0x01);
463    }
464
465    sr12 = hwp->readSeq(hwp, 0x12);
466    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
467                        "SR12: 0x%02X\n", sr12));
468    sr13 = hwp->readSeq(hwp, 0x13);
469    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
470                        "SR13: 0x%02X\n", sr13));
471
472    switch (pVia->Chipset) {
473    case VIA_CLE266:
474        break;
475    case VIA_KM400:
476    case VIA_K8M800:
477    case VIA_PM800:
478    case VIA_P4M800PRO:
479        break;
480    case VIA_P4M890:
481    case VIA_K8M890:
482    case VIA_P4M900:
483        /* The tricky thing about VIA Technologies PCI Express based
484         * north bridge / south bridge 2 chip chipset is that
485         * it pin multiplexes DVP0 / DVP1 with north bridge's PCI
486         * Express x16 link. In particular, HP 2133 Mini-Note's WLAN
487         * is connected to north bridge's PCI Express Lane 0, but the
488         * Lane 0 is also pin multiplexed with DVP0. What this means is
489         * turning on DVP0 without probing the relevant strapping pin
490         * to determine the connected panel interface type will lead to
491         * the PCIe based WLAN to getting disabled by OpenChrome DDX
492         * when X.Org Server starts.
493         *     The current remedy for this will be to turn on DVP0
494         * only when an 18-bit / 24-bit interface flat panel is
495         * connected. */
496        /* 3C5.12[4] - DVP0D4 pin strapping
497         *             0: Use DVP1 only for a flat panel.
498         *             1: Use DVP0 and DVP1 for a flat panel */
499        if (sr12 & 0x10) {
500            /* Since an 18-bit / 24-bit flat panel is being used, actively
501             * control DVP0. */
502            viaDVP0PCIeSetIOPadSetting(pScrn, ioPadOn ? 0x03 : 0x00);
503        } else {
504            /* Keep DVP0 powered down. Otherwise, it will interfere with
505             * PCIe Lane 0 through 7. */
506            viaDVP0PCIeSetIOPadSetting(pScrn, 0x00);
507        }
508
509        /* Control DVP1 for a flat panel. */
510        viaDVP1PCIeSetIOPadSetting(pScrn, ioPadOn ? 0x03 : 0x00);
511        break;
512    case VIA_CX700:
513    case VIA_VX800:
514    case VIA_VX855:
515    case VIA_VX900:
516        /* 3C5.13[7:6] - DVP1D15 and DVP1D14 pin strappings
517         *               00: LVDS1 + LVDS2
518         *               01: DVI + LVDS2
519         *               10: Dual LVDS (LVDS1 + LVDS2 used
520         *                   simultaneously)
521         *               11: DVI only */
522        if ((((~(sr13 & 0x80)) && (~(sr13 & 0x40)))
523             || ((sr13 & 0x80) && (~(sr13 & 0x40))))
524           && (!pVia->isVIANanoBook)) {
525
526            viaLVDS1SetIOPadSetting(pScrn, ioPadOn ? 0x03 : 0x00);
527        }
528
529        if (((~(sr13 & 0x80)) || (~(sr13 & 0x40)))
530           || (pVia->isVIANanoBook)) {
531
532            viaLVDS2SetIOPadSetting(pScrn, ioPadOn ? 0x03 : 0x00);
533        }
534        break;
535    default:
536        break;
537    }
538
539    if ((pVia->Chipset == VIA_CX700)
540        || (pVia->Chipset == VIA_VX800)
541        || (pVia->Chipset == VIA_VX855)
542        || (pVia->Chipset == VIA_VX900)) {
543
544        hwp->writeSeq(hwp, 0x5A, sr5a);
545        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
546                            "Restoring 3C5.5A[0].\n"));
547    }
548
549    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
550                        "Exiting viaFPIOPadSetting.\n"));
551}
552
553static void
554ViaLVDSSoftwarePowerFirstSequence(ScrnInfoPtr pScrn, Bool on)
555{
556    vgaHWPtr hwp = VGAHWPTR(pScrn);
557
558    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, "ViaLVDSSoftwarePowerFirstSequence: %d\n", on));
559    if (on) {
560
561        /* Software control power sequence ON*/
562        hwp->writeCrtc(hwp, 0x91, hwp->readCrtc(hwp, 0x91) & 0x7F);
563        hwp->writeCrtc(hwp, 0x91, hwp->readCrtc(hwp, 0x91) | 0x01);
564        usleep(TD0);
565
566        /* VDD ON*/
567        hwp->writeCrtc(hwp, 0x91, hwp->readCrtc(hwp, 0x91) | 0x10);
568        usleep(TD1);
569
570        /* DATA ON */
571        hwp->writeCrtc(hwp, 0x91, hwp->readCrtc(hwp, 0x91) | 0x08);
572        usleep(TD2);
573
574        /* VEE ON (unused on vt3353)*/
575        hwp->writeCrtc(hwp, 0x91, hwp->readCrtc(hwp, 0x91) | 0x04);
576        usleep(TD3);
577
578        /* Back-Light ON */
579        hwp->writeCrtc(hwp, 0x91, hwp->readCrtc(hwp, 0x91) | 0x02);
580    } else {
581        /* Back-Light OFF */
582        hwp->writeCrtc(hwp, 0x91, hwp->readCrtc(hwp, 0x91) & 0xFD);
583        usleep(TD3);
584
585        /* VEE OFF (unused on vt3353)*/
586        hwp->writeCrtc(hwp, 0x91, hwp->readCrtc(hwp, 0x91) & 0xFB);
587        usleep(TD2);
588
589        /* DATA OFF */
590        hwp->writeCrtc(hwp, 0x91, hwp->readCrtc(hwp, 0x91) & 0xF7);
591        usleep(TD1);
592
593        /* VDD OFF */
594        hwp->writeCrtc(hwp, 0x91, hwp->readCrtc(hwp, 0x91) & 0xEF);
595    }
596}
597
598static void
599ViaLVDSSoftwarePowerSecondSequence(ScrnInfoPtr pScrn, Bool on)
600{
601    vgaHWPtr hwp = VGAHWPTR(pScrn);
602
603    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, "ViaLVDSSoftwarePowerSecondSequence: %d\n", on));
604    if (on) {
605        /* Secondary power hardware power sequence enable 0:off 1: on */
606        hwp->writeCrtc(hwp, 0xD4, hwp->readCrtc(hwp, 0xD4) & 0xFD);
607
608        /* Software control power sequence ON */
609        hwp->writeCrtc(hwp, 0xD3, hwp->readCrtc(hwp, 0xD3) | 0x01);
610        usleep(TD0);
611
612        /* VDD ON*/
613        hwp->writeCrtc(hwp, 0xD3, hwp->readCrtc(hwp, 0xD3) | 0x10);
614        usleep(TD1);
615
616        /* DATA ON */
617        hwp->writeCrtc(hwp, 0xD3, hwp->readCrtc(hwp, 0xD3) | 0x08);
618        usleep(TD2);
619
620        /* VEE ON (unused on vt3353)*/
621        hwp->writeCrtc(hwp, 0xD3, hwp->readCrtc(hwp, 0xD3) | 0x04);
622        usleep(TD3);
623
624        /* Back-Light ON */
625        hwp->writeCrtc(hwp, 0xD3, hwp->readCrtc(hwp, 0xD3) | 0x02);
626    } else {
627        /* Back-Light OFF */
628        hwp->writeCrtc(hwp, 0xD3, hwp->readCrtc(hwp, 0xD3) & 0xFD);
629        usleep(TD3);
630
631        /* VEE OFF */
632        hwp->writeCrtc(hwp, 0xD3, hwp->readCrtc(hwp, 0xD3) & 0xFB);
633        /* Delay TD2 msec. */
634        usleep(TD2);
635
636        /* DATA OFF */
637        hwp->writeCrtc(hwp, 0xD3, hwp->readCrtc(hwp, 0xD3) & 0xF7);
638        /* Delay TD1 msec. */
639        usleep(TD1);
640
641        /* VDD OFF */
642        hwp->writeCrtc(hwp, 0xD3, hwp->readCrtc(hwp, 0xD3) & 0xEF);
643    }
644}
645
646
647static void
648ViaLVDSHardwarePowerFirstSequence(ScrnInfoPtr pScrn, Bool on)
649{
650    vgaHWPtr hwp = VGAHWPTR(pScrn);
651
652    if (on) {
653        /* Use hardware control power sequence. */
654        hwp->writeCrtc(hwp, 0x91, hwp->readCrtc(hwp, 0x91) & 0xFE);
655        /* Turn on back light. */
656        hwp->writeCrtc(hwp, 0x91, hwp->readCrtc(hwp, 0x91) & 0x3F);
657        /* Turn on hardware power sequence. */
658        hwp->writeCrtc(hwp, 0x6A, hwp->readCrtc(hwp, 0x6A) | 0x08);
659    } else {
660        /* Turn off power sequence. */
661        hwp->writeCrtc(hwp, 0x6A, hwp->readCrtc(hwp, 0x6A) & 0xF7);
662        usleep(1);
663        /* Turn off back light. */
664        hwp->writeCrtc(hwp, 0x91, 0xC0);
665    }
666}
667
668static void
669ViaLVDSHardwarePowerSecondSequence(ScrnInfoPtr pScrn, Bool on)
670{
671    vgaHWPtr hwp = VGAHWPTR(pScrn);
672
673    if (on) {
674        /* Use hardware control power sequence. */
675        hwp->writeCrtc(hwp, 0xD3, hwp->readCrtc(hwp, 0xD3) & 0xFE);
676        /* Turn on back light. */
677        hwp->writeCrtc(hwp, 0xD3, hwp->readCrtc(hwp, 0xD3) & 0x3F);
678        /* Turn on hardware power sequence. */
679        hwp->writeCrtc(hwp, 0xD4, hwp->readCrtc(hwp, 0xD4) | 0x02);
680    } else {
681        /* Turn off power sequence. */
682        hwp->writeCrtc(hwp, 0xD4, hwp->readCrtc(hwp, 0xD4) & 0xFD);
683        usleep(1);
684        /* Turn off back light. */
685        hwp->writeCrtc(hwp, 0xD3, 0xC0);
686    }
687}
688
689static void
690ViaLVDSPowerChannel(ScrnInfoPtr pScrn, Bool on)
691{
692    vgaHWPtr hwp = VGAHWPTR(pScrn);
693    CARD8 lvdsMask;
694
695    if (on) {
696        /* LVDS0: 0x7F, LVDS1: 0xBF */
697        lvdsMask = 0x7F & 0xBF;
698        hwp->writeCrtc(hwp, 0xD2, hwp->readCrtc(hwp, 0xD2) & lvdsMask);
699    } else {
700        /* LVDS0: 0x80, LVDS1: 0x40 */
701        lvdsMask = 0x80 | 0x40;
702        hwp->writeCrtc(hwp, 0xD2, hwp->readCrtc(hwp, 0xD2) | lvdsMask);
703    }
704}
705
706static void
707ViaLVDSPower(ScrnInfoPtr pScrn, Bool Power_On)
708{
709    vgaHWPtr hwp = VGAHWPTR(pScrn);
710    VIAPtr pVia = VIAPTR(pScrn);
711    CARD8 crd2;
712
713    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
714                        "Entered ViaLVDSPower.\n"));
715
716    /*
717     * VX800, CX700 have HW issue, so we'd better use SW power sequence
718     * Fix Ticket #308
719     */
720    switch (pVia->Chipset) {
721    case VIA_CX700:
722    case VIA_VX800:
723
724        /* Is the integrated TMDS transmitter (DVI) not in use? */
725        crd2 = hwp->readCrtc(hwp, 0xD2);
726        if (((pVia->Chipset == VIA_CX700)
727                || (pVia->Chipset == VIA_VX800)
728                || (pVia->Chipset == VIA_VX855)
729                || (pVia->Chipset == VIA_VX900))
730            && (!(crd2 & 0x10))) {
731            ViaLVDSSoftwarePowerFirstSequence(pScrn, Power_On);
732        }
733
734        ViaLVDSSoftwarePowerSecondSequence(pScrn, Power_On);
735        break;
736
737    case VIA_VX855:
738    case VIA_VX900:
739        /* Is the integrated TMDS transmitter (DVI) not in use? */
740        crd2 = hwp->readCrtc(hwp, 0xD2);
741        if (((pVia->Chipset == VIA_CX700)
742                || (pVia->Chipset == VIA_VX800)
743                || (pVia->Chipset == VIA_VX855)
744                || (pVia->Chipset == VIA_VX900))
745            && (!(crd2 & 0x10))) {
746            ViaLVDSHardwarePowerFirstSequence(pScrn, Power_On);
747        }
748
749        ViaLVDSHardwarePowerSecondSequence(pScrn, Power_On);
750        break;
751    default:
752        ViaLVDSHardwarePowerFirstSequence(pScrn, Power_On);
753        ViaLVDSHardwarePowerSecondSequence(pScrn, Power_On);
754        break;
755    }
756
757    ViaLVDSPowerChannel(pScrn, Power_On);
758
759    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
760                "Integrated LVDS Flat Panel Power: %s\n",
761                Power_On ? "On" : "Off");
762
763    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
764                        "Exiting ViaLVDSPower.\n"));
765}
766
767static void
768ViaLCDPowerSequence(vgaHWPtr hwp, VIALCDPowerSeqRec Sequence)
769{
770    int i;
771
772    for (i = 0; i < Sequence.numEntry; i++) {
773        ViaVgahwMask(hwp, 0x300 + Sequence.port[i], Sequence.offset[i],
774                     0x301 + Sequence.port[i], Sequence.data[i],
775                     Sequence.mask[i]);
776        usleep(Sequence.delay[i]);
777    }
778}
779
780static void
781ViaLCDPower(xf86OutputPtr output, Bool Power_On)
782{
783    ViaPanelInfoPtr Panel = output->driver_private;
784    ScrnInfoPtr pScrn = output->scrn;
785    vgaHWPtr hwp = VGAHWPTR(pScrn);
786    VIAPtr pVia = VIAPTR(pScrn);
787    VIABIOSInfoPtr pBIOSInfo = pVia->pBIOSInfo;
788    int i;
789
790    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
791                        "Entered ViaLCDPower.\n"));
792
793    /* Enable LCD */
794    if (Power_On)
795        ViaCrtcMask(hwp, 0x6A, 0x08, 0x08);
796    else
797        ViaCrtcMask(hwp, 0x6A, 0x00, 0x08);
798
799    if (pBIOSInfo->LCDPower)
800        pBIOSInfo->LCDPower(pScrn, Power_On);
801
802    /* Find Panel Size Index for PowerSeq Table */
803    if (pVia->Chipset == VIA_CLE266) {
804        if (Panel->NativeModeIndex != VIA_PANEL_INVALID) {
805            for (i = 0; i < NumPowerOn; i++) {
806                if (lcdTable[Panel->PanelIndex].powerSeq
807                    == powerOn[i].powerSeq)
808                    break;
809            }
810        } else
811            i = 0;
812    } else
813        /* KM and K8M use PowerSeq Table index 2. */
814        i = 2;
815
816    usleep(1);
817    if (Power_On)
818        ViaLCDPowerSequence(hwp, powerOn[i]);
819    else
820        ViaLCDPowerSequence(hwp, powerOff[i]);
821    usleep(1);
822
823    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
824                "Integrated LVDS Flat Panel Power: %s\n",
825                Power_On ? "On" : "Off");
826
827    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
828                        "Exiting ViaLCDPower.\n"));
829}
830
831/*
832 * Try to interpret EDID ourselves.
833 */
834static Bool
835ViaPanelGetSizeFromEDID(ScrnInfoPtr pScrn, xf86MonPtr pMon,
836                        int *width, int *height)
837{
838    int i, max_hsize = 0, vsize = 0;
839
840    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, "VIAGetPanelSizeFromEDID\n"));
841
842    /* !!! Why are we not checking VESA modes? */
843
844    /* checking standard timings */
845    for (i = 0; i < STD_TIMINGS; i++)
846        if ((pMon->timings2[i].hsize > 256)
847            && (pMon->timings2[i].hsize > max_hsize)) {
848            max_hsize = pMon->timings2[i].hsize;
849            vsize = pMon->timings2[i].vsize;
850        }
851
852    if (max_hsize != 0) {
853        *width = max_hsize;
854        *height = vsize;
855        return TRUE;
856    }
857
858    /* checking detailed monitor section */
859
860    /* !!! skip Ranges and standard timings */
861
862    /* check detailed timings */
863    for (i = 0; i < DET_TIMINGS; i++)
864        if (pMon->det_mon[i].type == DT) {
865            struct detailed_timings timing = pMon->det_mon[i].section.d_timings;
866
867            /* ignore v_active for now */
868            if ((timing.clock > 15000000) && (timing.h_active > max_hsize)) {
869                max_hsize = timing.h_active;
870                vsize = timing.v_active;
871            }
872        }
873
874    if (max_hsize != 0) {
875        *width = max_hsize;
876        *height = vsize;
877        return TRUE;
878    }
879    return FALSE;
880}
881
882static Bool
883ViaPanelGetSizeFromDDCv1(xf86OutputPtr output, int *width, int *height)
884{
885    ScrnInfoPtr pScrn = output->scrn;
886    VIAPtr pVia = VIAPTR(pScrn);
887    xf86MonPtr pMon;
888
889    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
890                        "Entered VIAGetPanelSizeFromDDCv1.\n"));
891
892    if (!pVia->pI2CBus2) {
893        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
894                    "I2C Bus 2 does not exist.\n");
895        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
896                            "Exiting VIAGetPanelSizeFromDDCv1.\n"));
897        return FALSE;
898    }
899
900    if (!xf86I2CProbeAddress(pVia->pI2CBus2, 0xA0)) {
901        xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
902                    "I2C device on I2C Bus 2 does not support EDID.\n");
903        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
904                            "Exiting VIAGetPanelSizeFromDDCv1.\n"));
905        return FALSE;
906    }
907
908    /* Probe I2C Bus 2 to see if a flat panel is connected. */
909    xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
910                "Probing for a flat panel on I2C Bus 2.\n");
911    pMon = xf86OutputGetEDID(output, pVia->pI2CBus2);
912    if (pMon && DIGITAL(pMon->features.input_type)) {
913        xf86OutputSetEDID(output, pMon);
914        xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
915                    "Detected a flat panel on I2C Bus 2.\n");
916    } else {
917        xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
918                    "Did not detect a flat panel on I2C Bus 2.\n");
919        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
920                            "Exiting VIAGetPanelSizeFromDDCv1.\n"));
921        return FALSE;
922
923    }
924
925    if (!ViaPanelGetSizeFromEDID(pScrn, pMon, width, height)) {
926        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
927                    "Unable to obtain panel size from EDID information.\n");
928        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
929                            "Exiting VIAGetPanelSizeFromDDCv1.\n"));
930        return FALSE;
931    }
932
933    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
934                        "VIAGetPanelSizeFromDDCv1: (%d X %d)\n",
935                        *width, *height));
936    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
937                        "Exiting VIAGetPanelSizeFromDDCv1.\n"));
938    return TRUE;
939}
940
941/*
942 * Gets the native panel resolution from scratch pad registers.
943 */
944static void
945viaLVDSGetFPInfoFromScratchPad(xf86OutputPtr output)
946{
947    ScrnInfoPtr pScrn = output->scrn;
948    vgaHWPtr hwp = VGAHWPTR(pScrn);
949    ViaPanelInfoPtr panel = output->driver_private;
950    CARD8 index;
951
952    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
953                     "Entered viaLVDSGetFPInfoFromScratchPad.\n"));
954
955    index = hwp->readCrtc(hwp, 0x3F) & 0x0F;
956
957    panel->NativeModeIndex = index;
958    panel->NativeWidth = ViaPanelNativeModes[index].Width;
959    panel->NativeHeight = ViaPanelNativeModes[index].Height;
960    panel->useDualEdge = ViaPanelNativeModes[index].useDualEdge;
961    panel->useDithering = ViaPanelNativeModes[index].useDithering;
962
963    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
964               "VIA Technologies VGA BIOS Scratch Pad Register "
965               "Flat Panel Index: %d\n",
966               panel->NativeModeIndex);
967    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
968               "Flat Panel Native Resolution: %dx%d\n",
969               panel->NativeWidth, panel->NativeHeight);
970    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
971               "Flat Panel Dual Edge Transfer: %s\n",
972               panel->useDualEdge ? "On" : "Off");
973    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
974               "Flat Panel Output Color Dithering: %s\n",
975               panel->useDithering ? "On (18 bit)" : "Off (24 bit)");
976
977    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
978                     "Exiting viaLVDSGetFPInfoFromScratchPad.\n"));
979}
980
981static void
982ViaPanelCenterMode(DisplayModePtr mode, DisplayModePtr adjusted_mode)
983{
984    int panelHSyncTime = adjusted_mode->HSyncEnd - adjusted_mode->HSyncStart;
985    int panelVSyncTime = adjusted_mode->VSyncEnd - adjusted_mode->VSyncStart;
986    int panelHBlankStart = adjusted_mode->HDisplay;
987    int panelVBlankStart = adjusted_mode->VDisplay;
988    int hBorder = (adjusted_mode->HDisplay - mode->HDisplay)/2;
989    int vBorder = (adjusted_mode->VDisplay - mode->VDisplay)/2;
990    int newHBlankStart = hBorder + mode->HDisplay;
991    int newVBlankStart = vBorder + mode->VDisplay;
992
993    adjusted_mode->HDisplay = mode->HDisplay;
994    adjusted_mode->HSyncStart = (adjusted_mode->HSyncStart - panelHBlankStart) + newHBlankStart;
995    adjusted_mode->HSyncEnd = adjusted_mode->HSyncStart + panelHSyncTime;
996    adjusted_mode->VDisplay = mode->VDisplay;
997    adjusted_mode->VSyncStart = (adjusted_mode->VSyncStart - panelVBlankStart) + newVBlankStart;
998    adjusted_mode->VSyncEnd = adjusted_mode->VSyncStart + panelVSyncTime;
999    /* Adjust Crtc H and V */
1000    adjusted_mode->CrtcHDisplay = adjusted_mode->HDisplay;
1001    adjusted_mode->CrtcHBlankStart = newHBlankStart;
1002    adjusted_mode->CrtcHBlankEnd = adjusted_mode->CrtcHTotal - hBorder;
1003    adjusted_mode->CrtcHSyncStart = adjusted_mode->HSyncStart;
1004    adjusted_mode->CrtcHSyncEnd = adjusted_mode->HSyncEnd;
1005    adjusted_mode->CrtcVDisplay = adjusted_mode->VDisplay;
1006    adjusted_mode->CrtcVBlankStart = newVBlankStart;
1007    adjusted_mode->CrtcVBlankEnd = adjusted_mode->CrtcVTotal - vBorder;
1008    adjusted_mode->CrtcVSyncStart = adjusted_mode->VSyncStart;
1009    adjusted_mode->CrtcVSyncEnd = adjusted_mode->VSyncEnd;
1010}
1011
1012static void
1013ViaPanelScale(ScrnInfoPtr pScrn, int resWidth, int resHeight,
1014              int panelWidth, int panelHeight)
1015{
1016    VIAPtr pVia = VIAPTR(pScrn);
1017    vgaHWPtr hwp = VGAHWPTR(pScrn);
1018    int horScalingFactor = 0;
1019    int verScalingFactor = 0;
1020    CARD8 cra2 = 0;
1021    CARD8 cr77 = 0;
1022    CARD8 cr78 = 0;
1023    CARD8 cr79 = 0;
1024    CARD8 cr9f = 0;
1025    Bool scaling = FALSE;
1026
1027    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1028                    "ViaPanelScale: %d,%d -> %d,%d\n",
1029                    resWidth, resHeight, panelWidth, panelHeight));
1030
1031    if (resWidth < panelWidth) {
1032        /* Load Horizontal Scaling Factor */
1033        if (pVia->Chipset != VIA_CLE266 && pVia->Chipset != VIA_KM400) {
1034            horScalingFactor = ((resWidth - 1) * 4096) / (panelWidth - 1);
1035
1036            /* Horizontal scaling enabled */
1037            cra2 = 0xC0;
1038            cr9f = horScalingFactor & 0x0003;   /* HSCaleFactor[1:0] at CR9F[1:0] */
1039        } else {
1040            /* TODO: Need testing */
1041            horScalingFactor = ((resWidth - 1) * 1024) / (panelWidth - 1);
1042        }
1043
1044        cr77 = (horScalingFactor & 0x03FC) >> 2;   /* HSCaleFactor[9:2] at CR77[7:0] */
1045        cr79 = (horScalingFactor & 0x0C00) >> 10;  /* HSCaleFactor[11:10] at CR79[5:4] */
1046        cr79 <<= 4;
1047        scaling = TRUE;
1048    }
1049
1050    if (resHeight < panelHeight) {
1051        /* Load Vertical Scaling Factor */
1052        if (pVia->Chipset != VIA_CLE266 && pVia->Chipset != VIA_KM400) {
1053            verScalingFactor = ((resHeight - 1) * 2048) / (panelHeight - 1);
1054
1055            /* Vertical scaling enabled */
1056            cra2 |= 0x08;
1057            cr79 |= ((verScalingFactor & 0x0001) << 3); /* VSCaleFactor[0] at CR79[3] */
1058        } else {
1059            /* TODO: Need testing */
1060            verScalingFactor = ((resHeight - 1) * 1024) / (panelHeight - 1);
1061        }
1062
1063        cr78 |= (verScalingFactor & 0x01FE) >> 1;           /* VSCaleFactor[8:1] at CR78[7:0] */
1064
1065        cr79 |= ((verScalingFactor & 0x0600) >> 9) << 6;    /* VSCaleFactor[10:9] at CR79[7:6] */
1066        scaling = TRUE;
1067    }
1068
1069    if (scaling) {
1070        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1071                "Scaling factor: horizontal %d (0x%x), vertical %d (0x%x)\n",
1072        horScalingFactor, horScalingFactor,
1073        verScalingFactor, verScalingFactor));
1074
1075        ViaCrtcMask(hwp, 0x77, cr77, 0xFF);
1076        ViaCrtcMask(hwp, 0x78, cr78, 0xFF);
1077        ViaCrtcMask(hwp, 0x79, cr79, 0xF8);
1078
1079        if (pVia->Chipset != VIA_CLE266 && pVia->Chipset != VIA_KM400) {
1080            ViaCrtcMask(hwp, 0x9F, cr9f, 0x03);
1081        }
1082        ViaCrtcMask(hwp, 0x79, 0x03, 0x03);
1083    } else {
1084        /*  Disable panel scale */
1085        ViaCrtcMask(hwp, 0x79, 0x00, 0x01);
1086    }
1087
1088    if (pVia->Chipset != VIA_CLE266 && pVia->Chipset != VIA_KM400) {
1089        ViaCrtcMask(hwp, 0xA2, cra2, 0xC8);
1090    }
1091
1092    /* Horizontal scaling selection: interpolation */
1093    // ViaCrtcMask(hwp, 0x79, 0x02, 0x02);
1094    // else
1095    // ViaCrtcMask(hwp, 0x79, 0x00, 0x02);
1096    /* Horizontal scaling factor selection original / linear */
1097    //ViaCrtcMask(hwp, 0xA2, 0x40, 0x40);
1098}
1099
1100static void
1101ViaPanelScaleDisable(ScrnInfoPtr pScrn)
1102{
1103    VIAPtr pVia = VIAPTR(pScrn);
1104    vgaHWPtr hwp = VGAHWPTR(pScrn);
1105
1106    ViaCrtcMask(hwp, 0x79, 0x00, 0x01);
1107    /* Disable VX900 down scaling */
1108    if (pVia->Chipset == VIA_VX900)
1109        ViaCrtcMask(hwp, 0x89, 0x00, 0x01);
1110    if (pVia->Chipset != VIA_CLE266 && pVia->Chipset != VIA_KM400)
1111        ViaCrtcMask(hwp, 0xA2, 0x00, 0xC8);
1112}
1113
1114static void
1115via_lvds_create_resources(xf86OutputPtr output)
1116{
1117}
1118
1119static void
1120via_lvds_dpms(xf86OutputPtr output, int mode)
1121{
1122    ScrnInfoPtr pScrn = output->scrn;
1123    VIAPtr pVia = VIAPTR(pScrn);
1124
1125    switch (mode) {
1126    case DPMSModeOn:
1127        switch (pVia->Chipset) {
1128        case VIA_PM800:
1129        case VIA_P4M800PRO:
1130        case VIA_P4M890:
1131        case VIA_K8M890:
1132        case VIA_P4M900:
1133        case VIA_CX700:
1134        case VIA_VX800:
1135        case VIA_VX855:
1136        case VIA_VX900:
1137            ViaLVDSPower(pScrn, TRUE);
1138            break;
1139        default:
1140            ViaLCDPower(output, TRUE);
1141            break;
1142        }
1143
1144        viaFPIOPadSetting(pScrn, TRUE);
1145        break;
1146
1147    case DPMSModeStandby:
1148    case DPMSModeSuspend:
1149    case DPMSModeOff:
1150        switch (pVia->Chipset) {
1151        case VIA_PM800:
1152        case VIA_P4M800PRO:
1153        case VIA_P4M890:
1154        case VIA_K8M890:
1155        case VIA_P4M900:
1156        case VIA_CX700:
1157        case VIA_VX800:
1158        case VIA_VX855:
1159        case VIA_VX900:
1160            ViaLVDSPower(pScrn, FALSE);
1161            break;
1162        default:
1163            ViaLCDPower(output, FALSE);
1164            break;
1165        }
1166
1167        viaFPIOPadSetting(pScrn, FALSE);
1168        break;
1169    }
1170}
1171
1172static void
1173via_lvds_save(xf86OutputPtr output)
1174{
1175}
1176
1177static void
1178via_lvds_restore(xf86OutputPtr output)
1179{
1180    ViaLCDPower(output, TRUE);
1181}
1182
1183static int
1184via_lvds_mode_valid(xf86OutputPtr output, DisplayModePtr pMode)
1185{
1186    ScrnInfoPtr pScrn = output->scrn;
1187    ViaPanelInfoPtr Panel = output->driver_private;
1188
1189    if (Panel->NativeWidth < pMode->HDisplay ||
1190        Panel->NativeHeight < pMode->VDisplay)
1191        return MODE_PANEL;
1192
1193    if (!Panel->Scale && Panel->NativeHeight != pMode->VDisplay &&
1194         Panel->NativeWidth != pMode->HDisplay)
1195        return MODE_PANEL;
1196
1197    if (!ViaModeDotClockTranslate(pScrn, pMode))
1198        return MODE_NOCLOCK;
1199
1200    return MODE_OK;
1201}
1202
1203static Bool
1204via_lvds_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
1205                    DisplayModePtr adjusted_mode)
1206{
1207    ViaPanelInfoPtr Panel = output->driver_private;
1208
1209    xf86SetModeCrtc(adjusted_mode, 0);
1210    if (!Panel->Center && (mode->HDisplay < Panel->NativeWidth ||
1211        mode->VDisplay < Panel->NativeHeight)) {
1212        Panel->Scale = TRUE;
1213    } else {
1214        Panel->Scale = FALSE;
1215        ViaPanelCenterMode(mode, adjusted_mode);
1216    }
1217    return TRUE;
1218}
1219
1220static void
1221via_lvds_prepare(xf86OutputPtr output)
1222{
1223    ScrnInfoPtr pScrn = output->scrn;
1224
1225    via_lvds_dpms(output, DPMSModeOff);
1226    viaFPIOPadSetting(pScrn, FALSE);
1227}
1228
1229static void
1230via_lvds_commit(xf86OutputPtr output)
1231{
1232    ScrnInfoPtr pScrn = output->scrn;
1233
1234    via_lvds_dpms(output, DPMSModeOn);
1235    viaFPIOPadSetting(pScrn, TRUE);
1236}
1237
1238static void
1239via_lvds_mode_set(xf86OutputPtr output, DisplayModePtr mode,
1240                    DisplayModePtr adjusted_mode)
1241{
1242    ViaPanelInfoPtr Panel = output->driver_private;
1243    ScrnInfoPtr pScrn = output->scrn;
1244    drmmode_crtc_private_ptr iga = output->crtc->driver_private;
1245    VIAPtr pVia = VIAPTR(pScrn);
1246
1247    if (output->crtc) {
1248        if (Panel->Scale) {
1249            ViaPanelScale(pScrn, mode->HDisplay, mode->VDisplay,
1250                            Panel->NativeWidth,
1251                            Panel->NativeHeight);
1252        } else {
1253            ViaPanelScaleDisable(pScrn);
1254        }
1255
1256        switch (pVia->Chipset) {
1257        case VIA_P4M900:
1258            viaDFPLowSetDelayTap(pScrn, 0x08);
1259            break;
1260        case VIA_CX700:
1261            viaLVDS2SetDelayTap(pScrn, 0x01);
1262            break;
1263        default:
1264            break;
1265        }
1266
1267
1268        switch (pVia->Chipset) {
1269        case VIA_KM400:
1270        case VIA_K8M800:
1271        case VIA_PM800:
1272        case VIA_P4M800PRO:
1273            viaDFPLowSetDisplaySource(pScrn, iga->index ? 0x01 : 0x00);
1274            viaDFPHighSetDisplaySource(pScrn, iga->index ? 0x01 : 0x00);
1275            break;
1276        case VIA_P4M890:
1277        case VIA_K8M890:
1278        case VIA_P4M900:
1279            viaDFPLowSetDisplaySource(pScrn, iga->index ? 0x01 : 0x00);
1280            viaDVP1SetDisplaySource(pScrn, iga->index ? 0x01 : 0x00);
1281            break;
1282        case VIA_CX700:
1283        case VIA_VX800:
1284        case VIA_VX855:
1285        case VIA_VX900:
1286            viaLVDS2SetDisplaySource(pScrn, iga->index ? 0x01 : 0x00);
1287
1288            /* Set LVDS2 output color dithering. */
1289            viaLVDS2SetDithering(pScrn, Panel->useDithering ? TRUE : FALSE);
1290
1291            /* Set LVDS2 output format to sequential mode. */
1292            viaLVDS2SetOutputFormat(pScrn, 0x01);
1293
1294            /* Set LVDS2 output to OPENLDI mode. */
1295            viaLVDS2SetFormat(pScrn, 0x01);
1296            break;
1297        default:
1298            break;
1299        }
1300    }
1301}
1302
1303static xf86OutputStatus
1304via_lvds_detect(xf86OutputPtr output)
1305{
1306    xf86OutputStatus status = XF86OutputStatusDisconnected;
1307    ScrnInfoPtr pScrn = output->scrn;
1308    VIAPtr pVia = VIAPTR(pScrn);
1309    ViaPanelInfoPtr panel = output->driver_private;
1310
1311    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1312                        "Entered via_lvds_detect.\n"));
1313
1314    /* Hardcode panel size for the OLPC XO-1.5. */
1315    if (pVia->IsOLPCXO15) {
1316        xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
1317                    "Setting up OLPC XO-1.5 flat panel.\n");
1318        panel->NativeWidth = 1200;
1319        panel->NativeHeight = 900;
1320        status = XF86OutputStatusConnected;
1321        goto exit;
1322    }
1323
1324    /* For now, FP detection code will not scan the I2C bus
1325     * in order to obtain EDID since it is often used by DVI
1326     * as well. Hence, reading off the CRTC scratch pad register
1327     * supplied by the VGA BIOS is the only method available
1328     * to figure out the FP native screen resolution. */
1329    viaLVDSGetFPInfoFromScratchPad(output);
1330    status = XF86OutputStatusConnected;
1331
1332exit:
1333    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1334                        "Exiting via_lvds_detect.\n"));
1335    return status;
1336}
1337
1338static DisplayModePtr
1339via_lvds_get_modes(xf86OutputPtr output)
1340{
1341    ViaPanelInfoPtr pPanel = output->driver_private;
1342    ScrnInfoPtr pScrn = output->scrn;
1343    DisplayModePtr pDisplay_Mode = NULL;
1344
1345    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1346                        "Entered via_lvds_get_modes.\n"));
1347
1348    if (output->status == XF86OutputStatusConnected) {
1349        if (!output->MonInfo) {
1350            /*
1351             * Generates a display mode for the native panel resolution,
1352             * using CVT.
1353             */
1354            if (pPanel->NativeWidth && pPanel->NativeHeight) {
1355                VIAPtr pVia = VIAPTR(pScrn);
1356
1357                if (pVia->IsOLPCXO15) {
1358                    pDisplay_Mode = xf86DuplicateMode(&OLPCMode);
1359                } else {
1360                    pDisplay_Mode = xf86CVTMode(pPanel->NativeWidth, pPanel->NativeHeight,
1361                                    60.0f, FALSE, FALSE);
1362                }
1363
1364                if (pDisplay_Mode) {
1365                    pDisplay_Mode->CrtcHDisplay = pDisplay_Mode->HDisplay;
1366                    pDisplay_Mode->CrtcHSyncStart = pDisplay_Mode->HSyncStart;
1367                    pDisplay_Mode->CrtcHSyncEnd = pDisplay_Mode->HSyncEnd;
1368                    pDisplay_Mode->CrtcHTotal = pDisplay_Mode->HTotal;
1369                    pDisplay_Mode->CrtcHSkew = pDisplay_Mode->HSkew;
1370                    pDisplay_Mode->CrtcVDisplay = pDisplay_Mode->VDisplay;
1371                    pDisplay_Mode->CrtcVSyncStart = pDisplay_Mode->VSyncStart;
1372                    pDisplay_Mode->CrtcVSyncEnd = pDisplay_Mode->VSyncEnd;
1373                    pDisplay_Mode->CrtcVTotal = pDisplay_Mode->VTotal;
1374
1375                    pDisplay_Mode->CrtcVBlankStart = min(pDisplay_Mode->CrtcVSyncStart, pDisplay_Mode->CrtcVDisplay);
1376                    pDisplay_Mode->CrtcVBlankEnd = max(pDisplay_Mode->CrtcVSyncEnd, pDisplay_Mode->CrtcVTotal);
1377                    pDisplay_Mode->CrtcHBlankStart = min(pDisplay_Mode->CrtcHSyncStart, pDisplay_Mode->CrtcHDisplay);
1378                    pDisplay_Mode->CrtcHBlankEnd = max(pDisplay_Mode->CrtcHSyncEnd, pDisplay_Mode->CrtcHTotal);
1379                    pDisplay_Mode->type = M_T_DRIVER | M_T_PREFERRED;
1380                } else {
1381                    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1382                                "Out of memory. Size: %zu bytes\n", sizeof(DisplayModeRec));
1383                }
1384            } else {
1385                xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
1386                            "Invalid Flat Panel Screen Resolution: "
1387                            "%dx%d\n",
1388                            pPanel->NativeWidth, pPanel->NativeHeight);
1389            }
1390        } else {
1391            pDisplay_Mode = xf86OutputGetEDIDModes(output);
1392        }
1393    }
1394    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
1395                        "Exiting via_lvds_get_modes.\n"));
1396    return pDisplay_Mode;
1397}
1398
1399#ifdef RANDR_12_INTERFACE
1400static Bool
1401via_lvds_set_property(xf86OutputPtr output, Atom property,
1402                        RRPropertyValuePtr value)
1403{
1404    return FALSE;
1405}
1406
1407static Bool
1408via_lvds_get_property(xf86OutputPtr output, Atom property)
1409{
1410    return FALSE;
1411}
1412#endif
1413
1414static void
1415via_lvds_destroy(xf86OutputPtr output)
1416{
1417    if (output->driver_private)
1418        free(output->driver_private);
1419    output->driver_private = NULL;
1420}
1421
1422static const xf86OutputFuncsRec via_lvds_funcs = {
1423    .create_resources   = via_lvds_create_resources,
1424    .dpms               = via_lvds_dpms,
1425    .save               = via_lvds_save,
1426    .restore            = via_lvds_restore,
1427    .mode_valid         = via_lvds_mode_valid,
1428    .mode_fixup         = via_lvds_mode_fixup,
1429    .prepare            = via_lvds_prepare,
1430    .commit             = via_lvds_commit,
1431    .mode_set           = via_lvds_mode_set,
1432    .detect             = via_lvds_detect,
1433    .get_modes          = via_lvds_get_modes,
1434#ifdef RANDR_12_INTERFACE
1435    .set_property       = via_lvds_set_property,
1436#endif
1437#ifdef RANDR_13_INTERFACE
1438    .get_property       = via_lvds_get_property,
1439#endif
1440    .destroy            = via_lvds_destroy
1441};
1442
1443
1444void
1445via_lvds_init(ScrnInfoPtr pScrn)
1446{
1447    ViaPanelInfoPtr Panel = (ViaPanelInfoPtr) xnfcalloc(sizeof(ViaPanelInfoRec), 1);
1448    OptionInfoPtr  Options = xnfalloc(sizeof(ViaPanelOptions));
1449    MessageType from = X_DEFAULT;
1450    VIAPtr pVia = VIAPTR(pScrn);
1451    xf86OutputPtr output = NULL;
1452    vgaHWPtr hwp = VGAHWPTR(pScrn);
1453    CARD8 cr3b = 0x00;
1454    CARD8 cr3b_mask = 0x00;
1455    char outputNameBuffer[32];
1456
1457    if (!Panel)
1458        return;
1459
1460    /* Apparently this is the way VIA Technologies passes */
1461    /* the presence of a flat panel to the device driver */
1462    /* via BIOS setup. */
1463    if (pVia->Chipset == VIA_CLE266) {
1464        cr3b_mask = 0x08;
1465    } else {
1466        cr3b_mask = 0x02;
1467    }
1468
1469    cr3b = hwp->readCrtc(hwp, 0x3B) & cr3b_mask;
1470
1471    if (!cr3b) {
1472        return;
1473    }
1474
1475    memcpy(Options, ViaPanelOptions, sizeof(ViaPanelOptions));
1476    xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, Options);
1477
1478    Panel->NativeModeIndex = VIA_PANEL_INVALID;
1479
1480    /* LCD Center/Expend Option */
1481    Panel->Center = FALSE;
1482    from = xf86GetOptValBool(Options, OPTION_CENTER, &Panel->Center)
1483            ? X_CONFIG : X_DEFAULT;
1484    xf86DrvMsg(pScrn->scrnIndex, from, "LVDS-0 : DVI Center is %s.\n",
1485               Panel->Center ? "enabled" : "disabled");
1486
1487    /* The code to dynamically designate a particular FP (i.e., FP-1,
1488     * FP-2, etc.) for xrandr was borrowed from xf86-video-r128 DDX. */
1489    sprintf(outputNameBuffer, "FP-%d", (pVia->numberFP + 1));
1490    output = xf86OutputCreate(pScrn, &via_lvds_funcs, outputNameBuffer);
1491
1492    if (output)  {
1493        output->driver_private = Panel;
1494
1495        /* While there are two (2) display controllers registered with the
1496         * X.Org Server, it is often desirable to fix FP (Flat Panel) to
1497         * IGA2 since only IGA2 contains panel resolution scaling
1498         * functionality. IGA1 does not have this. */
1499        output->possible_crtcs = 1 << 1;
1500
1501        output->possible_clones = 0;
1502        output->interlaceAllowed = FALSE;
1503        output->doubleScanAllowed = FALSE;
1504
1505        /* Increment the number of FP connectors. */
1506        pVia->numberFP++;
1507
1508        if (pVia->IsOLPCXO15) {
1509            output->mm_height = 152;
1510            output->mm_width = 114;
1511        }
1512    } else {
1513        free(Panel);
1514    }
1515}
1516