summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Anholt <anholt@FreeBSD.org>2006-03-23 09:48:18 -0800
committerEric Anholt <anholt@leguin.anholt.net>2006-04-06 15:58:47 -0700
commit6414ad89b9d368a032adf2358a65404f5443ef35 (patch)
treed4911f2481052e27a8798af2c6d2fc0cc5cd6d92
parent5215e19af58f5c2746c8d281e33ecee86e55f0a5 (diff)
Add enough of airlied's DDC/I2C work to pull EDID info from my CRT.
-rw-r--r--src/Makefile.am1
-rw-r--r--src/common.h1
-rw-r--r--src/i810_driver.c6
-rw-r--r--src/i810_reg.h19
-rw-r--r--src/i830.h46
-rw-r--r--src/i830_driver.c112
-rw-r--r--src/i830_i2c.c286
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
*/
diff --git a/src/i830.h b/src/i830.h
index 4effcd5c..d3d1f5a3 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -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;
+}