1/*
2 * Copyright 2009 Luc Verhaegen.
3 * Copyright 2004 The Unichrome Project  [unichrome.sf.net]
4 * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
5 * Copyright 2001-2003 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 * Implements three I2C buses through registers SR26, SR2C, and SR31.
29 */
30
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
35#include "via_driver.h"
36
37#define SDA_READ  0x04
38#define SCL_READ  0x08
39#define SDA_WRITE 0x10
40#define SCL_WRITE 0x20
41
42/*
43 * First I2C Bus: Typically used for detecting a VGA monitor.
44 */
45static void
46ViaI2C1PutBits(I2CBusPtr Bus, int clock, int data)
47{
48    vgaHWPtr hwp = Bus->DriverPrivate.ptr;
49    CARD8 value = 0x01; /* Enable */
50
51    if (clock)
52        value |= SCL_WRITE;
53
54    if (data)
55        value |= SDA_WRITE;
56
57    ViaSeqMask(hwp, 0x26, value, 0x01 | SCL_WRITE | SDA_WRITE);
58}
59
60static void
61ViaI2C1GetBits(I2CBusPtr Bus, int *clock, int *data)
62{
63    vgaHWPtr hwp = Bus->DriverPrivate.ptr;
64    CARD8 value = hwp->readSeq(hwp, 0x26);
65
66    *clock = (value & SCL_READ) != 0;
67    *data = (value & SDA_READ) != 0;
68}
69
70static I2CBusPtr
71ViaI2CBus1Init(ScrnInfoPtr pScrn)
72{
73    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
74                        "Entered ViaI2CBus1Init.\n"));
75
76    I2CBusPtr pI2CBus = xf86CreateI2CBusRec();
77    vgaHWPtr hwp = VGAHWPTR(pScrn);
78
79    if (!pI2CBus) {
80        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
81                            "xf86CreateI2CBusRec failed.\n"));
82        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
83                            "Initialization of I2C Bus 1 failed.\n");
84        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
85                            "Exiting ViaI2CBus1Init.\n"));
86        return NULL;
87    }
88
89    pI2CBus->BusName = "I2C Bus 1";
90    pI2CBus->scrnIndex = pScrn->scrnIndex;
91    pI2CBus->I2CPutBits = ViaI2C1PutBits;
92    pI2CBus->I2CGetBits = ViaI2C1GetBits;
93    pI2CBus->DriverPrivate.ptr = hwp;
94    pI2CBus->ByteTimeout = 2200;
95    pI2CBus->StartTimeout = 550;
96    pI2CBus->HoldTime = 40;
97    pI2CBus->BitTimeout = 40;
98
99    if (!xf86I2CBusInit(pI2CBus)) {
100        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
101                            "xf86I2CBusInit failed.\n"));
102        xf86DestroyI2CBusRec(pI2CBus, TRUE, FALSE);
103        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
104                    "Initialization of I2C Bus 1 failed.\n");
105        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
106                            "Exiting ViaI2CBus1Init.\n"));
107        return NULL;
108    }
109
110    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
111                        "Exiting ViaI2CBus1Init.\n"));
112    return pI2CBus;
113}
114
115/*
116 * Second I2C Bus: Used to detect a DVI monitor, VGA monitor via
117 *                 a DVI-I connector, or TV encoders.
118 */
119static void
120ViaI2C2PutBits(I2CBusPtr Bus, int clock, int data)
121{
122    vgaHWPtr hwp = Bus->DriverPrivate.ptr;
123    CARD8 value = 0x01; /* Enable */
124
125    if (clock)
126        value |= SCL_WRITE;
127
128    if (data)
129        value |= SDA_WRITE;
130
131    ViaSeqMask(hwp, 0x31, value, 0x01 | SCL_WRITE | SDA_WRITE);
132}
133
134static void
135ViaI2C2GetBits(I2CBusPtr Bus, int *clock, int *data)
136{
137    vgaHWPtr hwp = Bus->DriverPrivate.ptr;
138    CARD8 value = hwp->readSeq(hwp, 0x31);
139
140    *clock = (value & SCL_READ) != 0;
141    *data = (value & SDA_READ) != 0;
142}
143
144static I2CBusPtr
145ViaI2CBus2Init(ScrnInfoPtr pScrn)
146{
147    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
148                        "Entered ViaI2CBus2Init.\n"));
149
150    I2CBusPtr pI2CBus = xf86CreateI2CBusRec();
151    vgaHWPtr hwp = VGAHWPTR(pScrn);
152
153    if (!pI2CBus) {
154        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
155                            "xf86CreateI2CBusRec failed.\n"));
156        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
157                    "Initialization of I2C Bus 2 failed.\n");
158        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
159                            "Exiting ViaI2CBus2Init.\n"));
160        return NULL;
161    }
162
163    pI2CBus->BusName = "I2C Bus 2";
164    pI2CBus->scrnIndex = pScrn->scrnIndex;
165    pI2CBus->I2CPutBits = ViaI2C2PutBits;
166    pI2CBus->I2CGetBits = ViaI2C2GetBits;
167    pI2CBus->DriverPrivate.ptr = hwp;
168
169    if (!xf86I2CBusInit(pI2CBus)) {
170        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
171                            "xf86I2CBusInit failed.\n"));
172        xf86DestroyI2CBusRec(pI2CBus, TRUE, FALSE);
173        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
174                    "Initialization of I2C Bus 2 failed.\n");
175        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
176                            "Exiting ViaI2CBus2Init.\n"));
177        return NULL;
178    }
179
180    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
181                        "Exiting ViaI2CBus2Init.\n"));
182    return pI2CBus;
183}
184
185/*
186 * Third I2C Bus: Implemented via manipulation of GPIO (General
187 *                Purpose I/O) pins.
188 */
189static Bool
190ViaI2C3Start(I2CBusPtr b, int timeout)
191{
192    vgaHWPtr hwp = b->DriverPrivate.ptr;
193
194    ViaSeqMask(hwp, 0x2C, 0xF0, 0xF0);
195    b->I2CUDelay(b, b->RiseFallTime);
196
197    ViaSeqMask(hwp, 0x2C, 0x00, 0x10);
198    b->I2CUDelay(b, b->HoldTime);
199    ViaSeqMask(hwp, 0x2C, 0x00, 0x20);
200    b->I2CUDelay(b, b->HoldTime);
201
202    return TRUE;
203}
204
205static Bool
206ViaI2C3Address(I2CDevPtr d, I2CSlaveAddr addr)
207{
208    I2CBusPtr b = d->pI2CBus;
209
210#ifdef X_NEED_I2CSTART
211    if (b->I2CStart(d->pI2CBus, d->StartTimeout)) {
212#else
213    if (ViaI2C3Start(d->pI2CBus, d->StartTimeout)) {
214#endif
215        if (b->I2CPutByte(d, addr & 0xFF)) {
216            if ((addr & 0xF8) != 0xF0 && (addr & 0xFE) != 0x00)
217                return TRUE;
218
219            if (b->I2CPutByte(d, (addr >> 8) & 0xFF))
220                return TRUE;
221        }
222
223        b->I2CStop(d);
224    }
225    return FALSE;
226}
227
228static void
229ViaI2C3Stop(I2CDevPtr d)
230{
231    I2CBusPtr b = d->pI2CBus;
232    vgaHWPtr hwp = b->DriverPrivate.ptr;
233
234    ViaSeqMask(hwp, 0x2C, 0xC0, 0xF0);
235    b->I2CUDelay(b, b->RiseFallTime);
236
237    ViaSeqMask(hwp, 0x2C, 0x20, 0x20);
238    b->I2CUDelay(b, b->HoldTime);
239
240    ViaSeqMask(hwp, 0x2C, 0x10, 0x10);
241    b->I2CUDelay(b, b->HoldTime);
242
243    ViaSeqMask(hwp, 0x2C, 0x00, 0x20);
244    b->I2CUDelay(b, b->HoldTime);
245}
246
247static void
248ViaI2C3PutBit(I2CBusPtr b, Bool sda, int timeout)
249{
250    vgaHWPtr hwp = b->DriverPrivate.ptr;
251
252    if (sda)
253        ViaSeqMask(hwp, 0x2C, 0x50, 0x50);
254    else
255        ViaSeqMask(hwp, 0x2C, 0x40, 0x50);
256    b->I2CUDelay(b, b->RiseFallTime / 5);
257
258    ViaSeqMask(hwp, 0x2C, 0xA0, 0xA0);
259    b->I2CUDelay(b, b->HoldTime);
260    b->I2CUDelay(b, timeout);
261
262    ViaSeqMask(hwp, 0x2C, 0x80, 0xA0);
263    b->I2CUDelay(b, b->RiseFallTime / 5);
264}
265
266static Bool
267ViaI2C3PutByte(I2CDevPtr d, I2CByte data)
268{
269    I2CBusPtr b = d->pI2CBus;
270    vgaHWPtr hwp = b->DriverPrivate.ptr;
271    Bool ret;
272    int i;
273
274    for (i = 7; i >= 0; i--)
275        ViaI2C3PutBit(b, (data >> i) & 0x01, b->BitTimeout);
276
277    /* Raise first to avoid false positives. */
278    ViaSeqMask(hwp, 0x2C, 0x50, 0x50);
279    ViaSeqMask(hwp, 0x2C, 0x00, 0x40);
280    b->I2CUDelay(b, b->RiseFallTime);
281    ViaSeqMask(hwp, 0x2C, 0xA0, 0xA0);
282
283    if (hwp->readSeq(hwp, 0x2C) & 0x04)
284        ret = FALSE;
285    else
286        ret = TRUE;
287
288    ViaSeqMask(hwp, 0x2C, 0x80, 0xA0);
289    b->I2CUDelay(b, b->RiseFallTime);
290
291    return ret;
292}
293
294static Bool
295ViaI2C3GetBit(I2CBusPtr b, int timeout)
296{
297    vgaHWPtr hwp = b->DriverPrivate.ptr;
298    Bool ret;
299
300    ViaSeqMask(hwp, 0x2c, 0x80, 0xC0);
301    b->I2CUDelay(b, b->RiseFallTime / 5);
302    ViaSeqMask(hwp, 0x2c, 0xA0, 0xA0);
303    b->I2CUDelay(b, 3 * b->HoldTime);
304    b->I2CUDelay(b, timeout);
305
306    if (hwp->readSeq(hwp, 0x2C) & 0x04)
307        ret = TRUE;
308    else
309        ret = FALSE;
310
311    ViaSeqMask(hwp, 0x2C, 0x80, 0xA0);
312    b->I2CUDelay(b, b->HoldTime);
313    b->I2CUDelay(b, b->RiseFallTime / 5);
314
315    return ret;
316}
317
318static Bool
319ViaI2C3GetByte(I2CDevPtr d, I2CByte * data, Bool last)
320{
321    I2CBusPtr b = d->pI2CBus;
322    vgaHWPtr hwp = b->DriverPrivate.ptr;
323    int i;
324
325    *data = 0x00;
326
327    for (i = 7; i >= 0; i--)
328        if (ViaI2C3GetBit(b, b->BitTimeout))
329            *data |= 0x01 << i;
330
331    if (last)   /* send NACK */
332        ViaSeqMask(hwp, 0x2C, 0x50, 0x50);
333    else        /* send ACK */
334        ViaSeqMask(hwp, 0x2C, 0x40, 0x50);
335
336    ViaSeqMask(hwp, 0x2C, 0xA0, 0xA0);
337    b->I2CUDelay(b, b->HoldTime);
338
339    ViaSeqMask(hwp, 0x2C, 0x80, 0xA0);
340
341    return TRUE;
342}
343
344static void
345ViaI2C3SimplePutBits(I2CBusPtr Bus, int clock, int data)
346{
347    vgaHWPtr hwp = Bus->DriverPrivate.ptr;
348    CARD8 value = 0xC0;
349
350    if (clock)
351        value |= SCL_WRITE;
352
353    if (data)
354        value |= SDA_WRITE;
355
356    ViaSeqMask(hwp, 0x2C, value, 0xC0 | SCL_WRITE | SDA_WRITE);
357}
358
359static void
360ViaI2C3SimpleGetBits(I2CBusPtr Bus, int *clock, int *data)
361{
362    vgaHWPtr hwp = Bus->DriverPrivate.ptr;
363    CARD8 value = hwp->readSeq(hwp, 0x2C);
364
365    *clock = (value & SCL_READ) != 0;
366    *data = (value & SDA_READ) != 0;
367}
368
369static I2CBusPtr
370ViaI2CBus3Init(ScrnInfoPtr pScrn)
371{
372    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
373                        "Entered ViaI2CBus3Init.\n"));
374
375    I2CBusPtr pI2CBus = xf86CreateI2CBusRec();
376    vgaHWPtr hwp = VGAHWPTR(pScrn);
377    VIAPtr pVia = VIAPTR(pScrn);
378
379    if (!pI2CBus) {
380        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
381                            "xf86CreateI2CBusRec failed.\n"));
382        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
383                    "Initialization of I2C Bus 3 failed.\n");
384        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
385                            "Exiting ViaI2CBus3Init.\n"));
386        return NULL;
387    }
388
389    pI2CBus->BusName = "I2C Bus 3";
390    pI2CBus->scrnIndex = pScrn->scrnIndex;
391    pI2CBus->DriverPrivate.ptr = hwp;
392
393    switch (pVia->Chipset) {
394        case VIA_P4M800PRO:
395            DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
396                                "using alternative PutBits/GetBits functions for I2C Bus 3\n"));
397            pI2CBus->I2CPutBits = ViaI2C3SimplePutBits;
398            pI2CBus->I2CGetBits = ViaI2C3SimpleGetBits;
399            break;
400        default:
401            pI2CBus->I2CAddress = ViaI2C3Address;
402#ifdef X_NEED_I2CSTART
403            pI2CBus->I2CStart = ViaI2C3Start;
404#endif
405            pI2CBus->I2CStop = ViaI2C3Stop;
406            pI2CBus->I2CPutByte = ViaI2C3PutByte;
407            pI2CBus->I2CGetByte = ViaI2C3GetByte;
408            pI2CBus->DriverPrivate.ptr = hwp;
409
410            pI2CBus->BitTimeout = 10;
411            pI2CBus->ByteTimeout = 10;
412            pI2CBus->HoldTime = 10;
413            pI2CBus->StartTimeout = 10;
414            break;
415    }
416
417    if (!xf86I2CBusInit(pI2CBus)) {
418        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
419                            "xf86I2CBusInit failed.\n"));
420        xf86DestroyI2CBusRec(pI2CBus, TRUE, FALSE);
421        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
422                    "Initialization of I2C Bus 3 failed.\n");
423        DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
424                            "Exiting ViaI2CBus3Init.\n"));
425        return NULL;
426    }
427
428    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
429                        "Exiting ViaI2CBus3Init.\n"));
430    return pI2CBus;
431}
432
433#ifdef HAVE_DEBUG
434static void
435ViaI2CScan(I2CBusPtr Bus)
436{
437    CARD8 i;
438
439    DEBUG(xf86DrvMsg(Bus->scrnIndex, X_INFO,
440                        "Entered ViaI2CScan.\n"));
441
442    xf86DrvMsg(Bus->scrnIndex, X_INFO, "Scanning %s.\n",
443               Bus->BusName);
444
445    for (i = 0x10; i < 0xF0; i += 2)
446        if (xf86I2CProbeAddress(Bus, i))
447            xf86DrvMsg(Bus->scrnIndex, X_PROBED, "Found slave on %s "
448                       "- 0x%02X.\n", Bus->BusName, i);
449
450    DEBUG(xf86DrvMsg(Bus->scrnIndex, X_INFO,
451                        "Exiting ViaI2CScan.\n"));
452}
453#endif /* HAVE_DEBUG */
454
455void
456ViaI2CInit(ScrnInfoPtr pScrn)
457{
458    VIAPtr pVia = VIAPTR(pScrn);
459
460    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
461                        "Entered ViaI2CInit.\n"));
462
463    if (pVia->I2CDevices & VIA_I2C_BUS1)
464        pVia->pI2CBus1 = ViaI2CBus1Init(pScrn);
465    if (pVia->I2CDevices & VIA_I2C_BUS2)
466        pVia->pI2CBus2 = ViaI2CBus2Init(pScrn);
467    if (pVia->I2CDevices & VIA_I2C_BUS3)
468        pVia->pI2CBus3 = ViaI2CBus3Init(pScrn);
469
470#ifdef HAVE_DEBUG
471    if (pVia->I2CScan) {
472        if (pVia->pI2CBus2)
473            ViaI2CScan(pVia->pI2CBus2);
474        if (pVia->pI2CBus3)
475            ViaI2CScan(pVia->pI2CBus3);
476    }
477#endif
478
479    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
480                        "Exiting ViaI2CInit.\n"));
481}
482
483/*
484 * The code originated from Luc Verhaegen's xf86-video-unichrome DDX.
485 *
486 * Sure, it is polluting namespace, but this one is quite useful.
487 */
488Bool
489xf86I2CMaskByte(I2CDevPtr d, I2CByte subaddr, I2CByte value, I2CByte mask)
490{
491    I2CByte tmp;
492    Bool ret;
493
494    ret = xf86I2CReadByte(d, subaddr, &tmp);
495    if (!ret)
496        return FALSE;
497
498    tmp &= ~mask;
499    tmp |= (value & mask);
500
501    return xf86I2CWriteByte(d, subaddr, tmp);
502}
503