i830_i2c.c revision ce2d3770
1/************************************************************************** 2 3 Copyright 2006 Dave Airlie <airlied@linux.ie> 4 5All Rights Reserved. 6 7Permission is hereby granted, free of charge, to any person obtaining a 8copy of this software and associated documentation files (the "Software"), 9to deal in the Software without restriction, including without limitation 10on the rights to use, copy, modify, merge, publish, distribute, sub 11license, and/or sell copies of the Software, and to permit persons to whom 12the Software is furnished to do so, subject to the following conditions: 13 14The above copyright notice and this permission notice (including the next 15paragraph) shall be included in all copies or substantial portions of the 16Software. 17 18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 21THE COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 22DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24USE OR OTHER DEALINGS IN THE SOFTWARE. 25 26**************************************************************************/ 27 28#ifdef HAVE_CONFIG_H 29#include "config.h" 30#endif 31 32#include "xf86.h" 33#include "xf86_OSproc.h" 34#include "xf86cmap.h" 35#include "compiler.h" 36#include "vgaHW.h" 37#include "mipointer.h" 38#include "micmap.h" 39#include "shadowfb.h" 40#include <X11/extensions/randr.h> 41#include "fb.h" 42#include "miscstruct.h" 43#include "xf86xv.h" 44#include <X11/extensions/Xv.h> 45#include "shadow.h" 46#include "i830.h" 47 48#define AIRLIED_I2C 0 49 50#if AIRLIED_I2C 51 52#define I2C_TIMEOUT(x) /*(x)*/ /* Report timeouts */ 53#define I2C_TRACE(x) /*(x)*/ /* Report progress */ 54 55static void i830_setscl(I2CBusPtr b, int state) 56{ 57 ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; 58 I830Ptr pI830 = I830PTR(pScrn); 59 uint32_t val; 60 61 OUTREG(b->DriverPrivate.uval, 62 (state ? GPIO_CLOCK_VAL_OUT : 0) | GPIO_CLOCK_DIR_OUT | 63 GPIO_CLOCK_DIR_MASK | GPIO_CLOCK_VAL_MASK); 64 val = INREG(b->DriverPrivate.uval); 65} 66 67static void i830_setsda(I2CBusPtr b, int state) 68{ 69 ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; 70 I830Ptr pI830 = I830PTR(pScrn); 71 uint32_t val; 72 73 OUTREG(b->DriverPrivate.uval, 74 (state ? GPIO_DATA_VAL_OUT : 0) | GPIO_DATA_DIR_OUT | 75 GPIO_DATA_DIR_MASK | GPIO_DATA_VAL_MASK); 76 val = INREG(b->DriverPrivate.uval); 77} 78 79static void i830_getscl(I2CBusPtr b, int *state) 80{ 81 ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; 82 I830Ptr pI830 = I830PTR(pScrn); 83 uint32_t val; 84 85 OUTREG(b->DriverPrivate.uval, GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK); 86 OUTREG(b->DriverPrivate.uval, 0); 87 val = INREG(b->DriverPrivate.uval); 88 *state = ((val & GPIO_CLOCK_VAL_IN) != 0); 89} 90 91static int i830_getsda(I2CBusPtr b) 92 { 93 ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; 94 I830Ptr pI830 = I830PTR(pScrn); 95 uint32_t val; 96 97 OUTREG(b->DriverPrivate.uval, GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK); 98 OUTREG(b->DriverPrivate.uval, 0); 99 val = INREG(b->DriverPrivate.uval); 100 return ((val & GPIO_DATA_VAL_IN) != 0); 101} 102 103static inline void sdalo(I2CBusPtr b) 104{ 105 i830_setsda(b, 0); 106 b->I2CUDelay(b, b->RiseFallTime); 107} 108 109static inline void sdahi(I2CBusPtr b) 110{ 111 i830_setsda(b, 1); 112 b->I2CUDelay(b, b->RiseFallTime); 113} 114 115static inline void scllo(I2CBusPtr b) 116{ 117 i830_setscl(b, 0); 118 b->I2CUDelay(b, b->RiseFallTime); 119} 120 121static inline int sclhi(I2CBusPtr b, int timeout) 122{ 123 int scl = 0; 124 int i; 125 126 i830_setscl(b, 1); 127 b->I2CUDelay(b, b->RiseFallTime); 128 129 for (i = timeout; i > 0; i -= b->RiseFallTime) { 130 i830_getscl(b, &scl); 131 if (scl) break; 132 b->I2CUDelay(b, b->RiseFallTime); 133 } 134 135 if (i <= 0) { 136 I2C_TIMEOUT(ErrorF("[I2CRaiseSCL(<%s>, %d) timeout]", 137 b->BusName, timeout)); 138 return FALSE; 139 } 140 return TRUE; 141} 142 143static Bool 144I830I2CGetByte(I2CDevPtr d, I2CByte *data, Bool last) 145{ 146 I2CBusPtr b = d->pI2CBus; 147 int i, sda; 148 unsigned char indata = 0; 149 150 sdahi(b); 151 152 for (i = 0; i < 8; i++) { 153 if (sclhi(b, d->BitTimeout) == FALSE) { 154 I2C_TRACE(ErrorF("timeout at bit #%d\n", 7-i)); 155 return FALSE; 156 }; 157 indata *= 2; 158 if (i830_getsda(b)) 159 indata |= 0x01; 160 scllo(b); 161 } 162 163 if (last) { 164 sdahi(b); 165 } else { 166 sdalo(b); 167 } 168 169 if (sclhi(b, d->BitTimeout) == FALSE) { 170 sdahi(b); 171 return FALSE; 172 }; 173 174 scllo(b); 175 sdahi(b); 176 177 *data = indata & 0xff; 178 I2C_TRACE(ErrorF("R%02x ", (int) *data)); 179 180 return TRUE; 181} 182 183static Bool 184I830I2CPutByte(I2CDevPtr d, I2CByte c) 185{ 186 Bool r; 187 int i, scl, sda; 188 int sb, ack; 189 I2CBusPtr b = d->pI2CBus; 190 191 for (i = 7; i >= 0; i--) { 192 sb = c & (1 << i); 193 i830_setsda(b, sb); 194 b->I2CUDelay(b, b->RiseFallTime); 195 196 if (sclhi(b, d->ByteTimeout) == FALSE) { 197 sdahi(b); 198 return FALSE; 199 } 200 201 i830_setscl(b, 0); 202 b->I2CUDelay(b, b->RiseFallTime); 203 } 204 sdahi(b); 205 if (sclhi(b, d->ByteTimeout) == FALSE) { 206 I2C_TIMEOUT(ErrorF("[I2CPutByte(<%s>, 0x%02x, %d, %d, %d) timeout]", 207 b->BusName, c, d->BitTimeout, 208 d->ByteTimeout, d->AcknTimeout)); 209 return FALSE; 210 } 211 ack = i830_getsda(b); 212 I2C_TRACE(ErrorF("Put byte 0x%02x , getsda() = %d\n", c & 0xff, ack)); 213 214 scllo(b); 215 return (0 == ack); 216} 217 218static Bool 219I830I2CStart(I2CBusPtr b, int timeout) 220{ 221 if (sclhi(b, timeout) == FALSE) 222 return FALSE; 223 224 sdalo(b); 225 scllo(b); 226 227 return TRUE; 228} 229 230static void 231I830I2CStop(I2CDevPtr d) 232{ 233 I2CBusPtr b = d->pI2CBus; 234 235 sdalo(b); 236 sclhi(b, d->ByteTimeout); 237 sdahi(b); 238} 239 240static Bool 241I830I2CAddress(I2CDevPtr d, I2CSlaveAddr addr) 242{ 243 if (I830I2CStart(d->pI2CBus, d->StartTimeout)) { 244 if (I830I2CPutByte(d, addr & 0xFF)) { 245 if ((addr & 0xF8) != 0xF0 && 246 (addr & 0xFE) != 0x00) 247 return TRUE; 248 249 if (I830I2CPutByte(d, (addr >> 8) & 0xFF)) 250 return TRUE; 251 } 252 253 I830I2CStop(d); 254 } 255 256 return FALSE; 257} 258 259#else 260 261#define I2C_DEBUG 0 262 263#if I2C_DEBUG 264static Bool first = TRUE; 265#endif 266 267static void 268i830I2CGetBits(I2CBusPtr b, int *clock, int *data) 269{ 270 ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; 271 I830Ptr pI830 = I830PTR(pScrn); 272 uint32_t val; 273 274 val = INREG(b->DriverPrivate.uval); 275 276 /* 277 * to read valid data, we must have written a 1 to 278 * the associated bit. Writing a 1 is done by 279 * tri-stating the bus in PutBits, so we needn't make 280 * sure that is true here 281 */ 282 *data = (val & GPIO_DATA_VAL_IN) != 0; 283 *clock = (val & GPIO_CLOCK_VAL_IN) != 0; 284 285#if I2C_DEBUG 286 ErrorF("Getting %s: %c %c\n", b->BusName, 287 *clock ? '^' : 'v', 288 *data ? '^' : 'v'); 289#endif 290} 291 292static void 293i830I2CPutBits(I2CBusPtr b, int clock, int data) 294{ 295 uint32_t reserved = 0; 296 uint32_t data_bits, clock_bits; 297 298#if I2C_DEBUG 299 int cur_clock, cur_data; 300#endif 301 302 ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; 303 I830Ptr pI830 = I830PTR(pScrn); 304 305#if I2C_DEBUG 306 i830I2CGetBits(b, &cur_clock, &cur_data); 307 308 if (first) { 309 ErrorF("%s Debug: C D C D\n", b->BusName); 310 first = FALSE; 311 } 312 313 ErrorF("Setting %s 0x%08x to: %c %c\n", b->BusName, 314 (int)b->DriverPrivate.uval, 315 clock ? '^' : 'v', 316 data ? '^' : 'v'); 317#endif 318 319 if (!IS_I830(pI830) && !IS_845G(pI830)) { 320 /* On most chips, these bits must be preserved in software. */ 321 reserved = INREG(b->DriverPrivate.uval) & 322 (GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE); 323 } 324 325 /* data or clock == 1 means to tristate the bus. otherwise, drive it low */ 326 if (data) 327 data_bits = GPIO_DATA_DIR_IN|GPIO_DATA_DIR_MASK; 328 else 329 data_bits = GPIO_DATA_DIR_OUT|GPIO_DATA_DIR_MASK|GPIO_DATA_VAL_MASK; 330 if (clock) 331 clock_bits = GPIO_CLOCK_DIR_IN|GPIO_CLOCK_DIR_MASK; 332 else 333 clock_bits = GPIO_CLOCK_DIR_OUT|GPIO_CLOCK_DIR_MASK|GPIO_CLOCK_VAL_MASK; 334 335 OUTREG(b->DriverPrivate.uval, reserved | data_bits | clock_bits); 336 POSTING_READ(b->DriverPrivate.uval); 337} 338 339#endif 340 341/* the i830 has a number of I2C Buses */ 342Bool 343I830I2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, int i2c_reg, char *name) 344{ 345 I2CBusPtr pI2CBus; 346 I830Ptr pI830 = I830PTR(pScrn); 347 348 pI2CBus = xf86CreateI2CBusRec(); 349 350 if (!pI2CBus) 351 return FALSE; 352 353 pI2CBus->BusName = name; 354 pI2CBus->scrnIndex = pScrn->scrnIndex; 355#if AIRLIED_I2C 356 pI2CBus->I2CGetByte = I830I2CGetByte; 357 pI2CBus->I2CPutByte = I830I2CPutByte; 358 pI2CBus->I2CStart = I830I2CStart; 359 pI2CBus->I2CStop = I830I2CStop; 360 pI2CBus->I2CAddress = I830I2CAddress; 361#else 362 pI2CBus->I2CGetBits = i830I2CGetBits; 363 pI2CBus->I2CPutBits = i830I2CPutBits; 364#endif 365 pI2CBus->DriverPrivate.uval = i2c_reg; 366 367 /* Assume all busses are used for DDCish stuff */ 368 369 /* 370 * These were set incorrectly in the server pre-1.3, Having 371 * duplicate settings is sub-optimal, but this lets the driver 372 * work with older servers 373 */ 374 pI2CBus->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */ 375 pI2CBus->StartTimeout = 550; 376 pI2CBus->BitTimeout = 40; 377 pI2CBus->AcknTimeout = 40; 378 pI2CBus->RiseFallTime = 20; 379 380 /* Disable the GMBUS, which we won't use. If it is left enabled (for 381 * example, by Mac Mini EFI initialization), GPIO access to the pins it 382 * uses gets disabled. 383 */ 384 OUTREG(GMBUS0, 0); 385 386 if (!xf86I2CBusInit(pI2CBus)) 387 return FALSE; 388 389 *bus_ptr = pI2CBus; 390 return TRUE; 391} 392