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