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