|
@@ -10,6 +10,12 @@
|
|
* Created : Thu Sep 23 18:17:43 1999, hmallat
|
|
* Created : Thu Sep 23 18:17:43 1999, hmallat
|
|
* Last modified: Tue Nov 2 21:19:47 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
|
|
* Lots of the information here comes from the Daryll Strauss' Banshee
|
|
* patches to the XF86 server, and the rest comes from the 3dfx
|
|
* patches to the XF86 server, and the rest comes from the 3dfx
|
|
* Banshee specification. I'm very much indebted to Daryll for his
|
|
* Banshee specification. I'm very much indebted to Daryll for his
|
|
@@ -1167,6 +1173,190 @@ static struct fb_ops tdfxfb_ops = {
|
|
#endif
|
|
#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
|
|
* tdfxfb_probe - Device Initializiation
|
|
*
|
|
*
|
|
@@ -1284,7 +1474,9 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
|
|
if (hwcursor)
|
|
if (hwcursor)
|
|
info->fix.smem_len = (info->fix.smem_len - 1024) &
|
|
info->fix.smem_len = (info->fix.smem_len - 1024) &
|
|
(PAGE_MASK << 1);
|
|
(PAGE_MASK << 1);
|
|
-
|
|
|
|
|
|
+#ifdef CONFIG_FB_3DFX_I2C
|
|
|
|
+ tdfxfb_create_i2c_busses(info);
|
|
|
|
+#endif
|
|
if (!mode_option)
|
|
if (!mode_option)
|
|
mode_option = "640x480@60";
|
|
mode_option = "640x480@60";
|
|
|
|
|
|
@@ -1315,6 +1507,9 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
out_err_iobase:
|
|
out_err_iobase:
|
|
|
|
+#ifdef CONFIG_FB_3DFX_I2C
|
|
|
|
+ tdfxfb_delete_i2c_busses(default_par);
|
|
|
|
+#endif
|
|
if (default_par->mtrr_handle >= 0)
|
|
if (default_par->mtrr_handle >= 0)
|
|
mtrr_del(default_par->mtrr_handle, info->fix.smem_start,
|
|
mtrr_del(default_par->mtrr_handle, info->fix.smem_start,
|
|
info->fix.smem_len);
|
|
info->fix.smem_len);
|
|
@@ -1379,6 +1574,9 @@ static void __devexit tdfxfb_remove(struct pci_dev *pdev)
|
|
struct tdfx_par *par = info->par;
|
|
struct tdfx_par *par = info->par;
|
|
|
|
|
|
unregister_framebuffer(info);
|
|
unregister_framebuffer(info);
|
|
|
|
+#ifdef CONFIG_FB_3DFX_I2C
|
|
|
|
+ tdfxfb_delete_i2c_busses(par);
|
|
|
|
+#endif
|
|
if (par->mtrr_handle >= 0)
|
|
if (par->mtrr_handle >= 0)
|
|
mtrr_del(par->mtrr_handle, info->fix.smem_start,
|
|
mtrr_del(par->mtrr_handle, info->fix.smem_start,
|
|
info->fix.smem_len);
|
|
info->fix.smem_len);
|