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  */
45 static void
ViaI2C1PutBits(I2CBusPtr Bus,int clock,int data)46 ViaI2C1PutBits(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 
60 static void
ViaI2C1GetBits(I2CBusPtr Bus,int * clock,int * data)61 ViaI2C1GetBits(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 
70 static I2CBusPtr
ViaI2CBus1Init(ScrnInfoPtr pScrn)71 ViaI2CBus1Init(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  */
119 static void
ViaI2C2PutBits(I2CBusPtr Bus,int clock,int data)120 ViaI2C2PutBits(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 
134 static void
ViaI2C2GetBits(I2CBusPtr Bus,int * clock,int * data)135 ViaI2C2GetBits(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 
144 static I2CBusPtr
ViaI2CBus2Init(ScrnInfoPtr pScrn)145 ViaI2CBus2Init(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  */
189 static Bool
ViaI2C3Start(I2CBusPtr b,int timeout)190 ViaI2C3Start(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 
205 static Bool
ViaI2C3Address(I2CDevPtr d,I2CSlaveAddr addr)206 ViaI2C3Address(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 
228 static void
229 ViaI2C3Stop(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 
247 static void
248 ViaI2C3PutBit(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 
266 static Bool
267 ViaI2C3PutByte(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 
294 static Bool
295 ViaI2C3GetBit(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 
318 static Bool
319 ViaI2C3GetByte(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 
344 static void
345 ViaI2C3SimplePutBits(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 
359 static void
360 ViaI2C3SimpleGetBits(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 
369 static I2CBusPtr
370 ViaI2CBus3Init(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
434 static void
435 ViaI2CScan(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 
455 void
456 ViaI2CInit(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  */
488 Bool
489 xf86I2CMaskByte(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