diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/video/savage/savagefb-i2c.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/video/savage/savagefb-i2c.c')
-rw-r--r-- | drivers/video/savage/savagefb-i2c.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/drivers/video/savage/savagefb-i2c.c b/drivers/video/savage/savagefb-i2c.c new file mode 100644 index 00000000000..024a0cecff1 --- /dev/null +++ b/drivers/video/savage/savagefb-i2c.c @@ -0,0 +1,282 @@ +/* + * linux/drivers/video/savage/savagefb-i2c.c - S3 Savage DDC2 + * + * Copyright 2004 Antonino A. Daplas <adaplas @pol.net> + * + * Based partly on rivafb-i2c.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/fb.h> + +#include <asm/io.h> +#include "savagefb.h" + +#define SAVAGE_DDC 0x50 + +#define VGA_CR_IX 0x3d4 +#define VGA_CR_DATA 0x3d5 + +#define CR_SERIAL1 0xa0 /* I2C serial communications interface */ +#define MM_SERIAL1 0xff20 +#define CR_SERIAL2 0xb1 /* DDC2 monitor communications interface */ + +/* based on vt8365 documentation */ +#define PROSAVAGE_I2C_ENAB 0x10 +#define PROSAVAGE_I2C_SCL_OUT 0x01 +#define PROSAVAGE_I2C_SDA_OUT 0x02 +#define PROSAVAGE_I2C_SCL_IN 0x04 +#define PROSAVAGE_I2C_SDA_IN 0x08 + +#define SAVAGE4_I2C_ENAB 0x00000020 +#define SAVAGE4_I2C_SCL_OUT 0x00000001 +#define SAVAGE4_I2C_SDA_OUT 0x00000002 +#define SAVAGE4_I2C_SCL_IN 0x00000008 +#define SAVAGE4_I2C_SDA_IN 0x00000010 + +#define SET_CR_IX(base, val) writeb((val), base + 0x8000 + VGA_CR_IX) +#define SET_CR_DATA(base, val) writeb((val), base + 0x8000 + VGA_CR_DATA) +#define GET_CR_DATA(base) readb(base + 0x8000 + VGA_CR_DATA) + +static void savage4_gpio_setscl(void *data, int val) +{ + struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data; + unsigned int r; + + r = readl(chan->ioaddr + chan->reg); + if(val) + r |= SAVAGE4_I2C_SCL_OUT; + else + r &= ~SAVAGE4_I2C_SCL_OUT; + writel(r, chan->ioaddr + chan->reg); + readl(chan->ioaddr + chan->reg); /* flush posted write */ +} + +static void savage4_gpio_setsda(void *data, int val) +{ + struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data; + + unsigned int r; + r = readl(chan->ioaddr + chan->reg); + if(val) + r |= SAVAGE4_I2C_SDA_OUT; + else + r &= ~SAVAGE4_I2C_SDA_OUT; + writel(r, chan->ioaddr + chan->reg); + readl(chan->ioaddr + chan->reg); /* flush posted write */ +} + +static int savage4_gpio_getscl(void *data) +{ + struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data; + + return (0 != (readl(chan->ioaddr + chan->reg) & SAVAGE4_I2C_SCL_IN)); +} + +static int savage4_gpio_getsda(void *data) +{ + struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data; + + return (0 != (readl(chan->ioaddr + chan->reg) & SAVAGE4_I2C_SDA_IN)); +} + +static void prosavage_gpio_setscl(void* data, int val) +{ + struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data; + u32 r; + + SET_CR_IX(chan->ioaddr, chan->reg); + r = GET_CR_DATA(chan->ioaddr); + r |= PROSAVAGE_I2C_ENAB; + if (val) { + r |= PROSAVAGE_I2C_SCL_OUT; + } else { + r &= ~PROSAVAGE_I2C_SCL_OUT; + } + SET_CR_DATA(chan->ioaddr, r); +} + +static void prosavage_gpio_setsda(void* data, int val) +{ + struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data; + unsigned int r; + + SET_CR_IX(chan->ioaddr, chan->reg); + r = GET_CR_DATA(chan->ioaddr); + r |= PROSAVAGE_I2C_ENAB; + if (val) { + r |= PROSAVAGE_I2C_SDA_OUT; + } else { + r &= ~PROSAVAGE_I2C_SDA_OUT; + } + SET_CR_DATA(chan->ioaddr, r); +} + +static int prosavage_gpio_getscl(void* data) +{ + struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data; + + SET_CR_IX(chan->ioaddr, chan->reg); + return (0 != (GET_CR_DATA(chan->ioaddr) & PROSAVAGE_I2C_SCL_IN)); +} + +static int prosavage_gpio_getsda(void* data) +{ + struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data; + + SET_CR_IX(chan->ioaddr, chan->reg); + return (0 != (GET_CR_DATA(chan->ioaddr) & PROSAVAGE_I2C_SDA_IN)); +} + +#define I2C_ALGO_SAVAGE 0x0f0000 +static int savage_setup_i2c_bus(struct savagefb_i2c_chan *chan, + const char *name) +{ + int (*add_bus)(struct i2c_adapter *) = symbol_get(i2c_bit_add_bus); + int rc = 0; + + if (add_bus && chan->par) { + strcpy(chan->adapter.name, name); + chan->adapter.owner = THIS_MODULE; + chan->adapter.id = I2C_ALGO_SAVAGE; + chan->adapter.algo_data = &chan->algo; + chan->adapter.dev.parent = &chan->par->pcidev->dev; + chan->algo.udelay = 40; + chan->algo.mdelay = 5; + chan->algo.timeout = 20; + chan->algo.data = chan; + + i2c_set_adapdata(&chan->adapter, chan); + + /* Raise SCL and SDA */ + chan->algo.setsda(chan, 1); + chan->algo.setscl(chan, 1); + udelay(20); + + rc = add_bus(&chan->adapter); + + if (rc == 0) + dev_dbg(&chan->par->pcidev->dev, + "I2C bus %s registered.\n", name); + else + dev_warn(&chan->par->pcidev->dev, + "Failed to register I2C bus %s.\n", name); + + symbol_put(i2c_bit_add_bus); + } else + chan->par = NULL; + + return rc; +} + +void savagefb_create_i2c_busses(struct fb_info *info) +{ + struct savagefb_par *par = (struct savagefb_par *)info->par; + par->chan.par = par; + + switch(info->fix.accel) { + case FB_ACCEL_PROSAVAGE_DDRK: + case FB_ACCEL_PROSAVAGE_PM: + par->chan.reg = CR_SERIAL2; + par->chan.ioaddr = par->mmio.vbase; + par->chan.algo.setsda = prosavage_gpio_setsda; + par->chan.algo.setscl = prosavage_gpio_setscl; + par->chan.algo.getsda = prosavage_gpio_getsda; + par->chan.algo.getscl = prosavage_gpio_getscl; + break; + case FB_ACCEL_SAVAGE4: + par->chan.reg = 0xff20; + par->chan.ioaddr = par->mmio.vbase; + par->chan.algo.setsda = savage4_gpio_setsda; + par->chan.algo.setscl = savage4_gpio_setscl; + par->chan.algo.getsda = savage4_gpio_getsda; + par->chan.algo.getscl = savage4_gpio_getscl; + break; + default: + par->chan.par = NULL; + } + + savage_setup_i2c_bus(&par->chan, "SAVAGE DDC2"); +} + +void savagefb_delete_i2c_busses(struct fb_info *info) +{ + struct savagefb_par *par = (struct savagefb_par *)info->par; + int (*del_bus)(struct i2c_adapter *) = + symbol_get(i2c_bit_del_bus); + + if (del_bus && par->chan.par) { + del_bus(&par->chan.adapter); + symbol_put(i2c_bit_del_bus); + } + + par->chan.par = NULL; +} + +static u8 *savage_do_probe_i2c_edid(struct savagefb_i2c_chan *chan) +{ + u8 start = 0x0; + int (*transfer)(struct i2c_adapter *, struct i2c_msg *, int) = + symbol_get(i2c_transfer); + struct i2c_msg msgs[] = { + { + .addr = SAVAGE_DDC, + .len = 1, + .buf = &start, + }, { + .addr = SAVAGE_DDC, + .flags = I2C_M_RD, + .len = EDID_LENGTH, + }, + }; + u8 *buf = NULL; + + if (transfer && chan->par) { + buf = kmalloc(EDID_LENGTH, GFP_KERNEL); + + if (buf) { + msgs[1].buf = buf; + + if (transfer(&chan->adapter, msgs, 2) != 2) { + dev_dbg(&chan->par->pcidev->dev, + "Unable to read EDID block.\n"); + kfree(buf); + buf = NULL; + } + } + + symbol_put(i2c_transfer); + } + + return buf; +} + +int savagefb_probe_i2c_connector(struct savagefb_par *par, u8 **out_edid) +{ + u8 *edid = NULL; + int i; + + for (i = 0; i < 3; i++) { + /* Do the real work */ + edid = savage_do_probe_i2c_edid(&par->chan); + if (edid) + break; + } + if (out_edid) + *out_edid = edid; + if (!edid) + return 1; + + return 0; +} + +MODULE_LICENSE("GPL"); |