17e6fb56fSmrg/*
2983b4bf2Smrg * Copyright 2009 Luc Verhaegen.
37e6fb56fSmrg * Copyright 2004 The Unichrome Project  [unichrome.sf.net]
47e6fb56fSmrg * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
57e6fb56fSmrg * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved.
67e6fb56fSmrg *
77e6fb56fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
87e6fb56fSmrg * copy of this software and associated documentation files (the "Software"),
97e6fb56fSmrg * to deal in the Software without restriction, including without limitation
107e6fb56fSmrg * the rights to use, copy, modify, merge, publish, distribute, sub license,
117e6fb56fSmrg * and/or sell copies of the Software, and to permit persons to whom the
127e6fb56fSmrg * Software is furnished to do so, subject to the following conditions:
137e6fb56fSmrg *
147e6fb56fSmrg * The above copyright notice and this permission notice (including the
157e6fb56fSmrg * next paragraph) shall be included in all copies or substantial portions
167e6fb56fSmrg * of the Software.
177e6fb56fSmrg *
187e6fb56fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
197e6fb56fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
207e6fb56fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
217e6fb56fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
227e6fb56fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
237e6fb56fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
247e6fb56fSmrg * DEALINGS IN THE SOFTWARE.
257e6fb56fSmrg */
267e6fb56fSmrg
277e6fb56fSmrg/*
28963d66acSmrg * Implements three I2C buses through registers SR26, SR2C, and SR31.
297e6fb56fSmrg */
307e6fb56fSmrg
317e6fb56fSmrg#ifdef HAVE_CONFIG_H
327e6fb56fSmrg#include "config.h"
337e6fb56fSmrg#endif
347e6fb56fSmrg
357e6fb56fSmrg#include "via_driver.h"
367e6fb56fSmrg
377e6fb56fSmrg#define SDA_READ  0x04
387e6fb56fSmrg#define SCL_READ  0x08
397e6fb56fSmrg#define SDA_WRITE 0x10
407e6fb56fSmrg#define SCL_WRITE 0x20
417e6fb56fSmrg
427e6fb56fSmrg/*
43963d66acSmrg * First I2C Bus: Typically used for detecting a VGA monitor.
447e6fb56fSmrg */
457e6fb56fSmrgstatic void
467e6fb56fSmrgViaI2C1PutBits(I2CBusPtr Bus, int clock, int data)
477e6fb56fSmrg{
4890b17f1bSmrg    vgaHWPtr hwp = Bus->DriverPrivate.ptr;
497e6fb56fSmrg    CARD8 value = 0x01; /* Enable */
507e6fb56fSmrg
517e6fb56fSmrg    if (clock)
527e6fb56fSmrg        value |= SCL_WRITE;
537e6fb56fSmrg
547e6fb56fSmrg    if (data)
557e6fb56fSmrg        value |= SDA_WRITE;
567e6fb56fSmrg
577e6fb56fSmrg    ViaSeqMask(hwp, 0x26, value, 0x01 | SCL_WRITE | SDA_WRITE);
587e6fb56fSmrg}
597e6fb56fSmrg
607e6fb56fSmrgstatic void
617e6fb56fSmrgViaI2C1GetBits(I2CBusPtr Bus, int *clock, int *data)
627e6fb56fSmrg{
6390b17f1bSmrg    vgaHWPtr hwp = Bus->DriverPrivate.ptr;
647e6fb56fSmrg    CARD8 value = hwp->readSeq(hwp, 0x26);
657e6fb56fSmrg
667e6fb56fSmrg    *clock = (value & SCL_READ) != 0;
677e6fb56fSmrg    *data = (value & SDA_READ) != 0;
687e6fb56fSmrg}
697e6fb56fSmrg
707e6fb56fSmrgstatic I2CBusPtr
7190b17f1bSmrgViaI2CBus1Init(ScrnInfoPtr pScrn)
727e6fb56fSmrg{
73963d66acSmrg    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
74963d66acSmrg                        "Entered ViaI2CBus1Init.\n"));
75963d66acSmrg
767e6fb56fSmrg    I2CBusPtr pI2CBus = xf86CreateI2CBusRec();
7790b17f1bSmrg    vgaHWPtr hwp = VGAHWPTR(pScrn);
787e6fb56fSmrg
79963d66acSmrg    if (!pI2CBus) {
80963d66acSmrg        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
81963d66acSmrg                            "xf86CreateI2CBusRec failed.\n"));
82963d66acSmrg        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
83963d66acSmrg                            "Initialization of I2C Bus 1 failed.\n");
84963d66acSmrg        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
85963d66acSmrg                            "Exiting ViaI2CBus1Init.\n"));
867e6fb56fSmrg        return NULL;
87963d66acSmrg    }
887e6fb56fSmrg
89963d66acSmrg    pI2CBus->BusName = "I2C Bus 1";
9090b17f1bSmrg    pI2CBus->scrnIndex = pScrn->scrnIndex;
917e6fb56fSmrg    pI2CBus->I2CPutBits = ViaI2C1PutBits;
927e6fb56fSmrg    pI2CBus->I2CGetBits = ViaI2C1GetBits;
9390b17f1bSmrg    pI2CBus->DriverPrivate.ptr = hwp;
9490b17f1bSmrg    pI2CBus->ByteTimeout = 2200;
9590b17f1bSmrg    pI2CBus->StartTimeout = 550;
9690b17f1bSmrg    pI2CBus->HoldTime = 40;
9790b17f1bSmrg    pI2CBus->BitTimeout = 40;
987e6fb56fSmrg
997e6fb56fSmrg    if (!xf86I2CBusInit(pI2CBus)) {
100963d66acSmrg        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
101963d66acSmrg                            "xf86I2CBusInit failed.\n"));
1027e6fb56fSmrg        xf86DestroyI2CBusRec(pI2CBus, TRUE, FALSE);
103963d66acSmrg        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
104963d66acSmrg                    "Initialization of I2C Bus 1 failed.\n");
105963d66acSmrg        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
106963d66acSmrg                            "Exiting ViaI2CBus1Init.\n"));
1077e6fb56fSmrg        return NULL;
1087e6fb56fSmrg    }
109963d66acSmrg
110963d66acSmrg    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
111963d66acSmrg                        "Exiting ViaI2CBus1Init.\n"));
1127e6fb56fSmrg    return pI2CBus;
1137e6fb56fSmrg}
1147e6fb56fSmrg
1157e6fb56fSmrg/*
116963d66acSmrg * Second I2C Bus: Used to detect a DVI monitor, VGA monitor via
117963d66acSmrg *                 a DVI-I connector, or TV encoders.
1187e6fb56fSmrg */
1197e6fb56fSmrgstatic void
1207e6fb56fSmrgViaI2C2PutBits(I2CBusPtr Bus, int clock, int data)
1217e6fb56fSmrg{
12290b17f1bSmrg    vgaHWPtr hwp = Bus->DriverPrivate.ptr;
1237e6fb56fSmrg    CARD8 value = 0x01; /* Enable */
1247e6fb56fSmrg
1257e6fb56fSmrg    if (clock)
1267e6fb56fSmrg        value |= SCL_WRITE;
1277e6fb56fSmrg
1287e6fb56fSmrg    if (data)
1297e6fb56fSmrg        value |= SDA_WRITE;
1307e6fb56fSmrg
1317e6fb56fSmrg    ViaSeqMask(hwp, 0x31, value, 0x01 | SCL_WRITE | SDA_WRITE);
1327e6fb56fSmrg}
1337e6fb56fSmrg
1347e6fb56fSmrgstatic void
1357e6fb56fSmrgViaI2C2GetBits(I2CBusPtr Bus, int *clock, int *data)
1367e6fb56fSmrg{
13790b17f1bSmrg    vgaHWPtr hwp = Bus->DriverPrivate.ptr;
1387e6fb56fSmrg    CARD8 value = hwp->readSeq(hwp, 0x31);
1397e6fb56fSmrg
1407e6fb56fSmrg    *clock = (value & SCL_READ) != 0;
1417e6fb56fSmrg    *data = (value & SDA_READ) != 0;
1427e6fb56fSmrg}
1437e6fb56fSmrg
1447e6fb56fSmrgstatic I2CBusPtr
14590b17f1bSmrgViaI2CBus2Init(ScrnInfoPtr pScrn)
1467e6fb56fSmrg{
147963d66acSmrg    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
148963d66acSmrg                        "Entered ViaI2CBus2Init.\n"));
149963d66acSmrg
1507e6fb56fSmrg    I2CBusPtr pI2CBus = xf86CreateI2CBusRec();
15190b17f1bSmrg    vgaHWPtr hwp = VGAHWPTR(pScrn);
1527e6fb56fSmrg
153963d66acSmrg    if (!pI2CBus) {
154963d66acSmrg        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
155963d66acSmrg                            "xf86CreateI2CBusRec failed.\n"));
156963d66acSmrg        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
157963d66acSmrg                    "Initialization of I2C Bus 2 failed.\n");
158963d66acSmrg        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
159963d66acSmrg                            "Exiting ViaI2CBus2Init.\n"));
1607e6fb56fSmrg        return NULL;
161963d66acSmrg    }
1627e6fb56fSmrg
163963d66acSmrg    pI2CBus->BusName = "I2C Bus 2";
16490b17f1bSmrg    pI2CBus->scrnIndex = pScrn->scrnIndex;
1657e6fb56fSmrg    pI2CBus->I2CPutBits = ViaI2C2PutBits;
1667e6fb56fSmrg    pI2CBus->I2CGetBits = ViaI2C2GetBits;
16790b17f1bSmrg    pI2CBus->DriverPrivate.ptr = hwp;
1687e6fb56fSmrg
1697e6fb56fSmrg    if (!xf86I2CBusInit(pI2CBus)) {
170963d66acSmrg        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
171963d66acSmrg                            "xf86I2CBusInit failed.\n"));
1727e6fb56fSmrg        xf86DestroyI2CBusRec(pI2CBus, TRUE, FALSE);
173963d66acSmrg        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
174963d66acSmrg                    "Initialization of I2C Bus 2 failed.\n");
175963d66acSmrg        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
176963d66acSmrg                            "Exiting ViaI2CBus2Init.\n"));
1777e6fb56fSmrg        return NULL;
1787e6fb56fSmrg    }
179963d66acSmrg
180963d66acSmrg    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
181963d66acSmrg                        "Exiting ViaI2CBus2Init.\n"));
1827e6fb56fSmrg    return pI2CBus;
1837e6fb56fSmrg}
1847e6fb56fSmrg
1857e6fb56fSmrg/*
186963d66acSmrg * Third I2C Bus: Implemented via manipulation of GPIO (General
187963d66acSmrg *                Purpose I/O) pins.
1887e6fb56fSmrg */
1897e6fb56fSmrgstatic Bool
1907e6fb56fSmrgViaI2C3Start(I2CBusPtr b, int timeout)
1917e6fb56fSmrg{
19290b17f1bSmrg    vgaHWPtr hwp = b->DriverPrivate.ptr;
1937e6fb56fSmrg
1947e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0xF0, 0xF0);
1957e6fb56fSmrg    b->I2CUDelay(b, b->RiseFallTime);
1967e6fb56fSmrg
1977e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0x00, 0x10);
1987e6fb56fSmrg    b->I2CUDelay(b, b->HoldTime);
1997e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0x00, 0x20);
2007e6fb56fSmrg    b->I2CUDelay(b, b->HoldTime);
2017e6fb56fSmrg
2027e6fb56fSmrg    return TRUE;
2037e6fb56fSmrg}
2047e6fb56fSmrg
2057e6fb56fSmrgstatic Bool
2067e6fb56fSmrgViaI2C3Address(I2CDevPtr d, I2CSlaveAddr addr)
2077e6fb56fSmrg{
2087e6fb56fSmrg    I2CBusPtr b = d->pI2CBus;
2097e6fb56fSmrg
2107e6fb56fSmrg#ifdef X_NEED_I2CSTART
2117e6fb56fSmrg    if (b->I2CStart(d->pI2CBus, d->StartTimeout)) {
2127e6fb56fSmrg#else
2137e6fb56fSmrg    if (ViaI2C3Start(d->pI2CBus, d->StartTimeout)) {
2147e6fb56fSmrg#endif
2157e6fb56fSmrg        if (b->I2CPutByte(d, addr & 0xFF)) {
2167e6fb56fSmrg            if ((addr & 0xF8) != 0xF0 && (addr & 0xFE) != 0x00)
2177e6fb56fSmrg                return TRUE;
2187e6fb56fSmrg
2197e6fb56fSmrg            if (b->I2CPutByte(d, (addr >> 8) & 0xFF))
2207e6fb56fSmrg                return TRUE;
2217e6fb56fSmrg        }
2227e6fb56fSmrg
2237e6fb56fSmrg        b->I2CStop(d);
2247e6fb56fSmrg    }
2257e6fb56fSmrg    return FALSE;
2267e6fb56fSmrg}
2277e6fb56fSmrg
2287e6fb56fSmrgstatic void
2297e6fb56fSmrgViaI2C3Stop(I2CDevPtr d)
2307e6fb56fSmrg{
2317e6fb56fSmrg    I2CBusPtr b = d->pI2CBus;
23290b17f1bSmrg    vgaHWPtr hwp = b->DriverPrivate.ptr;
2337e6fb56fSmrg
2347e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0xC0, 0xF0);
2357e6fb56fSmrg    b->I2CUDelay(b, b->RiseFallTime);
2367e6fb56fSmrg
2377e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0x20, 0x20);
2387e6fb56fSmrg    b->I2CUDelay(b, b->HoldTime);
2397e6fb56fSmrg
2407e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0x10, 0x10);
2417e6fb56fSmrg    b->I2CUDelay(b, b->HoldTime);
2427e6fb56fSmrg
2437e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0x00, 0x20);
2447e6fb56fSmrg    b->I2CUDelay(b, b->HoldTime);
2457e6fb56fSmrg}
2467e6fb56fSmrg
2477e6fb56fSmrgstatic void
2487e6fb56fSmrgViaI2C3PutBit(I2CBusPtr b, Bool sda, int timeout)
2497e6fb56fSmrg{
25090b17f1bSmrg    vgaHWPtr hwp = b->DriverPrivate.ptr;
2517e6fb56fSmrg
2527e6fb56fSmrg    if (sda)
2537e6fb56fSmrg        ViaSeqMask(hwp, 0x2C, 0x50, 0x50);
2547e6fb56fSmrg    else
2557e6fb56fSmrg        ViaSeqMask(hwp, 0x2C, 0x40, 0x50);
2567e6fb56fSmrg    b->I2CUDelay(b, b->RiseFallTime / 5);
2577e6fb56fSmrg
2587e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0xA0, 0xA0);
2597e6fb56fSmrg    b->I2CUDelay(b, b->HoldTime);
2607e6fb56fSmrg    b->I2CUDelay(b, timeout);
2617e6fb56fSmrg
2627e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0x80, 0xA0);
2637e6fb56fSmrg    b->I2CUDelay(b, b->RiseFallTime / 5);
2647e6fb56fSmrg}
2657e6fb56fSmrg
2667e6fb56fSmrgstatic Bool
2677e6fb56fSmrgViaI2C3PutByte(I2CDevPtr d, I2CByte data)
2687e6fb56fSmrg{
2697e6fb56fSmrg    I2CBusPtr b = d->pI2CBus;
27090b17f1bSmrg    vgaHWPtr hwp = b->DriverPrivate.ptr;
2717e6fb56fSmrg    Bool ret;
2727e6fb56fSmrg    int i;
2737e6fb56fSmrg
2747e6fb56fSmrg    for (i = 7; i >= 0; i--)
2757e6fb56fSmrg        ViaI2C3PutBit(b, (data >> i) & 0x01, b->BitTimeout);
2767e6fb56fSmrg
2777e6fb56fSmrg    /* Raise first to avoid false positives. */
2787e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0x50, 0x50);
2797e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0x00, 0x40);
2807e6fb56fSmrg    b->I2CUDelay(b, b->RiseFallTime);
2817e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0xA0, 0xA0);
2827e6fb56fSmrg
2837e6fb56fSmrg    if (hwp->readSeq(hwp, 0x2C) & 0x04)
2847e6fb56fSmrg        ret = FALSE;
2857e6fb56fSmrg    else
2867e6fb56fSmrg        ret = TRUE;
2877e6fb56fSmrg
2887e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0x80, 0xA0);
2897e6fb56fSmrg    b->I2CUDelay(b, b->RiseFallTime);
2907e6fb56fSmrg
2917e6fb56fSmrg    return ret;
2927e6fb56fSmrg}
2937e6fb56fSmrg
2947e6fb56fSmrgstatic Bool
2957e6fb56fSmrgViaI2C3GetBit(I2CBusPtr b, int timeout)
2967e6fb56fSmrg{
29790b17f1bSmrg    vgaHWPtr hwp = b->DriverPrivate.ptr;
2987e6fb56fSmrg    Bool ret;
2997e6fb56fSmrg
3007e6fb56fSmrg    ViaSeqMask(hwp, 0x2c, 0x80, 0xC0);
3017e6fb56fSmrg    b->I2CUDelay(b, b->RiseFallTime / 5);
3027e6fb56fSmrg    ViaSeqMask(hwp, 0x2c, 0xA0, 0xA0);
3037e6fb56fSmrg    b->I2CUDelay(b, 3 * b->HoldTime);
3047e6fb56fSmrg    b->I2CUDelay(b, timeout);
3057e6fb56fSmrg
3067e6fb56fSmrg    if (hwp->readSeq(hwp, 0x2C) & 0x04)
3077e6fb56fSmrg        ret = TRUE;
3087e6fb56fSmrg    else
3097e6fb56fSmrg        ret = FALSE;
3107e6fb56fSmrg
3117e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0x80, 0xA0);
3127e6fb56fSmrg    b->I2CUDelay(b, b->HoldTime);
3137e6fb56fSmrg    b->I2CUDelay(b, b->RiseFallTime / 5);
3147e6fb56fSmrg
3157e6fb56fSmrg    return ret;
3167e6fb56fSmrg}
3177e6fb56fSmrg
3187e6fb56fSmrgstatic Bool
3197e6fb56fSmrgViaI2C3GetByte(I2CDevPtr d, I2CByte * data, Bool last)
3207e6fb56fSmrg{
3217e6fb56fSmrg    I2CBusPtr b = d->pI2CBus;
32290b17f1bSmrg    vgaHWPtr hwp = b->DriverPrivate.ptr;
3237e6fb56fSmrg    int i;
3247e6fb56fSmrg
3257e6fb56fSmrg    *data = 0x00;
3267e6fb56fSmrg
3277e6fb56fSmrg    for (i = 7; i >= 0; i--)
3287e6fb56fSmrg        if (ViaI2C3GetBit(b, b->BitTimeout))
3297e6fb56fSmrg            *data |= 0x01 << i;
3307e6fb56fSmrg
3317e6fb56fSmrg    if (last)   /* send NACK */
3327e6fb56fSmrg        ViaSeqMask(hwp, 0x2C, 0x50, 0x50);
3337e6fb56fSmrg    else        /* send ACK */
3347e6fb56fSmrg        ViaSeqMask(hwp, 0x2C, 0x40, 0x50);
3357e6fb56fSmrg
3367e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0xA0, 0xA0);
3377e6fb56fSmrg    b->I2CUDelay(b, b->HoldTime);
3387e6fb56fSmrg
3397e6fb56fSmrg    ViaSeqMask(hwp, 0x2C, 0x80, 0xA0);
3407e6fb56fSmrg
3417e6fb56fSmrg    return TRUE;
3427e6fb56fSmrg}
3437e6fb56fSmrg
344963d66acSmrgstatic void
345963d66acSmrgViaI2C3SimplePutBits(I2CBusPtr Bus, int clock, int data)
346963d66acSmrg{
347963d66acSmrg    vgaHWPtr hwp = Bus->DriverPrivate.ptr;
348963d66acSmrg    CARD8 value = 0xC0;
349963d66acSmrg
350963d66acSmrg    if (clock)
351963d66acSmrg        value |= SCL_WRITE;
352963d66acSmrg
353963d66acSmrg    if (data)
354963d66acSmrg        value |= SDA_WRITE;
355963d66acSmrg
356963d66acSmrg    ViaSeqMask(hwp, 0x2C, value, 0xC0 | SCL_WRITE | SDA_WRITE);
357963d66acSmrg}
358963d66acSmrg
359963d66acSmrgstatic void
360963d66acSmrgViaI2C3SimpleGetBits(I2CBusPtr Bus, int *clock, int *data)
361963d66acSmrg{
362963d66acSmrg    vgaHWPtr hwp = Bus->DriverPrivate.ptr;
363963d66acSmrg    CARD8 value = hwp->readSeq(hwp, 0x2C);
364963d66acSmrg
365963d66acSmrg    *clock = (value & SCL_READ) != 0;
366963d66acSmrg    *data = (value & SDA_READ) != 0;
367963d66acSmrg}
368963d66acSmrg
3697e6fb56fSmrgstatic I2CBusPtr
37090b17f1bSmrgViaI2CBus3Init(ScrnInfoPtr pScrn)
3717e6fb56fSmrg{
372963d66acSmrg    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
373963d66acSmrg                        "Entered ViaI2CBus3Init.\n"));
374963d66acSmrg
3757e6fb56fSmrg    I2CBusPtr pI2CBus = xf86CreateI2CBusRec();
37690b17f1bSmrg    vgaHWPtr hwp = VGAHWPTR(pScrn);
377963d66acSmrg    VIAPtr pVia = VIAPTR(pScrn);
3787e6fb56fSmrg
379963d66acSmrg    if (!pI2CBus) {
380963d66acSmrg        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
381963d66acSmrg                            "xf86CreateI2CBusRec failed.\n"));
382963d66acSmrg        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
383963d66acSmrg                    "Initialization of I2C Bus 3 failed.\n");
384963d66acSmrg        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
385963d66acSmrg                            "Exiting ViaI2CBus3Init.\n"));
3867e6fb56fSmrg        return NULL;
387963d66acSmrg    }
3887e6fb56fSmrg
389963d66acSmrg    pI2CBus->BusName = "I2C Bus 3";
39090b17f1bSmrg    pI2CBus->scrnIndex = pScrn->scrnIndex;
39190b17f1bSmrg    pI2CBus->DriverPrivate.ptr = hwp;
3927e6fb56fSmrg
393963d66acSmrg    switch (pVia->Chipset) {
394963d66acSmrg        case VIA_P4M800PRO:
395963d66acSmrg            DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
396963d66acSmrg                                "using alternative PutBits/GetBits functions for I2C Bus 3\n"));
397963d66acSmrg            pI2CBus->I2CPutBits = ViaI2C3SimplePutBits;
398963d66acSmrg            pI2CBus->I2CGetBits = ViaI2C3SimpleGetBits;
399963d66acSmrg            break;
400963d66acSmrg        default:
401963d66acSmrg            pI2CBus->I2CAddress = ViaI2C3Address;
402963d66acSmrg#ifdef X_NEED_I2CSTART
403963d66acSmrg            pI2CBus->I2CStart = ViaI2C3Start;
404963d66acSmrg#endif
405963d66acSmrg            pI2CBus->I2CStop = ViaI2C3Stop;
406963d66acSmrg            pI2CBus->I2CPutByte = ViaI2C3PutByte;
407963d66acSmrg            pI2CBus->I2CGetByte = ViaI2C3GetByte;
408963d66acSmrg            pI2CBus->DriverPrivate.ptr = hwp;
409963d66acSmrg
410963d66acSmrg            pI2CBus->BitTimeout = 10;
411963d66acSmrg            pI2CBus->ByteTimeout = 10;
412963d66acSmrg            pI2CBus->HoldTime = 10;
413963d66acSmrg            pI2CBus->StartTimeout = 10;
414963d66acSmrg            break;
415963d66acSmrg    }
4167e6fb56fSmrg
4177e6fb56fSmrg    if (!xf86I2CBusInit(pI2CBus)) {
418963d66acSmrg        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
419963d66acSmrg                            "xf86I2CBusInit failed.\n"));
4207e6fb56fSmrg        xf86DestroyI2CBusRec(pI2CBus, TRUE, FALSE);
421963d66acSmrg        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
422963d66acSmrg                    "Initialization of I2C Bus 3 failed.\n");
423963d66acSmrg        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
424963d66acSmrg                            "Exiting ViaI2CBus3Init.\n"));
4257e6fb56fSmrg        return NULL;
4267e6fb56fSmrg    }
427963d66acSmrg
428963d66acSmrg    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
429963d66acSmrg                        "Exiting ViaI2CBus3Init.\n"));
4307e6fb56fSmrg    return pI2CBus;
4317e6fb56fSmrg}
4327e6fb56fSmrg
4337e6fb56fSmrg#ifdef HAVE_DEBUG
4347e6fb56fSmrgstatic void
4357e6fb56fSmrgViaI2CScan(I2CBusPtr Bus)
4367e6fb56fSmrg{
4377e6fb56fSmrg    CARD8 i;
4387e6fb56fSmrg
439963d66acSmrg    DEBUG(xf86DrvMsg(Bus->scrnIndex, X_INFO,
440963d66acSmrg                        "Entered ViaI2CScan.\n"));
441963d66acSmrg
442963d66acSmrg    xf86DrvMsg(Bus->scrnIndex, X_INFO, "Scanning %s.\n",
4437e6fb56fSmrg               Bus->BusName);
4447e6fb56fSmrg
4457e6fb56fSmrg    for (i = 0x10; i < 0xF0; i += 2)
4467e6fb56fSmrg        if (xf86I2CProbeAddress(Bus, i))
4477e6fb56fSmrg            xf86DrvMsg(Bus->scrnIndex, X_PROBED, "Found slave on %s "
448963d66acSmrg                       "- 0x%02X.\n", Bus->BusName, i);
449963d66acSmrg
450963d66acSmrg    DEBUG(xf86DrvMsg(Bus->scrnIndex, X_INFO,
451963d66acSmrg                        "Exiting ViaI2CScan.\n"));
4527e6fb56fSmrg}
4537e6fb56fSmrg#endif /* HAVE_DEBUG */
4547e6fb56fSmrg
4557e6fb56fSmrgvoid
4567e6fb56fSmrgViaI2CInit(ScrnInfoPtr pScrn)
4577e6fb56fSmrg{
4587e6fb56fSmrg    VIAPtr pVia = VIAPTR(pScrn);
4597e6fb56fSmrg
460963d66acSmrg    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
461963d66acSmrg                        "Entered ViaI2CInit.\n"));
4627e6fb56fSmrg
46390b17f1bSmrg    if (pVia->I2CDevices & VIA_I2C_BUS1)
46490b17f1bSmrg        pVia->pI2CBus1 = ViaI2CBus1Init(pScrn);
46590b17f1bSmrg    if (pVia->I2CDevices & VIA_I2C_BUS2)
46690b17f1bSmrg        pVia->pI2CBus2 = ViaI2CBus2Init(pScrn);
46790b17f1bSmrg    if (pVia->I2CDevices & VIA_I2C_BUS3)
46890b17f1bSmrg        pVia->pI2CBus3 = ViaI2CBus3Init(pScrn);
4697e6fb56fSmrg
4707e6fb56fSmrg#ifdef HAVE_DEBUG
4717e6fb56fSmrg    if (pVia->I2CScan) {
4727e6fb56fSmrg        if (pVia->pI2CBus2)
4737e6fb56fSmrg            ViaI2CScan(pVia->pI2CBus2);
4747e6fb56fSmrg        if (pVia->pI2CBus3)
4757e6fb56fSmrg            ViaI2CScan(pVia->pI2CBus3);
4767e6fb56fSmrg    }
4777e6fb56fSmrg#endif
478963d66acSmrg
479963d66acSmrg    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
480963d66acSmrg                        "Exiting ViaI2CInit.\n"));
4817e6fb56fSmrg}
482983b4bf2Smrg
483983b4bf2Smrg/*
484983b4bf2Smrg * The code originated from Luc Verhaegen's xf86-video-unichrome DDX.
485983b4bf2Smrg *
486983b4bf2Smrg * Sure, it is polluting namespace, but this one is quite useful.
487983b4bf2Smrg */
488983b4bf2SmrgBool
489983b4bf2Smrgxf86I2CMaskByte(I2CDevPtr d, I2CByte subaddr, I2CByte value, I2CByte mask)
490983b4bf2Smrg{
491983b4bf2Smrg    I2CByte tmp;
492983b4bf2Smrg    Bool ret;
493983b4bf2Smrg
494983b4bf2Smrg    ret = xf86I2CReadByte(d, subaddr, &tmp);
495983b4bf2Smrg    if (!ret)
496983b4bf2Smrg        return FALSE;
497983b4bf2Smrg
498983b4bf2Smrg    tmp &= ~mask;
499983b4bf2Smrg    tmp |= (value & mask);
500983b4bf2Smrg
501983b4bf2Smrg    return xf86I2CWriteByte(d, subaddr, tmp);
502983b4bf2Smrg}
503