summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/video/Kconfig9
-rw-r--r--drivers/video/tdfxfb.c200
-rw-r--r--include/video/tdfx.h26
3 files changed, 234 insertions, 1 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index ffe2f2796e29..7826bdce4bbe 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1550,6 +1550,7 @@ config FB_3DFX
select FB_CFB_IMAGEBLIT
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
+ select FB_MODE_HELPERS
help
This driver supports graphics boards with the 3Dfx Banshee,
Voodoo3 or VSA-100 (aka Voodoo4/5) chips. Say Y if you have
@@ -1565,6 +1566,14 @@ config FB_3DFX_ACCEL
This will compile the 3Dfx Banshee/Voodoo3/VSA-100 frame buffer
device driver with acceleration functions.
+config FB_3DFX_I2C
+ bool "Enable DDC/I2C support"
+ depends on FB_3DFX && EXPERIMENTAL
+ select FB_DDC
+ default y
+ help
+ Say Y here if you want DDC/I2C support for your 3dfx Voodoo3.
+
config FB_VOODOO1
tristate "3Dfx Voodoo Graphics (sst1) support"
depends on FB && PCI
diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c
index ee64771fbe3d..bc6f916c53be 100644
--- a/drivers/video/tdfxfb.c
+++ b/drivers/video/tdfxfb.c
@@ -10,6 +10,12 @@
* Created : Thu Sep 23 18:17:43 1999, hmallat
* Last modified: Tue Nov 2 21:19:47 1999, hmallat
*
+ * I2C part copied from the i2c-voodoo3.c driver by:
+ * Frodo Looijaard <frodol@dds.nl>,
+ * Philip Edelbrock <phil@netroedge.com>,
+ * Ralph Metzler <rjkm@thp.uni-koeln.de>, and
+ * Mark D. Studebaker <mdsxyz123@yahoo.com>
+ *
* Lots of the information here comes from the Daryll Strauss' Banshee
* patches to the XF86 server, and the rest comes from the 3dfx
* Banshee specification. I'm very much indebted to Daryll for his
@@ -1167,6 +1173,190 @@ static struct fb_ops tdfxfb_ops = {
#endif
};
+#ifdef CONFIG_FB_3DFX_I2C
+/* The voo GPIO registers don't have individual masks for each bit
+ so we always have to read before writing. */
+
+static void tdfxfb_i2c_setscl(void *data, int val)
+{
+ struct tdfxfb_i2c_chan *chan = data;
+ struct tdfx_par *par = chan->par;
+ unsigned int r;
+
+ r = tdfx_inl(par, VIDSERPARPORT);
+ if (val)
+ r |= I2C_SCL_OUT;
+ else
+ r &= ~I2C_SCL_OUT;
+ tdfx_outl(par, VIDSERPARPORT, r);
+ tdfx_inl(par, VIDSERPARPORT); /* flush posted write */
+}
+
+static void tdfxfb_i2c_setsda(void *data, int val)
+{
+ struct tdfxfb_i2c_chan *chan = data;
+ struct tdfx_par *par = chan->par;
+ unsigned int r;
+
+ r = tdfx_inl(par, VIDSERPARPORT);
+ if (val)
+ r |= I2C_SDA_OUT;
+ else
+ r &= ~I2C_SDA_OUT;
+ tdfx_outl(par, VIDSERPARPORT, r);
+ tdfx_inl(par, VIDSERPARPORT); /* flush posted write */
+}
+
+/* The GPIO pins are open drain, so the pins always remain outputs.
+ We rely on the i2c-algo-bit routines to set the pins high before
+ reading the input from other chips. */
+
+static int tdfxfb_i2c_getscl(void *data)
+{
+ struct tdfxfb_i2c_chan *chan = data;
+ struct tdfx_par *par = chan->par;
+
+ return (0 != (tdfx_inl(par, VIDSERPARPORT) & I2C_SCL_IN));
+}
+
+static int tdfxfb_i2c_getsda(void *data)
+{
+ struct tdfxfb_i2c_chan *chan = data;
+ struct tdfx_par *par = chan->par;
+
+ return (0 != (tdfx_inl(par, VIDSERPARPORT) & I2C_SDA_IN));
+}
+
+static void tdfxfb_ddc_setscl(void *data, int val)
+{
+ struct tdfxfb_i2c_chan *chan = data;
+ struct tdfx_par *par = chan->par;
+ unsigned int r;
+
+ r = tdfx_inl(par, VIDSERPARPORT);
+ if (val)
+ r |= DDC_SCL_OUT;
+ else
+ r &= ~DDC_SCL_OUT;
+ tdfx_outl(par, VIDSERPARPORT, r);
+ tdfx_inl(par, VIDSERPARPORT); /* flush posted write */
+}
+
+static void tdfxfb_ddc_setsda(void *data, int val)
+{
+ struct tdfxfb_i2c_chan *chan = data;
+ struct tdfx_par *par = chan->par;
+ unsigned int r;
+
+ r = tdfx_inl(par, VIDSERPARPORT);
+ if (val)
+ r |= DDC_SDA_OUT;
+ else
+ r &= ~DDC_SDA_OUT;
+ tdfx_outl(par, VIDSERPARPORT, r);
+ tdfx_inl(par, VIDSERPARPORT); /* flush posted write */
+}
+
+static int tdfxfb_ddc_getscl(void *data)
+{
+ struct tdfxfb_i2c_chan *chan = data;
+ struct tdfx_par *par = chan->par;
+
+ return (0 != (tdfx_inl(par, VIDSERPARPORT) & DDC_SCL_IN));
+}
+
+static int tdfxfb_ddc_getsda(void *data)
+{
+ struct tdfxfb_i2c_chan *chan = data;
+ struct tdfx_par *par = chan->par;
+
+ return (0 != (tdfx_inl(par, VIDSERPARPORT) & DDC_SDA_IN));
+}
+
+static int __devinit tdfxfb_setup_ddc_bus(struct tdfxfb_i2c_chan *chan,
+ const char *name, struct device *dev)
+{
+ int rc;
+
+ strlcpy(chan->adapter.name, name, sizeof(chan->adapter.name));
+ chan->adapter.owner = THIS_MODULE;
+ chan->adapter.class = I2C_CLASS_DDC;
+ chan->adapter.algo_data = &chan->algo;
+ chan->adapter.dev.parent = dev;
+ chan->algo.setsda = tdfxfb_ddc_setsda;
+ chan->algo.setscl = tdfxfb_ddc_setscl;
+ chan->algo.getsda = tdfxfb_ddc_getsda;
+ chan->algo.getscl = tdfxfb_ddc_getscl;
+ chan->algo.udelay = 10;
+ chan->algo.timeout = msecs_to_jiffies(500);
+ chan->algo.data = chan;
+
+ i2c_set_adapdata(&chan->adapter, chan);
+
+ rc = i2c_bit_add_bus(&chan->adapter);
+ if (rc == 0)
+ DPRINTK("I2C bus %s registered.\n", name);
+ else
+ chan->par = NULL;
+
+ return rc;
+}
+
+static int __devinit tdfxfb_setup_i2c_bus(struct tdfxfb_i2c_chan *chan,
+ const char *name, struct device *dev)
+{
+ int rc;
+
+ strlcpy(chan->adapter.name, name, sizeof(chan->adapter.name));
+ chan->adapter.owner = THIS_MODULE;
+ chan->adapter.class = I2C_CLASS_TV_ANALOG;
+ chan->adapter.algo_data = &chan->algo;
+ chan->adapter.dev.parent = dev;
+ chan->algo.setsda = tdfxfb_i2c_setsda;
+ chan->algo.setscl = tdfxfb_i2c_setscl;
+ chan->algo.getsda = tdfxfb_i2c_getsda;
+ chan->algo.getscl = tdfxfb_i2c_getscl;
+ chan->algo.udelay = 10;
+ chan->algo.timeout = msecs_to_jiffies(500);
+ chan->algo.data = chan;
+
+ i2c_set_adapdata(&chan->adapter, chan);
+
+ rc = i2c_bit_add_bus(&chan->adapter);
+ if (rc == 0)
+ DPRINTK("I2C bus %s registered.\n", name);
+ else
+ chan->par = NULL;
+
+ return rc;
+}
+
+static void __devinit tdfxfb_create_i2c_busses(struct fb_info *info)
+{
+ struct tdfx_par *par = info->par;
+
+ tdfx_outl(par, VIDINFORMAT, 0x8160);
+ tdfx_outl(par, VIDSERPARPORT, 0xcffc0020);
+
+ par->chan[0].par = par;
+ par->chan[1].par = par;
+
+ tdfxfb_setup_ddc_bus(&par->chan[0], "Voodoo3-DDC", info->dev);
+ tdfxfb_setup_i2c_bus(&par->chan[1], "Voodoo3-I2C", info->dev);
+}
+
+static void tdfxfb_delete_i2c_busses(struct tdfx_par *par)
+{
+ if (par->chan[0].par)
+ i2c_del_adapter(&par->chan[0].adapter);
+ par->chan[0].par = NULL;
+
+ if (par->chan[1].par)
+ i2c_del_adapter(&par->chan[1].adapter);
+ par->chan[1].par = NULL;
+}
+#endif /* CONFIG_FB_3DFX_I2C */
+
/**
* tdfxfb_probe - Device Initializiation
*
@@ -1284,7 +1474,9 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
if (hwcursor)
info->fix.smem_len = (info->fix.smem_len - 1024) &
(PAGE_MASK << 1);
-
+#ifdef CONFIG_FB_3DFX_I2C
+ tdfxfb_create_i2c_busses(info);
+#endif
if (!mode_option)
mode_option = "640x480@60";
@@ -1315,6 +1507,9 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
return 0;
out_err_iobase:
+#ifdef CONFIG_FB_3DFX_I2C
+ tdfxfb_delete_i2c_busses(default_par);
+#endif
if (default_par->mtrr_handle >= 0)
mtrr_del(default_par->mtrr_handle, info->fix.smem_start,
info->fix.smem_len);
@@ -1379,6 +1574,9 @@ static void __devexit tdfxfb_remove(struct pci_dev *pdev)
struct tdfx_par *par = info->par;
unregister_framebuffer(info);
+#ifdef CONFIG_FB_3DFX_I2C
+ tdfxfb_delete_i2c_busses(par);
+#endif
if (par->mtrr_handle >= 0)
mtrr_del(par->mtrr_handle, info->fix.smem_start,
info->fix.smem_len);
diff --git a/include/video/tdfx.h b/include/video/tdfx.h
index 7431d9681e57..befbaf0a92d8 100644
--- a/include/video/tdfx.h
+++ b/include/video/tdfx.h
@@ -1,6 +1,9 @@
#ifndef _TDFX_H
#define _TDFX_H
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
/* membase0 register offsets */
#define STATUS 0x00
#define PCIINIT0 0x04
@@ -123,6 +126,18 @@
#define VIDCFG_PIXFMT_SHIFT 18
#define DACMODE_2X BIT(0)
+/* I2C bit locations in the VIDSERPARPORT register */
+#define DDC_ENAB 0x00040000
+#define DDC_SCL_OUT 0x00080000
+#define DDC_SDA_OUT 0x00100000
+#define DDC_SCL_IN 0x00200000
+#define DDC_SDA_IN 0x00400000
+#define I2C_ENAB 0x00800000
+#define I2C_SCL_OUT 0x01000000
+#define I2C_SDA_OUT 0x02000000
+#define I2C_SCL_IN 0x04000000
+#define I2C_SDA_IN 0x08000000
+
/* VGA rubbish, need to change this for multihead support */
#define MISC_W 0x3c2
#define MISC_R 0x3cc
@@ -168,12 +183,23 @@ struct banshee_reg {
unsigned long miscinit0;
};
+struct tdfx_par;
+
+struct tdfxfb_i2c_chan {
+ struct tdfx_par *par;
+ struct i2c_adapter adapter;
+ struct i2c_algo_bit_data algo;
+};
+
struct tdfx_par {
u32 max_pixclock;
u32 palette[16];
void __iomem *regbase_virt;
unsigned long iobase;
int mtrr_handle;
+#ifdef CONFIG_FB_3DFX_I2C
+ struct tdfxfb_i2c_chan chan[2];
+#endif
};
#endif /* __KERNEL__ */