diff options
Diffstat (limited to 'drivers/video/tdfxfb.c')
-rw-r--r-- | drivers/video/tdfxfb.c | 200 |
1 files changed, 199 insertions, 1 deletions
diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index ee64771..bc6f916 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); |