via_tv.c revision 983b4bf2
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_tv.c
30 *
31 * Handles the initialization and management of TV output 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
43static void
44viaTVSetDisplaySource(ScrnInfoPtr pScrn, CARD8 displaySource)
45{
46
47    vgaHWPtr hwp = VGAHWPTR(pScrn);
48    VIAPtr pVia = VIAPTR(pScrn);
49    CARD8 sr12, sr13, sr5a;
50
51    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
52                        "Entered viaTVSetDisplaySource.\n"));
53
54    if ((pVia->Chipset == VIA_CX700)
55        || (pVia->Chipset == VIA_VX800)
56        || (pVia->Chipset == VIA_VX855)
57        || (pVia->Chipset == VIA_VX900)) {
58
59        sr5a = hwp->readSeq(hwp, 0x5A);
60        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
61                            "SR5A: 0x%02X\n", sr5a));
62        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
63                            "Setting 3C5.5A[0] to 0.\n"));
64        ViaSeqMask(hwp, 0x5A, sr5a & 0xFE, 0x01);
65    }
66
67    sr12 = hwp->readSeq(hwp, 0x12);
68    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
69                        "SR12: 0x%02X\n", sr12));
70    sr13 = hwp->readSeq(hwp, 0x13);
71    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
72                        "SR13: 0x%02X\n", sr13));
73    switch (pVia->Chipset) {
74    case VIA_CLE266:
75        /* 3C5.12[5] - FPD18 pin strapping
76         *             0: DIP0 (Digital Interface Port 0) is used by
77         *                a TMDS transmitter (DVI)
78         *             1: DIP0 (Digital Interface Port 0) is used by
79         *                a TV encoder */
80        if (sr12 & 0x20) {
81            viaDIP0SetDisplaySource(pScrn, displaySource);
82        } else {
83            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
84                        "DIP0 was not set up for "
85                        "an external TV encoder use.\n");
86        }
87
88        break;
89    case VIA_KM400:
90    case VIA_K8M800:
91    case VIA_PM800:
92    case VIA_P4M800PRO:
93        /* 3C5.13[3] - DVP0D8 pin strapping
94         *             0: AGP pins are used for AGP
95         *             1: AGP pins are used by FPDP
96         *                (Flat Panel Display Port)
97         * 3C5.12[6] - DVP0D6 pin strapping
98         *             0: Disable DVP0 (Digital Video Port 0)
99         *             1: Enable DVP0 (Digital Video Port 0)
100         * 3C5.12[5] - DVP0D5 pin strapping
101         *             0: DVP0 is used by a TMDS transmitter (DVI)
102         *             1: DVP0 is used by a TV encoder
103         * 3C5.12[4] - DVP0D4 pin strapping
104         *             0: Dual 12-bit FPDP (Flat Panel Display Port)
105         *             1: 24-bit FPDP (Flat Panel Display Port) */
106        if ((sr12 & 0x40) && (sr12 & 0x20)) {
107            viaDVP0SetDisplaySource(pScrn, displaySource);
108        } else if ((sr13 & 0x08) && (!(sr12 & 0x10))) {
109            viaDFPLowSetDisplaySource(pScrn, displaySource);
110        } else if (sr13 & 0x08) {
111            viaDVP1SetDisplaySource(pScrn, displaySource);
112        } else {
113            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
114                        "None of the external ports were set up for "
115                        "external TV encoder use.\n");
116        }
117
118        break;
119    case VIA_P4M890:
120    case VIA_K8M890:
121    case VIA_P4M900:
122        /* 3C5.12[6] - FPD6 pin strapping
123         *             0: Disable DVP0 (Digital Video Port 0)
124         *             1: Enable DVP0 (Digital Video Port 0)
125         * 3C5.12[5] - FPD5 pin strapping
126         *             0: DVP0 is used by a TMDS transmitter (DVI)
127         *             1: DVP0 is used by a TV encoder
128         * 3C5.12[4] - FPD4 pin strapping
129         *             0: Dual 12-bit FPDP (Flat Panel Display Port)
130         *             1: 24-bit FPDP (Flat Panel Display Port) */
131        if ((sr12 & 0x40) && (sr12 & 0x20) && (!(sr12 & 0x10))) {
132            viaDVP0SetDisplaySource(pScrn, displaySource);
133        } else {
134            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
135                        "Unrecognized external TV encoder use.\n"
136                        "Contact the developer for assistance.\n");
137        }
138
139        break;
140    case VIA_CX700:
141    case VIA_VX800:
142    case VIA_VX855:
143    case VIA_VX900:
144        /* 3C5.13[6] - DVP1 DVP / capture port selection
145         *             0: DVP1 is used as a DVP (Digital Video Port)
146         *             1: DVP1 is used as a capture port
147         */
148        if (!(sr13 & 0x40)) {
149            viaDVP1SetDisplaySource(pScrn, displaySource);
150        } else {
151            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
152                        "DVP1 is not set up for external TV "
153                        "encoder use.\n");
154        }
155
156        break;
157    default:
158        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
159                    "Unrecognized IGP for "
160                    "an external TV encoder use.\n");
161        break;
162    }
163
164    if ((pVia->Chipset == VIA_CX700)
165        || (pVia->Chipset == VIA_VX800)
166        || (pVia->Chipset == VIA_VX855)
167        || (pVia->Chipset == VIA_VX900)) {
168
169        hwp->writeSeq(hwp, 0x5A, sr5a);
170        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
171                            "Restoring 3C5.5A[0].\n"));
172    }
173
174    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
175                        "Exiting viaTVSetDisplaySource.\n"));
176}
177
178static void
179viaTVEnableIOPads(ScrnInfoPtr pScrn, CARD8 ioPadState)
180{
181    vgaHWPtr hwp = VGAHWPTR(pScrn);
182    VIAPtr pVia = VIAPTR(pScrn);
183    CARD8 sr12, sr13, sr5a;
184
185    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
186                        "Entered viaTVEnableIOPads.\n"));
187
188    if ((pVia->Chipset == VIA_CX700)
189        || (pVia->Chipset == VIA_VX800)
190        || (pVia->Chipset == VIA_VX855)
191        || (pVia->Chipset == VIA_VX900)) {
192
193        sr5a = hwp->readSeq(hwp, 0x5A);
194        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
195                            "SR5A: 0x%02X\n", sr5a));
196        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
197                            "Setting 3C5.5A[0] to 0.\n"));
198        ViaSeqMask(hwp, 0x5A, sr5a & 0xFE, 0x01);
199    }
200
201    sr12 = hwp->readSeq(hwp, 0x12);
202    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
203                        "SR12: 0x%02X\n", sr12));
204    sr13 = hwp->readSeq(hwp, 0x13);
205    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
206                        "SR13: 0x%02X\n", sr13));
207    switch (pVia->Chipset) {
208    case VIA_CLE266:
209        /* 3C5.12[5] - FPD18 pin strapping
210         *             0: DIP0 (Digital Interface Port 0) is used by
211         *                a TMDS transmitter (DVI)
212         *             1: DIP0 (Digital Interface Port 0) is used by
213         *                a TV encoder */
214        if (sr12 & 0x20) {
215            viaDIP0EnableIOPads(pScrn, ioPadState);
216        } else {
217            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
218                        "DIP0 is not set up for "
219                        "an external TV encoder use.\n");
220        }
221
222        break;
223    case VIA_KM400:
224    case VIA_K8M800:
225    case VIA_PM800:
226    case VIA_P4M800PRO:
227        /* 3C5.13[3] - DVP0D8 pin strapping
228         *             0: AGP pins are used for AGP
229         *             1: AGP pins are used by FPDP
230         *                (Flat Panel Display Port)
231         * 3C5.12[6] - DVP0D6 pin strapping
232         *             0: Disable DVP0 (Digital Video Port 0)
233         *             1: Enable DVP0 (Digital Video Port 0)
234         * 3C5.12[5] - DVP0D5 pin strapping
235         *             0: DVP0 is used by a TMDS transmitter (DVI)
236         *             1: DVP0 is used by a TV encoder
237         * 3C5.12[4] - DVP0D4 pin strapping
238         *             0: Dual 12-bit FPDP (Flat Panel Display Port)
239         *             1: 24-bit FPDP (Flat Panel Display Port) */
240        if ((sr12 & 0x40) && (sr12 & 0x20)) {
241            viaDVP0EnableIOPads(pScrn, ioPadState);
242        } else if ((sr13 & 0x08) && (!(sr12 & 0x10))) {
243            viaDFPLowEnableIOPads(pScrn, ioPadState);
244        } else if (sr13 & 0x08) {
245            viaDVP1EnableIOPads(pScrn, ioPadState);
246        } else {
247            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
248                        "None of the external ports were set up for "
249                        "external TV encoder use.\n");
250        }
251
252        break;
253    case VIA_P4M890:
254    case VIA_K8M890:
255    case VIA_P4M900:
256        /* 3C5.12[6] - FPD6 pin strapping
257         *             0: Disable DVP0 (Digital Video Port 0)
258         *             1: Enable DVP0 (Digital Video Port 0)
259         * 3C5.12[5] - FPD5 pin strapping
260         *             0: DVP0 is used by a TMDS transmitter (DVI)
261         *             1: DVP0 is used by a TV encoder
262         * 3C5.12[4] - FPD4 pin strapping
263         *             0: Dual 12-bit FPDP (Flat Panel Display Port)
264         *             1: 24-bit FPDP (Flat Panel Display Port) */
265        if ((sr12 & 0x40) && (sr12 & 0x20) && (!(sr12 & 0x10))) {
266            viaDVP0EnableIOPads(pScrn, ioPadState);
267        } else {
268            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
269                        "Unrecognized external TV encoder use.\n"
270                        "Contact the developer for assistance.\n");
271        }
272
273        break;
274    case VIA_CX700:
275    case VIA_VX800:
276    case VIA_VX855:
277    case VIA_VX900:
278        /* 3C5.13[6] - DVP1 DVP / capture port selection
279         *             0: DVP1 is used as a DVP (Digital Video Port)
280         *             1: DVP1 is used as a capture port
281         */
282        if (!(sr13 & 0x40)) {
283            viaDVP1EnableIOPads(pScrn, ioPadState);
284        } else {
285            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
286                        "DVP1 is not set up for external TV "
287                        "encoder use.\n");
288        }
289
290        break;
291    default:
292        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
293                    "Unrecognized IGP for "
294                    "an external TV encoder use.\n");
295        break;
296    }
297
298    if ((pVia->Chipset == VIA_CX700)
299        || (pVia->Chipset == VIA_VX800)
300        || (pVia->Chipset == VIA_VX855)
301        || (pVia->Chipset == VIA_VX900)) {
302
303        hwp->writeSeq(hwp, 0x5A, sr5a);
304        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
305                            "Restoring 3C5.5A[0].\n"));
306    }
307
308    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
309                        "Exiting viaTVEnableIOPads.\n"));
310}
311
312static void
313viaTVSetClockDriveStrength(ScrnInfoPtr pScrn, CARD8 clockDriveStrength)
314{
315    vgaHWPtr hwp = VGAHWPTR(pScrn);
316    VIAPtr pVia = VIAPTR(pScrn);
317    CARD8 sr12, sr13, sr5a;
318
319    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
320                        "Entered viaTVSetClockDriveStrength.\n"));
321
322    if ((pVia->Chipset == VIA_CX700)
323        || (pVia->Chipset == VIA_VX800)
324        || (pVia->Chipset == VIA_VX855)
325        || (pVia->Chipset == VIA_VX900)) {
326
327        sr5a = hwp->readSeq(hwp, 0x5A);
328        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
329                            "SR5A: 0x%02X\n", sr5a));
330        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
331                            "Setting 3C5.5A[0] to 0.\n"));
332        ViaSeqMask(hwp, 0x5A, sr5a & 0xFE, 0x01);
333    }
334
335    sr12 = hwp->readSeq(hwp, 0x12);
336    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
337                        "SR12: 0x%02X\n", sr12));
338    sr13 = hwp->readSeq(hwp, 0x13);
339    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
340                        "SR13: 0x%02X\n", sr13));
341    switch (pVia->Chipset) {
342    case VIA_CLE266:
343        /* 3C5.12[5] - FPD18 pin strapping
344         *             0: DIP0 (Digital Interface Port 0) is used by
345         *                a TMDS transmitter (DVI)
346         *             1: DIP0 (Digital Interface Port 0) is used by
347         *                a TV encoder */
348        if (sr12 & 0x20) {
349            viaDIP0SetClockDriveStrength(pScrn, clockDriveStrength);
350        }
351
352        break;
353    case VIA_KM400:
354    case VIA_K8M800:
355    case VIA_PM800:
356    case VIA_P4M800PRO:
357        /* 3C5.13[3] - DVP0D8 pin strapping
358         *             0: AGP pins are used for AGP
359         *             1: AGP pins are used by FPDP
360         *                (Flat Panel Display Port)
361         * 3C5.12[6] - DVP0D6 pin strapping
362         *             0: Disable DVP0 (Digital Video Port 0)
363         *             1: Enable DVP0 (Digital Video Port 0)
364         * 3C5.12[5] - DVP0D5 pin strapping
365         *             0: DVP0 is used by a TMDS transmitter (DVI)
366         *             1: DVP0 is used by a TV encoder
367         * 3C5.12[4] - DVP0D4 pin strapping
368         *             0: Dual 12-bit FPDP (Flat Panel Display Port)
369         *             1: 24-bit FPDP (Flat Panel Display Port) */
370        if ((sr12 & 0x40) && (sr12 & 0x20)) {
371            viaDVP0SetClockDriveStrength(pScrn, clockDriveStrength);
372        } else if ((sr13 & 0x08) && (!(sr12 & 0x10))) {
373        } else if (sr13 & 0x08) {
374            viaDVP1SetClockDriveStrength(pScrn, clockDriveStrength);
375        }
376
377        break;
378    case VIA_P4M890:
379    case VIA_K8M890:
380    case VIA_P4M900:
381        /* 3C5.12[6] - FPD6 pin strapping
382         *             0: Disable DVP0 (Digital Video Port 0)
383         *             1: Enable DVP0 (Digital Video Port 0)
384         * 3C5.12[5] - FPD5 pin strapping
385         *             0: DVP0 is used by a TMDS transmitter (DVI)
386         *             1: DVP0 is used by a TV encoder
387         * 3C5.12[4] - FPD4 pin strapping
388         *             0: Dual 12-bit FPDP (Flat Panel Display Port)
389         *             1: 24-bit FPDP (Flat Panel Display Port) */
390        if ((sr12 & 0x40) && (sr12 & 0x20) && (!(sr12 & 0x10))) {
391            viaDVP0SetClockDriveStrength(pScrn, clockDriveStrength);
392        }
393
394        break;
395    case VIA_CX700:
396    case VIA_VX800:
397    case VIA_VX855:
398    case VIA_VX900:
399        /* 3C5.13[6] - DVP1 DVP / capture port selection
400         *             0: DVP1 is used as a DVP (Digital Video Port)
401         *             1: DVP1 is used as a capture port */
402        if (!(sr13 & 0x40)) {
403            viaDVP1SetClockDriveStrength(pScrn, clockDriveStrength);
404        }
405
406        break;
407    default:
408        break;
409    }
410
411    if ((pVia->Chipset == VIA_CX700)
412        || (pVia->Chipset == VIA_VX800)
413        || (pVia->Chipset == VIA_VX855)
414        || (pVia->Chipset == VIA_VX900)) {
415
416        hwp->writeSeq(hwp, 0x5A, sr5a);
417        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
418                            "Restoring 3C5.5A[0].\n"));
419    }
420
421    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
422                        "Exiting viaTVSetClockDriveStrength.\n"));
423}
424
425static void
426viaTVSetDataDriveStrength(ScrnInfoPtr pScrn, CARD8 dataDriveStrength)
427{
428    vgaHWPtr hwp = VGAHWPTR(pScrn);
429    VIAPtr pVia = VIAPTR(pScrn);
430    CARD8 sr12, sr13, sr5a;
431
432    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
433                        "Entered viaTVSetDataDriveStrength.\n"));
434
435    if ((pVia->Chipset == VIA_CX700)
436        || (pVia->Chipset == VIA_VX800)
437        || (pVia->Chipset == VIA_VX855)
438        || (pVia->Chipset == VIA_VX900)) {
439
440        sr5a = hwp->readSeq(hwp, 0x5A);
441        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
442                            "SR5A: 0x%02X\n", sr5a));
443        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
444                            "Setting 3C5.5A[0] to 0.\n"));
445        ViaSeqMask(hwp, 0x5A, sr5a & 0xFE, 0x01);
446    }
447
448    sr12 = hwp->readSeq(hwp, 0x12);
449    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
450                        "SR12: 0x%02X\n", sr12));
451    sr13 = hwp->readSeq(hwp, 0x13);
452    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
453                        "SR13: 0x%02X\n", sr13));
454    switch (pVia->Chipset) {
455    case VIA_CLE266:
456        /* 3C5.12[5] - FPD18 pin strapping
457         *             0: DIP0 (Digital Interface Port 0) is used by
458         *                a TMDS transmitter (DVI)
459         *             1: DIP0 (Digital Interface Port 0) is used by
460         *                a TV encoder */
461        if (sr12 & 0x20) {
462            viaDIP0SetDataDriveStrength(pScrn, dataDriveStrength);
463        }
464
465        break;
466    case VIA_KM400:
467    case VIA_K8M800:
468    case VIA_PM800:
469    case VIA_P4M800PRO:
470        /* 3C5.13[3] - DVP0D8 pin strapping
471         *             0: AGP pins are used for AGP
472         *             1: AGP pins are used by FPDP
473         *                (Flat Panel Display Port)
474         * 3C5.12[6] - DVP0D6 pin strapping
475         *             0: Disable DVP0 (Digital Video Port 0)
476         *             1: Enable DVP0 (Digital Video Port 0)
477         * 3C5.12[5] - DVP0D5 pin strapping
478         *             0: DVP0 is used by a TMDS transmitter (DVI)
479         *             1: DVP0 is used by a TV encoder
480         * 3C5.12[4] - DVP0D4 pin strapping
481         *             0: Dual 12-bit FPDP (Flat Panel Display Port)
482         *             1: 24-bit FPDP (Flat Panel Display Port) */
483        if ((sr12 & 0x40) && (sr12 & 0x20)) {
484            viaDVP0SetDataDriveStrength(pScrn, dataDriveStrength);
485        } else if ((sr13 & 0x08) && (!(sr12 & 0x10))) {
486        } else if (sr13 & 0x08) {
487            viaDVP1SetDataDriveStrength(pScrn, dataDriveStrength);
488        }
489
490        break;
491    case VIA_P4M890:
492    case VIA_K8M890:
493    case VIA_P4M900:
494        /* 3C5.12[6] - FPD6 pin strapping
495         *             0: Disable DVP0 (Digital Video Port 0)
496         *             1: Enable DVP0 (Digital Video Port 0)
497         * 3C5.12[5] - FPD5 pin strapping
498         *             0: DVP0 is used by a TMDS transmitter (DVI)
499         *             1: DVP0 is used by a TV encoder
500         * 3C5.12[4] - FPD4 pin strapping
501         *             0: Dual 12-bit FPDP (Flat Panel Display Port)
502         *             1: 24-bit FPDP (Flat Panel Display Port) */
503        if ((sr12 & 0x40) && (sr12 & 0x20) && (!(sr12 & 0x10))) {
504            viaDVP0SetDataDriveStrength(pScrn, dataDriveStrength);
505        }
506
507        break;
508    case VIA_CX700:
509    case VIA_VX800:
510    case VIA_VX855:
511    case VIA_VX900:
512        /* 3C5.13[6] - DVP1 DVP / capture port selection
513         *             0: DVP1 is used as a DVP (Digital Video Port)
514         *             1: DVP1 is used as a capture port */
515        if (!(sr13 & 0x40)) {
516            viaDVP1SetDataDriveStrength(pScrn, dataDriveStrength);
517        }
518
519        break;
520    default:
521        break;
522    }
523
524    if ((pVia->Chipset == VIA_CX700)
525        || (pVia->Chipset == VIA_VX800)
526        || (pVia->Chipset == VIA_VX855)
527        || (pVia->Chipset == VIA_VX900)) {
528
529        hwp->writeSeq(hwp, 0x5A, sr5a);
530        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
531                            "Restoring 3C5.5A[0].\n"));
532    }
533
534    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
535                        "Exiting viaTVSetDataDriveStrength.\n"));
536}
537
538static void
539ViaTVSave(ScrnInfoPtr pScrn)
540{
541    VIABIOSInfoPtr pBIOSInfo = VIAPTR(pScrn)->pBIOSInfo;
542
543    if (pBIOSInfo->TVSave)
544        pBIOSInfo->TVSave(pScrn);
545}
546
547static void
548ViaTVRestore(ScrnInfoPtr pScrn)
549{
550    VIABIOSInfoPtr pBIOSInfo = VIAPTR(pScrn)->pBIOSInfo;
551
552    if (pBIOSInfo->TVRestore)
553        pBIOSInfo->TVRestore(pScrn);
554}
555
556static Bool
557ViaTVDACSense(ScrnInfoPtr pScrn)
558{
559    VIABIOSInfoPtr pBIOSInfo = VIAPTR(pScrn)->pBIOSInfo;
560
561    if (pBIOSInfo->TVDACSense)
562        return pBIOSInfo->TVDACSense(pScrn);
563    return FALSE;
564}
565
566static void
567ViaTVSetMode(xf86CrtcPtr crtc, DisplayModePtr mode)
568{
569    ScrnInfoPtr pScrn = crtc->scrn;
570    VIAPtr pVia = VIAPTR(pScrn);
571    VIABIOSInfoPtr pBIOSInfo = pVia->pBIOSInfo;
572
573    if (pBIOSInfo->TVModeI2C)
574        pBIOSInfo->TVModeI2C(pScrn, mode);
575
576    if (pBIOSInfo->TVModeCrtc)
577        pBIOSInfo->TVModeCrtc(crtc, mode);
578
579    /* TV reset. */
580    xf86I2CWriteByte(pBIOSInfo->TVI2CDev, 0x1D, 0x00);
581    xf86I2CWriteByte(pBIOSInfo->TVI2CDev, 0x1D, 0x80);
582}
583
584static void
585ViaTVPower(ScrnInfoPtr pScrn, Bool On)
586{
587    VIABIOSInfoPtr pBIOSInfo = VIAPTR(pScrn)->pBIOSInfo;
588
589#ifdef HAVE_DEBUG
590    if (On)
591        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "ViaTVPower: On.\n");
592    else
593        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "ViaTVPower: Off.\n");
594#endif
595
596    if (pBIOSInfo->TVPower)
597        pBIOSInfo->TVPower(pScrn, On);
598}
599
600#ifdef HAVE_DEBUG
601void
602ViaTVPrintRegs(ScrnInfoPtr pScrn)
603{
604    VIABIOSInfoPtr pBIOSInfo = VIAPTR(pScrn)->pBIOSInfo;
605
606    if (pBIOSInfo->TVPrintRegs)
607        pBIOSInfo->TVPrintRegs(pScrn);
608}
609#endif /* HAVE_DEBUG */
610
611static void
612via_tv_create_resources(xf86OutputPtr output)
613{
614}
615
616#ifdef RANDR_12_INTERFACE
617static Bool
618via_tv_set_property(xf86OutputPtr output, Atom property,
619                    RRPropertyValuePtr value)
620{
621    return TRUE;
622}
623
624static Bool
625via_tv_get_property(xf86OutputPtr output, Atom property)
626{
627    return FALSE;
628}
629#endif
630
631static void
632via_tv_dpms(xf86OutputPtr output, int mode)
633{
634    ScrnInfoPtr pScrn = output->scrn;
635
636    switch (mode) {
637    case DPMSModeOn:
638        ViaTVPower(pScrn, TRUE);
639        break;
640
641    case DPMSModeStandby:
642    case DPMSModeSuspend:
643    case DPMSModeOff:
644        ViaTVPower(pScrn, FALSE);
645        break;
646    }
647}
648
649static void
650via_tv_save(xf86OutputPtr output)
651{
652    ScrnInfoPtr pScrn = output->scrn;
653
654    ViaTVSave(pScrn);
655}
656
657static void
658via_tv_restore(xf86OutputPtr output)
659{
660    ScrnInfoPtr pScrn = output->scrn;
661
662    ViaTVRestore(pScrn);
663}
664
665static int
666via_tv_mode_valid(xf86OutputPtr output, DisplayModePtr pMode)
667{
668    ScrnInfoPtr pScrn = output->scrn;
669    int ret = MODE_OK;
670
671    if (!ViaModeDotClockTranslate(pScrn, pMode))
672        return MODE_NOCLOCK;
673
674    return ret;
675}
676
677static Bool
678via_tv_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
679                  DisplayModePtr adjusted_mode)
680{
681    return TRUE;
682}
683
684static void
685via_tv_prepare(xf86OutputPtr output)
686{
687    via_tv_dpms(output, DPMSModeOff);
688}
689
690static void
691via_tv_commit(xf86OutputPtr output)
692{
693    via_tv_dpms(output, DPMSModeOn);
694}
695
696static void
697via_tv_mode_set(xf86OutputPtr output, DisplayModePtr mode,
698                DisplayModePtr adjusted_mode)
699{
700    ScrnInfoPtr pScrn = output->scrn;
701    drmmode_crtc_private_ptr iga = output->crtc->driver_private;
702    VIAPtr pVia = VIAPTR(pScrn);
703
704    /* TV on FirstCrtc */
705    if (output->crtc) {
706        viaTVSetDisplaySource(pScrn, iga->index ? 0x01 : 0x00);
707
708        /* Set I/O pads to automatic on / off mode. */
709        viaTVEnableIOPads(pScrn, 0x03);
710
711        viaTVSetClockDriveStrength(pScrn, 0x03);
712        viaTVSetDataDriveStrength(pScrn, 0x03);
713
714        ViaTVSetMode(output->crtc, adjusted_mode);
715    }
716
717    pVia->FirstInit = FALSE;
718}
719
720static xf86OutputStatus
721via_tv_detect(xf86OutputPtr output)
722{
723    xf86OutputStatus status = XF86OutputStatusDisconnected;
724    ScrnInfoPtr pScrn = output->scrn;
725
726    if (ViaTVDACSense(pScrn))
727        status = XF86OutputStatusConnected;
728    return status;
729}
730
731static DisplayModePtr
732via_tv_get_modes(xf86OutputPtr output)
733{
734    DisplayModePtr modes = NULL, mode = NULL;
735    ScrnInfoPtr pScrn = output->scrn;
736    VIAPtr pVia = VIAPTR(pScrn);
737    int i;
738
739    for (i = 0; i < pVia->pBIOSInfo->TVNumModes; i++) {
740        mode = xf86DuplicateMode(&pVia->pBIOSInfo->TVModes[i]);
741        modes = xf86ModesAdd(modes, mode);
742    }
743    return modes;
744}
745
746static void
747via_tv_destroy(xf86OutputPtr output)
748{
749}
750
751static const xf86OutputFuncsRec via_tv_funcs = {
752    .create_resources   = via_tv_create_resources,
753#ifdef RANDR_12_INTERFACE
754    .set_property       = via_tv_set_property,
755#endif
756#ifdef RANDR_13_INTERFACE
757    .get_property       = via_tv_get_property,
758#endif
759    .dpms               = via_tv_dpms,
760    .save               = via_tv_save,
761    .restore            = via_tv_restore,
762    .mode_valid         = via_tv_mode_valid,
763    .mode_fixup         = via_tv_mode_fixup,
764    .prepare            = via_tv_prepare,
765    .commit             = via_tv_commit,
766    .mode_set           = via_tv_mode_set,
767    .detect             = via_tv_detect,
768    .get_modes          = via_tv_get_modes,
769    .destroy            = via_tv_destroy,
770};
771
772/*
773 *
774 */
775Bool
776via_tv_init(ScrnInfoPtr pScrn)
777{
778    VIAPtr pVia = VIAPTR(pScrn);
779    VIABIOSInfoPtr pBIOSInfo = pVia->pBIOSInfo;
780    xf86OutputPtr output = NULL;
781
782    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
783                        "Entered via_tv_init.\n"));
784
785    /* preset some pBIOSInfo TV related values -- move up */
786    pBIOSInfo->TVEncoder = VIA_NONETV;
787    pBIOSInfo->TVI2CDev = NULL;
788    pBIOSInfo->TVSave = NULL;
789    pBIOSInfo->TVRestore = NULL;
790    pBIOSInfo->TVDACSense = NULL;
791    pBIOSInfo->TVModeValid = NULL;
792    pBIOSInfo->TVModeI2C = NULL;
793    pBIOSInfo->TVModeCrtc = NULL;
794    pBIOSInfo->TVPower = NULL;
795    pBIOSInfo->TVModes = NULL;
796    pBIOSInfo->TVPrintRegs = NULL;
797    pBIOSInfo->LCDPower = NULL;
798    pBIOSInfo->TVNumRegs = 0;
799
800    /*
801     * On an SK43G (KM400/Ch7011), false positive detections at a VT162x
802     * chip were observed, so try to detect the Ch7011 first.
803     */
804    if (pVia->pI2CBus2 && xf86I2CProbeAddress(pVia->pI2CBus2, 0xEC))
805        pBIOSInfo->TVI2CDev = ViaCH7xxxDetect(pScrn, pVia->pI2CBus2, 0xEC);
806    else if (pVia->pI2CBus2 && xf86I2CProbeAddress(pVia->pI2CBus2, 0x40))
807        pBIOSInfo->TVI2CDev = ViaVT162xDetect(pScrn, pVia->pI2CBus2, 0x40);
808    else if (pVia->pI2CBus3 && xf86I2CProbeAddress(pVia->pI2CBus3, 0x40))
809        pBIOSInfo->TVI2CDev = ViaVT162xDetect(pScrn, pVia->pI2CBus3, 0x40);
810    else if (pVia->pI2CBus2 && xf86I2CProbeAddress(pVia->pI2CBus2, 0xEA))
811        pBIOSInfo->TVI2CDev = ViaCH7xxxDetect(pScrn, pVia->pI2CBus2, 0xEA);
812    else if (pVia->pI2CBus3 && xf86I2CProbeAddress(pVia->pI2CBus3, 0xEA))
813        pBIOSInfo->TVI2CDev = ViaCH7xxxDetect(pScrn, pVia->pI2CBus3, 0xEA);
814
815    if (!pBIOSInfo->TVI2CDev) {
816        xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
817                    "Did not detect a TV encoder.\n");
818        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
819                            "Exiting via_tv_init.\n"));
820
821        return FALSE;
822    }
823
824    switch (pBIOSInfo->TVEncoder) {
825        case VIA_VT1621:
826        case VIA_VT1622:
827        case VIA_VT1623:
828        case VIA_VT1625:
829            ViaVT162xInit(pScrn);
830            break;
831        case VIA_CH7011:
832        case VIA_CH7019A:
833        case VIA_CH7019B:
834            ViaCH7xxxInit(pScrn);
835            break;
836        default:
837            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
838                        "Was not able to initialize a known TV encoder.\n");
839            DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
840                                "Exiting via_tv_init.\n"));
841            return FALSE;
842            break;
843    }
844
845    if (!pBIOSInfo->TVSave || !pBIOSInfo->TVRestore
846        || !pBIOSInfo->TVDACSense || !pBIOSInfo->TVModeValid
847        || !pBIOSInfo->TVModeI2C || !pBIOSInfo->TVModeCrtc
848        || !pBIOSInfo->TVPower || !pBIOSInfo->TVModes
849        || !pBIOSInfo->TVPrintRegs) {
850
851        xf86DestroyI2CDevRec(pBIOSInfo->TVI2CDev, TRUE);
852
853        pBIOSInfo->TVI2CDev = NULL;
854        pBIOSInfo->TVOutput = TVOUTPUT_NONE;
855        pBIOSInfo->TVEncoder = VIA_NONETV;
856        pBIOSInfo->TVI2CDev = NULL;
857        pBIOSInfo->TVSave = NULL;
858        pBIOSInfo->TVRestore = NULL;
859        pBIOSInfo->TVDACSense = NULL;
860        pBIOSInfo->TVModeValid = NULL;
861        pBIOSInfo->TVModeI2C = NULL;
862        pBIOSInfo->TVModeCrtc = NULL;
863        pBIOSInfo->TVPower = NULL;
864        pBIOSInfo->TVModes = NULL;
865        pBIOSInfo->TVPrintRegs = NULL;
866        pBIOSInfo->TVNumRegs = 0;
867
868        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
869                   "TV encoder was not properly initialized.\n");
870        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
871                            "Exiting via_tv_init.\n"));
872        return FALSE;
873    }
874
875    output = xf86OutputCreate(pScrn, &via_tv_funcs, "TV-1");
876    pVia->FirstInit = TRUE;
877
878    if (output) {
879        /* Allow tv output on both crtcs, set bit 0 and 1. */
880        output->possible_crtcs = 0x3;
881    } else {
882        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
883                   "Failed to register TV-1.\n");
884    }
885
886    pBIOSInfo->tv = output;
887    /* Save now */
888    pBIOSInfo->TVSave(pScrn);
889
890#ifdef HAVE_DEBUG
891    if (VIAPTR(pScrn)->PrintTVRegs)
892        pBIOSInfo->TVPrintRegs(pScrn);
893#endif
894
895    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
896                        "Exiting via_tv_init.\n"));
897    return TRUE;
898}
899