diff options
author | Eric Anholt <anholt@FreeBSD.org> | 2006-03-23 09:48:18 -0800 |
---|---|---|
committer | Eric Anholt <anholt@leguin.anholt.net> | 2006-04-06 15:58:47 -0700 |
commit | 6414ad89b9d368a032adf2358a65404f5443ef35 (patch) | |
tree | d4911f2481052e27a8798af2c6d2fc0cc5cd6d92 | |
parent | 5215e19af58f5c2746c8d281e33ecee86e55f0a5 (diff) |
Add enough of airlied's DDC/I2C work to pull EDID info from my CRT.
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/common.h | 1 | ||||
-rw-r--r-- | src/i810_driver.c | 6 | ||||
-rw-r--r-- | src/i810_reg.h | 19 | ||||
-rw-r--r-- | src/i830.h | 46 | ||||
-rw-r--r-- | src/i830_driver.c | 112 | ||||
-rw-r--r-- | src/i830_i2c.c | 286 |
7 files changed, 470 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index f0f6691e..37682eb0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,6 +53,7 @@ i810_drv_la_SOURCES = \ i830_display.h \ i830_driver.c \ i830.h \ + i830_i2c.c \ i830_io.c \ i830_memory.c \ i830_modes.c \ diff --git a/src/common.h b/src/common.h index e9debe50..6e68767a 100644 --- a/src/common.h +++ b/src/common.h @@ -85,6 +85,7 @@ extern const char *I810driSymbols[]; extern const char *I810drmSymbols[]; extern const char *I810shadowSymbols[]; #endif +extern const char *I810i2cSymbols[]; extern void I830DPRINTF_stub(const char *filename, int line, const char *function, const char *fmt, ...); diff --git a/src/i810_driver.c b/src/i810_driver.c index 54395a78..f7247f4b 100644 --- a/src/i810_driver.c +++ b/src/i810_driver.c @@ -332,6 +332,12 @@ const char *I810shadowSymbols[] = { NULL }; +const char *I810i2cSymbols[] = { + "xf86CreateI2CBusRec", + "xf86I2CBusInit", + NULL +}; + #ifndef I810_DEBUG int I810_DEBUG = (0 /* | DEBUG_ALWAYS_SYNC */ diff --git a/src/i810_reg.h b/src/i810_reg.h index 3316fed3..6976553f 100644 --- a/src/i810_reg.h +++ b/src/i810_reg.h @@ -258,7 +258,24 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define HSYNC_ON 0x00 #define HSYNC_OFF 0x02 - +#define GPIOA 0x5010 +#define GPIOB 0x5014 +#define GPIOC 0x5018 +#define GPIOD 0x501c +#define GPIOE 0x5020 +#define GPIOF 0x5024 +#define GPIOG 0x5028 +#define GPIOH 0x502c +# define GPIO_CLOCK_DIR_MASK (1 << 0) +# define GPIO_CLOCK_DIR (1 << 1) +# define GPIO_CLOCK_VAL_MASK (1 << 2) +# define GPIO_CLOCK_VAL_OUT (1 << 3) +# define GPIO_CLOCK_VAL_IN (1 << 4) +# define GPIO_DATA_DIR_MASK (1 << 8) +# define GPIO_DATA_DIR (1 << 9) +# define GPIO_DATA_VAL_MASK (1 << 10) +# define GPIO_DATA_VAL_OUT (1 << 11) +# define GPIO_DATA_VAL_IN (1 << 12) /* p317, 319 */ @@ -154,6 +154,44 @@ typedef struct { #endif } I830EntRec, *I830EntPtr; +/* store information about an Ixxx DVO */ +/* The i830->i865 use multiple DVOs with multiple i2cs */ +/* the i915, i945 have a single sDVO i2c bus - which is different */ +#define MAX_DVOS 4 + +#define I830_I2C_BUS_DVO 1 +#define I830_I2C_BUS_SDVO 2 + +#define I830_DVO_CHIP_NONE 0 +#define I830_DVO_CHIP_LVDS 1 +#define I830_DVO_CHIP_TMDS 2 +#define I830_DVO_CHIP_TVOUT 4 + +struct _I830RegI2CDriver { + int type; + char *modulename; + char *fntablename; + int address; + const char **symbols; + void *devpriv; + pointer modhandle; +}; + +struct _I830DVORec { + int bus_type; + int flags; + I2CBusPtr pI2CBus; + I2CBusPtr pDDCBus; + xf86MonPtr MonInfo; + struct _I830RegI2CDriver *i2c_drv; +}; + +typedef struct _I830SDVORec { + int found; + I2CDevRec d; + unsigned char sdvo_regs[20]; +} I830SDVORec, *I830SDVOPtr; + typedef struct _I830Rec { unsigned char *MMIOBase; unsigned char *FbBase; @@ -373,6 +411,12 @@ typedef struct _I830Rec { OsTimerPtr devicesTimer; + int ddc2; + int num_dvos; + + struct _I830DVORec dvos[MAX_DVOS]; + I830SDVOPtr sdvo; + CARD32 saveDSPACNTR; CARD32 saveDSPBCNTR; CARD32 savePIPEACONF; @@ -506,6 +550,8 @@ extern Bool I830RandRSetConfig(ScreenPtr pScreen, Rotation rotation, int rate, RRScreenSizePtr pSize); extern Rotation I830GetRotation(ScreenPtr pScreen); extern Bool I830RandRInit(ScreenPtr pScreen, int rotation); +extern Bool I830I2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, int i2c_reg, + char *name); /* * 12288 is set as the maximum, chosen because it is enough for diff --git a/src/i830_driver.c b/src/i830_driver.c index cdcea29b..f05e273f 100644 --- a/src/i830_driver.c +++ b/src/i830_driver.c @@ -1916,6 +1916,107 @@ I830UseDDC(ScrnInfoPtr pScrn) return mon_range->max_clock; } +void +I830PreInitDDC(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + + if (!xf86LoadSubModule(pScrn, "ddc")) { + pI830->ddc2 = FALSE; + } else { + xf86LoaderReqSymLists(I810ddcSymbols, NULL); + pI830->ddc2 = TRUE; + } + + /* DDC can use I2C bus */ + /* Load I2C if we have the code to use it */ + if (pI830->ddc2) { + if (xf86LoadSubModule(pScrn, "i2c")) { + xf86LoaderReqSymLists(I810i2cSymbols,NULL); + + pI830->num_dvos = 1; + pI830->dvos[0].bus_type = I830_I2C_BUS_DVO; + /* setup the common CRT DVO */ + pI830->ddc2 = I830I2CInit(pScrn, &pI830->dvos[0].pDDCBus, GPIOA, "DDCGPIOA"); + if (pI830->ddc2 == FALSE) + return; + pI830->ddc2 = I830I2CInit(pScrn, &pI830->dvos[0].pI2CBus, GPIOB, "I2CGPIOB"); + if (pI830->ddc2 == FALSE) + return; + + if (!(IS_I9XX(pI830))) { + pI830->dvos[1].bus_type = I830_I2C_BUS_DVO; + pI830->num_dvos = 2; + + pI830->ddc2 = I830I2CInit(pScrn, &pI830->dvos[1].pDDCBus, GPIOD, "DDCGPIOD"); + if (pI830->ddc2 == FALSE) + return; + pI830->ddc2 = I830I2CInit(pScrn, &pI830->dvos[1].pI2CBus, GPIOE, "I2CGPIOE"); + if (pI830->ddc2 == FALSE) + return; + } +#if 0 + else { + pointer ret_p; + pI830->num_dvos = 2; + pI830->dvos[1].bus_type = I830_I2C_BUS_SDVO; + /* i915 has sDVO */ + pI830->ddc2 = I830I2CInit(pScrn, &pI830->dvos[1].pI2CBus, GPIOE, "SDVOCTRL"); + if (pI830->ddc2 = FALSE) + return; + pI830->sdvo=I830SDVOInit(pI830->dvos[1].pI2CBus); + } +#endif + + pI830->ddc2 = TRUE; + } else { + pI830->ddc2 = FALSE; + } + } +} + +void I830DetectMonitors(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + int i; + + if (!pI830->ddc2) + return; + + for (i=0; i<pI830->num_dvos; i++) { + /* we can't do EDID on sDVO yet */ + if (pI830->dvos[i].bus_type == I830_I2C_BUS_DVO) { + pI830->dvos[i].MonInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, + pI830->dvos[i].pDDCBus); + + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "checking DVO %d, %08X\n", i, + pI830->dvos[i].pDDCBus->DriverPrivate.uval); + xf86PrintEDID(pI830->dvos[i].MonInfo); + +#if 0 + /* if we are on an i2C bus > 0 and we see a monitor - try to + * find a controller chip + */ + if (i > 0 && pI830->dvos[i].MonInfo) { + ret = I830I2CDetectControllers(pScrn, pI830->dvos[i].pI2CBus, + &pI830->dvos[i].i2c_drv); + if (ret==TRUE) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Found i2c %s on %08X\n", pI830->dvos[i].i2c_drv->modulename, pI830->dvos[i].pI2CBus->DriverPrivate.uval); + } + } +#endif + } +#if 0 + else { + ret = I830I2CDetectSDVOController(pScrn, pI830->dvos[i].pI2CBus); + if (ret==TRUE) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Found sDVO\n"); + } + } +#endif + } +} + static void PreInitCleanup(ScrnInfoPtr pScrn) { @@ -2397,6 +2498,17 @@ I830BIOSPreInit(ScrnInfoPtr pScrn, int flags) pI830->fixedPipe = 1; } + I830PreInitDDC(pScrn); + + I830DetectMonitors(pScrn); + + for (i=0; i<MAX_DVOS; i++) { + if (pI830->dvos[i].MonInfo) { + pScrn->monitor->DDC = pI830->dvos[i].MonInfo; + break; + } + } + pI830->MonType1 = PIPE_NONE; pI830->MonType2 = PIPE_NONE; pI830->specifiedMonitor = FALSE; diff --git a/src/i830_i2c.c b/src/i830_i2c.c new file mode 100644 index 00000000..8c80a0a1 --- /dev/null +++ b/src/i830_i2c.c @@ -0,0 +1,286 @@ +/************************************************************************** + + Copyright 2006 Dave Airlie <airlied@linux.ie> + +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +on the rights to use, copy, modify, merge, publish, distribute, sub +license, and/or sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL +THE COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. + +**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "xf86.h" +#include "xf86_ansic.h" +#include "xf86_OSproc.h" +#include "xf86Resources.h" +#include "xf86RAC.h" +#include "xf86cmap.h" +#include "compiler.h" +#include "mibstore.h" +#include "vgaHW.h" +#include "mipointer.h" +#include "micmap.h" +#include "shadowfb.h" +#include <X11/extensions/randr.h> +#include "fb.h" +#include "miscstruct.h" +#include "xf86xv.h" +#include <X11/extensions/Xv.h> +#include "shadow.h" +#include "i830.h" + +#define I2C_TIMEOUT(x) /*(x)*/ /* Report timeouts */ +#define I2C_TRACE(x) /*(x)*/ /* Report progress */ + +static void i830_setscl(I2CBusPtr b, int state) +{ + ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; + I830Ptr pI830 = I830PTR(pScrn); + CARD32 val; + + OUTREG(b->DriverPrivate.uval, + (state ? GPIO_CLOCK_VAL_OUT : 0) | + GPIO_CLOCK_DIR | + GPIO_CLOCK_DIR_MASK | + GPIO_CLOCK_VAL_MASK); + val = INREG(b->DriverPrivate.uval); +} + +static void i830_setsda(I2CBusPtr b, int state) +{ + ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; + I830Ptr pI830 = I830PTR(pScrn); + CARD32 val; + + OUTREG(b->DriverPrivate.uval, (state ? GPIO_DATA_VAL_OUT : 0) | + GPIO_DATA_DIR | + GPIO_DATA_DIR_MASK | + GPIO_DATA_VAL_MASK); + val = INREG(b->DriverPrivate.uval); +} + +static void i830_getscl(I2CBusPtr b, int *state) +{ + ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; + I830Ptr pI830 = I830PTR(pScrn); + CARD32 val; + + OUTREG(b->DriverPrivate.uval, GPIO_CLOCK_DIR_MASK); + OUTREG(b->DriverPrivate.uval, 0); + val = INREG(b->DriverPrivate.uval); + *state = ((val & GPIO_CLOCK_VAL_IN) != 0); +} + +static int i830_getsda(I2CBusPtr b) +{ + ScrnInfoPtr pScrn = xf86Screens[b->scrnIndex]; + I830Ptr pI830 = I830PTR(pScrn); + CARD32 val; + + OUTREG(b->DriverPrivate.uval, GPIO_DATA_DIR_MASK); + OUTREG(b->DriverPrivate.uval, 0); + val = INREG(b->DriverPrivate.uval); + return ((val & GPIO_DATA_VAL_IN) != 0); +} + +static inline void sdalo(I2CBusPtr b) +{ + i830_setsda(b, 0); + b->I2CUDelay(b, b->RiseFallTime); +} + +static inline void sdahi(I2CBusPtr b) +{ + i830_setsda(b, 1); + b->I2CUDelay(b, b->RiseFallTime); +} + +static inline void scllo(I2CBusPtr b) +{ + i830_setscl(b, 0); + b->I2CUDelay(b, b->RiseFallTime); +} + +static inline int sclhi(I2CBusPtr b, int timeout) +{ + int scl = 0; + int i; + + i830_setscl(b, 1); + b->I2CUDelay(b, b->RiseFallTime); + + for (i = timeout; i > 0; i -= b->RiseFallTime) { + i830_getscl(b, &scl); + if (scl) break; + b->I2CUDelay(b, b->RiseFallTime); + } + + if (i <= 0) { + I2C_TIMEOUT(ErrorF("[I2CRaiseSCL(<%s>, %d) timeout]", b->BusName, timeout)); + return FALSE; + } + return TRUE; +} + +static Bool +I830I2CGetByte(I2CDevPtr d, I2CByte *data, Bool last) +{ + I2CBusPtr b = d->pI2CBus; + int i; + unsigned char indata = 0; + + sdahi(b); + + for (i = 0; i < 8; i++) { + if (sclhi(b, d->BitTimeout)==FALSE) { + I2C_TRACE(ErrorF("timeout at bit #%d\n", 7-i)); + return FALSE; + } + indata*=2; + if ( i830_getsda (b) ) { + indata |= 0x01; + } + scllo(b); + } + + if (last) + sdahi(b); + else + sdalo(b); + + if (sclhi(b, d->BitTimeout) == FALSE) { + sdahi(b); + return FALSE; + } + + scllo(b); + sdahi(b); + + *data = indata & 0xff; + I2C_TRACE(ErrorF("R%02x ", (int) *data)); + + return TRUE; +} + +static Bool +I830I2CPutByte(I2CDevPtr d, I2CByte c) +{ + int i; + int sb, ack; + I2CBusPtr b = d->pI2CBus; + + for (i = 7; i>=0; i--) { + sb = c & (1 << i); + i830_setsda(b, sb); + b->I2CUDelay(b, b->RiseFallTime); + + if (sclhi(b, d->ByteTimeout) == FALSE) { + sdahi(b); + return FALSE; + } + + i830_setscl(b, 0); + b->I2CUDelay(b, b->RiseFallTime); + } + sdahi(b); + if (sclhi(b, d->ByteTimeout) == FALSE) { + I2C_TIMEOUT(ErrorF("[I2CPutByte(<%s>, 0x%02x, %d, %d, %d) timeout]", + b->BusName, c, d->BitTimeout, + d->ByteTimeout, d->AcknTimeout)); + return FALSE; + } + ack = i830_getsda(b); + I2C_TRACE(ErrorF("Put byte 0x%02x , getsda() = %d\n", c & 0xff, ack)); + + scllo(b); + return 0 == ack; +} + +static Bool +I830I2CStart(I2CBusPtr b, int timeout) +{ + if (sclhi(b, timeout) == FALSE) + return FALSE; + + sdalo(b); + scllo(b); + + return TRUE; +} + +static void +I830I2CStop(I2CDevPtr d) +{ + I2CBusPtr b = d->pI2CBus; + + sdalo(b); + sclhi(b, d->ByteTimeout); + sdahi(b); +} + +static Bool +I830I2CAddress(I2CDevPtr d, I2CSlaveAddr addr) +{ + if (I830I2CStart(d->pI2CBus, d->StartTimeout)) { + if (I830I2CPutByte(d, addr & 0xFF)) { + if ((addr & 0xF8) != 0xF0 && + (addr & 0xFE) != 0x00) + return TRUE; + + if (I830I2CPutByte(d, (addr >> 8) & 0xFF)) + return TRUE; + } + + I830I2CStop(d); + } + + return FALSE; +} + + +/* the i830 has a number of I2C Buses */ +Bool +I830I2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, int i2c_reg, char *name) +{ + I2CBusPtr pI2CBus; + + pI2CBus = xf86CreateI2CBusRec(); + + if (!pI2CBus) + return FALSE; + + pI2CBus->BusName = name; + pI2CBus->scrnIndex = pScrn->scrnIndex; + pI2CBus->I2CGetByte = I830I2CGetByte; + pI2CBus->I2CPutByte = I830I2CPutByte; + pI2CBus->I2CStart = I830I2CStart; + pI2CBus->I2CStop = I830I2CStop; + pI2CBus->I2CAddress = I830I2CAddress; + pI2CBus->DriverPrivate.uval = i2c_reg; + + if (!xf86I2CBusInit(pI2CBus)) + return FALSE; + + *bus_ptr = pI2CBus; + return TRUE; +} |