linux-fbdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 9/10] i810fb: Add i2c/DDC support
@ 2005-09-01 21:44 Antonino A. Daplas
  2005-09-04 13:51 ` Nicolas Boichat
  0 siblings, 1 reply; 3+ messages in thread
From: Antonino A. Daplas @ 2005-09-01 21:44 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Linux Fbdev development list

    Add ddc/i2c support for i810fb.  This will allow the driver to
    get display information, especially for monitors with fickle timings.
    The i2c support depends on CONFIG_FB_I810_GTF.
    
    Changed __init* to __devinit*
    
    Signed-off-by: Antonino Daplas <adaplas@pol.net>

---
 Kconfig          |    5 +
 i810/Makefile    |    2 
 i810/i810-i2c.c  |  257 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 i810/i810.h      |   13 ++
 i810/i810_main.c |  175 +++++++++++++++++++++++++++----------
 i810/i810_main.h |    7 +
 6 files changed, 413 insertions(+), 46 deletions(-)


diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -751,6 +751,11 @@ config FB_I810_GTF
   
           If unsure, say N.
 
+config FB_I810_I2C
+	bool "Enable DDC Support"
+	depends on FB_I810 && I2C && FB_I810_GTF
+	help
+
 config FB_INTEL
 	tristate "Intel 830M/845G/852GM/855GM/865G support (EXPERIMENTAL)"
 	depends on FB && EXPERIMENTAL && PCI && X86 && !X86_64
diff --git a/drivers/video/i810/Makefile b/drivers/video/i810/Makefile
--- a/drivers/video/i810/Makefile
+++ b/drivers/video/i810/Makefile
@@ -3,7 +3,7 @@
 #
 
 obj-$(CONFIG_FB_I810)		+= i810fb.o
-
+obj-$(CONFIG_FB_I810_I2C)       += i810-i2c.o
 
 i810fb-objs                     := i810_main.o i810_accel.o
 
diff --git a/drivers/video/i810/i810-i2c.c b/drivers/video/i810/i810-i2c.c
new file mode 100644
--- /dev/null
+++ b/drivers/video/i810/i810-i2c.c
@@ -0,0 +1,257 @@
+ /*-*- linux-c -*-
+ *  linux/drivers/video/i810-i2c.c -- Intel 810/815 I2C support 
+ *
+ *      Copyright (C) 2004 Antonino Daplas<adaplas@pol.net>
+ *      All Rights Reserved      
+ *
+ *  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 "i810.h"
+#include "i810_regs.h"
+#include "../edid.h"
+
+#define I810_DDC 0x50
+/* bit locations in the registers */
+#define SCL_DIR_MASK		0x0001
+#define SCL_DIR			0x0002
+#define SCL_VAL_MASK		0x0004
+#define SCL_VAL_OUT		0x0008
+#define SCL_VAL_IN		0x0010
+#define SDA_DIR_MASK		0x0100
+#define SDA_DIR			0x0200
+#define SDA_VAL_MASK		0x0400
+#define SDA_VAL_OUT		0x0800
+#define SDA_VAL_IN		0x1000
+
+#define DEBUG  /* define this for verbose EDID parsing output */
+
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk(fmt,## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+static void i810i2c_setscl(void *data, int state)
+{
+        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
+        struct i810fb_par         *par = chan->par;
+	u8                        *mmio = par->mmio_start_virtual;
+	
+	i810_writel(mmio, GPIOB, (state ? SCL_VAL_OUT : 0) | SCL_DIR |
+		    SCL_DIR_MASK | SCL_VAL_MASK);
+	i810_readl(mmio, GPIOB);	/* flush posted write */
+}
+
+static void i810i2c_setsda(void *data, int state)
+{
+        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
+        struct i810fb_par         *par = chan->par;
+	u8                        *mmio = par->mmio_start_virtual;
+	
+ 	i810_writel(mmio, GPIOB, (state ? SDA_VAL_OUT : 0) | SDA_DIR |
+		    SDA_DIR_MASK | SDA_VAL_MASK);
+	i810_readl(mmio, GPIOB);	/* flush posted write */
+}
+
+static int i810i2c_getscl(void *data)
+{
+        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
+        struct i810fb_par         *par = chan->par;
+	u8                        *mmio = par->mmio_start_virtual;
+	
+	i810_writel(mmio, GPIOB, SCL_DIR_MASK);
+	i810_writel(mmio, GPIOB, 0);
+	return (0 != (i810_readl(mmio, GPIOB) & SCL_VAL_IN));
+}
+
+static int i810i2c_getsda(void *data)
+{
+        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
+        struct i810fb_par         *par = chan->par;
+	u8                        *mmio = par->mmio_start_virtual;
+	
+	i810_writel(mmio, GPIOB, SDA_DIR_MASK);
+	i810_writel(mmio, GPIOB, 0);
+	return (0 != (i810_readl(mmio, GPIOB) & SDA_VAL_IN));
+}
+
+static void i810ddc_setscl(void *data, int state)
+{
+        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
+        struct i810fb_par       *par = chan->par;
+	u8                      *mmio = par->mmio_start_virtual;
+	
+	i810_writel(mmio, GPIOA, (state ? SCL_VAL_OUT : 0) | SCL_DIR |
+		    SCL_DIR_MASK | SCL_VAL_MASK);
+	i810_readl(mmio, GPIOA);	/* flush posted write */
+}
+
+static void i810ddc_setsda(void *data, int state)
+{
+        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
+        struct i810fb_par         *par = chan->par;
+	u8                      *mmio = par->mmio_start_virtual;
+	
+ 	i810_writel(mmio, GPIOA, (state ? SDA_VAL_OUT : 0) | SDA_DIR |
+		    SDA_DIR_MASK | SDA_VAL_MASK);
+	i810_readl(mmio, GPIOA);	/* flush posted write */
+}
+
+static int i810ddc_getscl(void *data)
+{
+        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
+        struct i810fb_par         *par = chan->par;
+	u8                      *mmio = par->mmio_start_virtual;
+	
+	i810_writel(mmio, GPIOA, SCL_DIR_MASK);
+	i810_writel(mmio, GPIOA, 0);
+	return (0 != (i810_readl(mmio, GPIOA) & SCL_VAL_IN));
+}
+
+static int i810ddc_getsda(void *data)
+{
+        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
+        struct i810fb_par         *par = chan->par;
+	u8                      *mmio = par->mmio_start_virtual;
+	
+	i810_writel(mmio, GPIOA, SDA_DIR_MASK);
+	i810_writel(mmio, GPIOA, 0);
+	return (0 != (i810_readl(mmio, GPIOA) & SDA_VAL_IN));
+}
+
+#define I2C_ALGO_DDC_I810   0x0e0000
+#define I2C_ALGO_I2C_I810   0x0f0000
+static int i810_setup_i2c_bus(struct i810fb_i2c_chan *chan, const char *name,
+			      int conn)
+{
+        int rc;
+
+        strcpy(chan->adapter.name, name);
+        chan->adapter.owner             = THIS_MODULE;
+        chan->adapter.algo_data         = &chan->algo;
+        chan->adapter.dev.parent        = &chan->par->dev->dev;
+	switch (conn) {
+	case 1:
+		chan->adapter.id                = I2C_ALGO_DDC_I810;
+		chan->algo.setsda               = i810ddc_setsda;
+		chan->algo.setscl               = i810ddc_setscl;
+		chan->algo.getsda               = i810ddc_getsda;
+		chan->algo.getscl               = i810ddc_getscl;
+		break;
+	case 2:
+		chan->adapter.id                = I2C_ALGO_I2C_I810;
+		chan->algo.setsda               = i810i2c_setsda;
+		chan->algo.setscl               = i810i2c_setscl;
+		chan->algo.getsda               = i810i2c_getsda;
+		chan->algo.getscl               = i810i2c_getscl;
+		break;
+	}
+	chan->algo.udelay               = 10;
+	chan->algo.mdelay               = 10;
+        chan->algo.timeout              = (HZ/2);
+        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 = i2c_bit_add_bus(&chan->adapter);
+        if (rc == 0) 
+                dev_dbg(&chan->par->dev->dev, "I2C bus %s registered.\n",name);
+        else
+                dev_warn(&chan->par->dev->dev, "Failed to register I2C bus "
+			 "%s.\n", name);
+        return rc;
+}
+
+void i810_create_i2c_busses(struct i810fb_par *par)
+{
+        par->chan[0].par        = par;
+	par->chan[1].par        = par;
+	i810_setup_i2c_bus(&par->chan[0], "I810-DDC", 1);
+	i810_setup_i2c_bus(&par->chan[1], "I810-I2C", 2);
+}
+
+void i810_delete_i2c_busses(struct i810fb_par *par)
+{
+        if (par->chan[0].par)
+                i2c_bit_del_bus(&par->chan[0].adapter);
+        par->chan[0].par = NULL;
+	if (par->chan[1].par)
+		i2c_bit_del_bus(&par->chan[1].adapter);
+	par->chan[1].par = NULL;
+}
+
+static u8 *i810_do_probe_i2c_edid(struct i810fb_i2c_chan *chan)
+{
+        u8 start = 0x0;
+        struct i2c_msg msgs[] = {
+                {
+                        .addr   = I810_DDC,
+                        .len    = 1,
+                        .buf    = &start,
+                }, {
+                        .addr   = I810_DDC,
+                        .flags  = I2C_M_RD,
+                        .len    = EDID_LENGTH,
+                },
+        };
+        u8 *buf;
+
+        buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
+        if (!buf) {
+		DPRINTK("i810-i2c: Failed to allocate memory\n");
+                return NULL;
+        }
+        msgs[1].buf = buf;
+
+        if (i2c_transfer(&chan->adapter, msgs, 2) == 2) {
+		DPRINTK("i810-i2c: I2C Transfer successful\n");
+                return buf;
+	}
+        DPRINTK("i810-i2c: Unable to read EDID block.\n");
+        kfree(buf);
+        return NULL;
+}
+
+int i810_probe_i2c_connector(struct fb_info *info, u8 **out_edid, int conn)
+{
+	struct i810fb_par *par = info->par;
+        u8 *edid = NULL;
+        int i;
+
+	DPRINTK("i810-i2c: Probe DDC%i Bus\n", conn);
+	if (conn < 3) {
+		for (i = 0; i < 3; i++) {
+			/* Do the real work */
+			edid = i810_do_probe_i2c_edid(&par->chan[conn-1]);
+			if (edid) 
+				break;
+		}
+	} else {
+		DPRINTK("i810-i2c: Getting EDID from BIOS\n");
+		edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
+		if (edid)
+			memcpy(edid, fb_firmware_edid(info->device),
+			       EDID_LENGTH);
+	}
+
+        if (out_edid)
+                *out_edid = edid;
+
+        return (edid) ? 0 : 1;
+}
+
+
diff --git a/drivers/video/i810/i810.h b/drivers/video/i810/i810.h
--- a/drivers/video/i810/i810.h
+++ b/drivers/video/i810/i810.h
@@ -16,6 +16,9 @@
 #include <linux/list.h>
 #include <linux/agp_backend.h>
 #include <linux/fb.h>
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
 #include <video/vga.h>
 
 /* Fence */
@@ -240,6 +243,14 @@ struct state_registers {
 	u8 cr39, cr41, cr70, sr01, msr;
 };
 
+struct i810fb_par;
+
+struct i810fb_i2c_chan {
+	struct i810fb_par *par;
+	struct i2c_adapter adapter;
+	struct i2c_algo_bit_data algo;
+};
+
 struct i810fb_par {
 	struct mode_registers    regs;
 	struct state_registers   hw_state;
@@ -251,10 +262,12 @@ struct i810fb_par {
 	struct heap_data         iring;
 	struct heap_data         cursor_heap;
 	struct vgastate          state;
+	struct i810fb_i2c_chan   chan[2];
 	atomic_t                 use_count;
 	u32 pseudo_palette[17];
 	unsigned long mmio_start_phys;
 	u8 __iomem *mmio_start_virtual;
+	u8 *edid;
 	u32 pitch;
 	u32 pixconf;
 	u32 watermark;
diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c
--- a/drivers/video/i810/i810_main.c
+++ b/drivers/video/i810/i810_main.c
@@ -92,20 +92,21 @@ static struct pci_driver i810fb_driver =
 	.resume   =     i810fb_resume,
 };
 
-static int vram       __initdata = 4;
-static int bpp        __initdata = 8;
-static int mtrr       __initdata = 0;
-static int accel      __initdata = 0;
-static int hsync1     __initdata = 0;
-static int hsync2     __initdata = 0;
-static int vsync1     __initdata = 0;
-static int vsync2     __initdata = 0;
-static int xres       __initdata = 640;
-static int yres       __initdata = 480;
-static int vyres      __initdata = 0;
-static int sync       __initdata = 0;
-static int ext_vga    __initdata = 0;
-static int dcolor     __initdata = 0;
+static char *mode_option __devinitdata = NULL;
+static int vram       __devinitdata = 4;
+static int bpp        __devinitdata = 8;
+static int mtrr       __devinitdata = 0;
+static int accel      __devinitdata = 0;
+static int hsync1     __devinitdata = 0;
+static int hsync2     __devinitdata = 0;
+static int vsync1     __devinitdata = 0;
+static int vsync2     __devinitdata = 0;
+static int xres       __devinitdata = 640;
+static int yres       __devinitdata = 480;
+static int vyres      __devinitdata = 0;
+static int sync       __devinitdata = 0;
+static int ext_vga    __devinitdata = 0;
+static int dcolor     __devinitdata = 0;
 
 /*------------------------------------------------------------*/
 
@@ -947,31 +948,24 @@ static int i810_check_params(struct fb_v
 			     struct fb_info *info)
 {
 	struct i810fb_par *par = (struct i810fb_par *) info->par;
-	int line_length, vidmem;
-	u32 xres, yres, vxres, vyres;
-
-	xres = var->xres;
-	yres = var->yres;
-	vxres = var->xres_virtual;
-	vyres = var->yres_virtual;
-
+	int line_length, vidmem, mode_valid = 0;
+	u32 vyres = var->yres_virtual, vxres = var->xres_virtual;
 	/*
 	 *  Memory limit
 	 */
-	line_length = get_line_length(par, vxres, 
-				      var->bits_per_pixel);
-
+	line_length = get_line_length(par, vxres, var->bits_per_pixel);
 	vidmem = line_length*vyres;
+
 	if (vidmem > par->fb.size) {
 		vyres = par->fb.size/line_length;
-		if (vyres < yres) {
+		if (vyres < var->yres) {
 			vyres = yres;
 			vxres = par->fb.size/vyres;
 			vxres /= var->bits_per_pixel >> 3;
 			line_length = get_line_length(par, vxres, 
 						      var->bits_per_pixel);
 			vidmem = line_length * yres;
-			if (vxres < xres) {
+			if (vxres < var->xres) {
 				printk("i810fb: required video memory, "
 				       "%d bytes, for %dx%d-%d (virtual) "
 				       "is out of range\n", 
@@ -981,6 +975,10 @@ static int i810_check_params(struct fb_v
 			}
 		}
 	}
+
+	var->xres_virtual = vxres;
+	var->yres_virtual = vyres;
+
 	/*
 	 * Monitor limit
 	 */
@@ -996,25 +994,39 @@ static int i810_check_params(struct fb_v
 		info->monspecs.dclkmax = 204000000;
 		break;
 	}
+
 	info->monspecs.dclkmin = 15000000;
 
-	if (fb_validate_mode(var, info)) {
+	if (!fb_validate_mode(var, info))
+		mode_valid = 1;
+
+#ifdef CONFIG_FB_I810_I2C
+	if (!mode_valid && info->monspecs.gtf &&
+	    !fb_get_mode(FB_MAXTIMINGS, 0, var, info))
+		mode_valid = 1;
+	
+	if (!mode_valid && info->monspecs.modedb_len) {
+		struct fb_videomode *mode;
+
+		mode = fb_find_best_mode(var, &info->modelist);
+		if (mode) {
+			fb_videomode_to_var(var, mode);
+			mode_valid = 1;
+		}
+	}
+#endif	
+	if (!mode_valid && info->monspecs.modedb_len == 0) {
 		if (fb_get_mode(FB_MAXTIMINGS, 0, var, info)) {
 			int default_sync = (info->monspecs.hfmin-HFMIN)
-						|(info->monspecs.hfmax-HFMAX)
-						|(info->monspecs.vfmin-VFMIN)
-						|(info->monspecs.vfmax-VFMAX);
+				|(info->monspecs.hfmax-HFMAX)
+				|(info->monspecs.vfmin-VFMIN)
+				|(info->monspecs.vfmax-VFMAX);
 			printk("i810fb: invalid video mode%s\n",
-			    default_sync ? "" :
-			    ". Specifying vsyncN/hsyncN parameters may help");
-			return -EINVAL;
+			       default_sync ? "" : ". Specifying "
+			       "vsyncN/hsyncN parameters may help");
 		}
-	}
-	
-	var->xres = xres;
-	var->yres = yres;
-	var->xres_virtual = vxres;
-	var->yres_virtual = vyres;
+	}		
+
 	return 0;
 }	
 
@@ -1812,8 +1824,68 @@ i810_allocate_pci_resource(struct i810fb
 	return 0;
 }
 
+static void __devinit i810fb_find_init_mode(struct fb_info *info)
+{
+	struct fb_videomode mode;
+	struct fb_var_screeninfo var;
+	struct i810fb_par *par = info->par;
+	struct fb_monspecs *specs = NULL;
+	int i, err, found = 0;
+
+	INIT_LIST_HEAD(&info->modelist);
+	memset(&mode, 0, sizeof(struct fb_videomode));
+	var = info->var;
+#ifdef CONFIG_FB_I810_I2C
+	i810_create_i2c_busses(par);
+
+	for (i = 0; i < 3; i++) {
+		err = i810_probe_i2c_connector(info, &par->edid, i+1);
+		if (!err)
+			break;
+	}
+
+	if (!err)
+		printk("i810fb_init_pci: DDC probe successful\n");
+
+	fb_edid_to_monspecs(par->edid, &info->monspecs);
+
+	if (info->monspecs.modedb == NULL)
+		printk("i810fb_init_pci: Unable to get Mode Database\n");
+	
+	specs = &info->monspecs;
+	fb_videomode_to_modelist(specs->modedb, specs->modedb_len,
+				 &info->modelist);
+	if (specs->modedb != NULL) {
+		if (specs->misc & FB_MISC_1ST_DETAIL) {
+			for (i = 0; i < specs->modedb_len; i++) {
+				if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {
+					mode = specs->modedb[i];
+					found = 1;
+					break;
+				}
+			}
+		}
+		
+		if (!found) { 
+			mode = specs->modedb[0];
+			found = 1;
+		}		
+
+		fb_videomode_to_var(&var, &mode);
+	}
+#endif
+	if (mode_option)
+		fb_find_mode(&var, info, mode_option, specs->modedb,
+			     specs->modedb_len, (found) ? &mode : NULL, 
+			     info->var.bits_per_pixel);
+
+	info->var = var;
+	fb_destroy_modedb(specs->modedb);
+	specs->modedb = NULL;
+}
+
 #ifndef MODULE
-static int __init i810fb_setup(char *options)
+static int __devinit i810fb_setup(char *options)
 {
 	char *this_opt, *suffix = NULL;
 
@@ -1855,6 +1927,8 @@ static int __init i810fb_setup(char *opt
 			vsync2 = simple_strtoul(this_opt+7, NULL, 0);
 		else if (!strncmp(this_opt, "dcolor", 6))
 			dcolor = 1;
+		else 
+			mode_option = this_opt;
 	}
 	return 0;
 }
@@ -1865,6 +1939,7 @@ static int __devinit i810fb_init_pci (st
 {
 	struct fb_info    *info;
 	struct i810fb_par *par = NULL;
+	struct fb_videomode mode;
 	int i, err = -1, vfreq, hfreq, pixclock;
 
 	i = 0;
@@ -1873,7 +1948,7 @@ static int __devinit i810fb_init_pci (st
 	if (!info)
 		return -ENOMEM;
 
-	par = (struct i810fb_par *) info->par;
+	par = info->par;
 	par->dev = dev;
 
 	if (!(info->pixmap.addr = kmalloc(8*1024, GFP_KERNEL))) {
@@ -1904,15 +1979,20 @@ static int __devinit i810fb_init_pci (st
 	info->fbops = &par->i810fb_ops;
 	info->pseudo_palette = par->pseudo_palette;
 	fb_alloc_cmap(&info->cmap, 256, 0);
+	i810fb_find_init_mode(info);
 
 	if ((err = info->fbops->fb_check_var(&info->var, info))) {
 		i810fb_release_resource(info, par);
 		return err;
 	}
+	
+	fb_var_to_videomode(&mode, &info->var);
+	fb_add_videomode(&mode, &info->modelist);
 	encode_fix(&info->fix, info); 
 	 	    
 	i810fb_init_ringbuffer(info);
 	err = register_framebuffer(info);
+
 	if (err < 0) {
     		i810fb_release_resource(info, par); 
 		printk("i810fb_init: cannot register framebuffer device\n");
@@ -1951,6 +2031,8 @@ static void i810fb_release_resource(stru
 	struct gtt_data *gtt = &par->i810_gtt;
 	unset_mtrr(par);
 
+	i810_delete_i2c_busses(par);
+
 	if (par->i810_gtt.i810_cursor_memory)
 		agp_free_memory(gtt->i810_cursor_memory);
 	if (par->i810_gtt.i810_fb_memory)
@@ -1960,7 +2042,8 @@ static void i810fb_release_resource(stru
 		iounmap(par->mmio_start_virtual);
 	if (par->aperture.virtual)
 		iounmap(par->aperture.virtual);
-
+	if (par->edid)
+		kfree(par->edid);
 	if (par->res_flags & FRAMEBUFFER_REQ)
 		release_mem_region(par->aperture.physical,
 				   par->aperture.size);
@@ -1986,7 +2069,7 @@ static void __exit i810fb_remove_pci(str
 }                                                	
 
 #ifndef MODULE
-static int __init i810fb_init(void)
+static int __devinit i810fb_init(void)
 {
 	char *option = NULL;
 
@@ -2004,7 +2087,7 @@ static int __init i810fb_init(void)
 
 #ifdef MODULE
 
-static int __init i810fb_init(void)
+static int __devinit i810fb_init(void)
 {
 	hsync1 *= 1000;
 	hsync2 *= 1000;
@@ -2052,6 +2135,8 @@ MODULE_PARM_DESC(sync, "wait for accel e
 module_param(dcolor, bool, 0);
 MODULE_PARM_DESC(dcolor, "use DirectColor visuals"
 		 " (default = 0 = TrueColor)");
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option, "Specify initial video mode");
 
 MODULE_AUTHOR("Tony A. Daplas");
 MODULE_DESCRIPTION("Framebuffer device for the Intel 810/815 and"
diff --git a/drivers/video/i810/i810_main.h b/drivers/video/i810/i810_main.h
--- a/drivers/video/i810/i810_main.h
+++ b/drivers/video/i810/i810_main.h
@@ -83,6 +83,13 @@ extern int  i810fb_sync     (struct fb_i
 extern void i810fb_init_ringbuffer(struct fb_info *info);
 extern void i810fb_load_front     (u32 offset, struct fb_info *info);
 
+/* I2C */
+extern int i810_probe_i2c_connector(struct fb_info *info, u8 **out_edid,
+				    int conn);
+extern void i810_create_i2c_busses(struct i810fb_par *par);
+extern void i810_delete_i2c_busses(struct i810fb_par *par);
+
+
 /* Conditionals */
 #ifdef CONFIG_X86
 inline void flush_cache(void)



-------------------------------------------------------
SF.Net email is Sponsored by the Better Software Conference & EXPO
September 19-22, 2005 * San Francisco, CA * Development Lifecycle Practices
Agile & Plan-Driven Development * Managing Projects & Teams * Testing & QA
Security * Process Improvement & Measurement * http://www.sqe.com/bsce5sf

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH 9/10] i810fb: Add i2c/DDC support
  2005-09-01 21:44 [PATCH 9/10] i810fb: Add i2c/DDC support Antonino A. Daplas
@ 2005-09-04 13:51 ` Nicolas Boichat
  2005-09-04 14:46   ` Antonino A. Daplas
  0 siblings, 1 reply; 3+ messages in thread
From: Nicolas Boichat @ 2005-09-04 13:51 UTC (permalink / raw)
  To: Antonino A. Daplas; +Cc: linux-fbdev-devel

Hello Antonino,

I have an information that may interest you: On i865G, you can connect
an AGP daughter card to add a DVI connector. The I2C bus of this
connector is accessed using the register that I called GPIO"C", and its
offset is 0x0501C (it is not documented in Intel datasheets). It works
exactly like the busses on GPIOA (0x05010) and GPIOB (0x05014), i.e. you
can reuse the same functions.

BTW, you could merge i810i2c_* and i810ddc_* functions, as they are the
same excepting the register address (you did this in rivafb).

Also note that your code to read/write to the I2C busses has been
reported to work on i915G (I don't know about the real framebuffer
support).

Best regards,

Nicolas

On Fri, 2005-09-02 at 05:44 +0800, Antonino A. Daplas wrote:
>     Add ddc/i2c support for i810fb.  This will allow the driver to
>     get display information, especially for monitors with fickle timings.
>     The i2c support depends on CONFIG_FB_I810_GTF.
>     
>     Changed __init* to __devinit*
>     
>     Signed-off-by: Antonino Daplas <adaplas@pol.net>
> 
[snip]




-------------------------------------------------------
SF.Net email is Sponsored by the Better Software Conference & EXPO
September 19-22, 2005 * San Francisco, CA * Development Lifecycle Practices
Agile & Plan-Driven Development * Managing Projects & Teams * Testing & QA
Security * Process Improvement & Measurement * http://www.sqe.com/bsce5sf

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH 9/10] i810fb: Add i2c/DDC support
  2005-09-04 13:51 ` Nicolas Boichat
@ 2005-09-04 14:46   ` Antonino A. Daplas
  0 siblings, 0 replies; 3+ messages in thread
From: Antonino A. Daplas @ 2005-09-04 14:46 UTC (permalink / raw)
  To: Nicolas Boichat; +Cc: linux-fbdev-devel

Nicolas Boichat wrote:
> Hello Antonino,
> 
> I have an information that may interest you: On i865G, you can connect
> an AGP daughter card to add a DVI connector. The I2C bus of this
> connector is accessed using the register that I called GPIO"C", and its
> offset is 0x0501C (it is not documented in Intel datasheets). It works
> exactly like the busses on GPIOA (0x05010) and GPIOB (0x05014), i.e. you
> can reuse the same functions.

Okay.

> 
> BTW, you could merge i810i2c_* and i810ddc_* functions, as they are the
> same excepting the register address (you did this in rivafb).
> 

Good idea. Can anyone test the patch? I don't have the hardware anymore

Tony

diff --git a/drivers/video/i810/i810-i2c.c b/drivers/video/i810/i810-i2c.c
--- a/drivers/video/i810/i810-i2c.c
+++ b/drivers/video/i810/i810-i2c.c
@@ -42,96 +42,51 @@
 
 static void i810i2c_setscl(void *data, int state)
 {
-        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
+        struct i810fb_i2c_chan    *chan = data;
         struct i810fb_par         *par = chan->par;
 	u8                        *mmio = par->mmio_start_virtual;
 	
-	i810_writel(mmio, GPIOB, (state ? SCL_VAL_OUT : 0) | SCL_DIR |
+	i810_writel(mmio, chan->ddc_base, (state ? SCL_VAL_OUT : 0) | SCL_DIR |
 		    SCL_DIR_MASK | SCL_VAL_MASK);
-	i810_readl(mmio, GPIOB);	/* flush posted write */
+	i810_readl(mmio, chan->ddc_base);	/* flush posted write */
 }
 
 static void i810i2c_setsda(void *data, int state)
 {
-        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
+        struct i810fb_i2c_chan    *chan = data;
         struct i810fb_par         *par = chan->par;
 	u8                        *mmio = par->mmio_start_virtual;
 	
- 	i810_writel(mmio, GPIOB, (state ? SDA_VAL_OUT : 0) | SDA_DIR |
+ 	i810_writel(mmio, chan->ddc_base, (state ? SDA_VAL_OUT : 0) | SDA_DIR |
 		    SDA_DIR_MASK | SDA_VAL_MASK);
-	i810_readl(mmio, GPIOB);	/* flush posted write */
+	i810_readl(mmio, chan->ddc_base);	/* flush posted write */
 }
 
 static int i810i2c_getscl(void *data)
 {
-        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
+        struct i810fb_i2c_chan    *chan = data;
         struct i810fb_par         *par = chan->par;
 	u8                        *mmio = par->mmio_start_virtual;
 	
-	i810_writel(mmio, GPIOB, SCL_DIR_MASK);
-	i810_writel(mmio, GPIOB, 0);
-	return (0 != (i810_readl(mmio, GPIOB) & SCL_VAL_IN));
+	i810_writel(mmio, chan->ddc_base, SCL_DIR_MASK);
+	i810_writel(mmio, chan->ddc_base, 0);
+	return (0 != (i810_readl(mmio, chan->ddc_base) & SCL_VAL_IN));
 }
 
 static int i810i2c_getsda(void *data)
 {
-        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
+        struct i810fb_i2c_chan    *chan = data;
         struct i810fb_par         *par = chan->par;
 	u8                        *mmio = par->mmio_start_virtual;
 	
-	i810_writel(mmio, GPIOB, SDA_DIR_MASK);
-	i810_writel(mmio, GPIOB, 0);
-	return (0 != (i810_readl(mmio, GPIOB) & SDA_VAL_IN));
+	i810_writel(mmio, chan->ddc_base, SDA_DIR_MASK);
+	i810_writel(mmio, chan->ddc_base, 0);
+	return (0 != (i810_readl(mmio, chan->ddc_base) & SDA_VAL_IN));
 }
 
-static void i810ddc_setscl(void *data, int state)
-{
-        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
-        struct i810fb_par       *par = chan->par;
-	u8                      *mmio = par->mmio_start_virtual;
-	
-	i810_writel(mmio, GPIOA, (state ? SCL_VAL_OUT : 0) | SCL_DIR |
-		    SCL_DIR_MASK | SCL_VAL_MASK);
-	i810_readl(mmio, GPIOA);	/* flush posted write */
-}
-
-static void i810ddc_setsda(void *data, int state)
-{
-        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
-        struct i810fb_par         *par = chan->par;
-	u8                      *mmio = par->mmio_start_virtual;
-	
- 	i810_writel(mmio, GPIOA, (state ? SDA_VAL_OUT : 0) | SDA_DIR |
-		    SDA_DIR_MASK | SDA_VAL_MASK);
-	i810_readl(mmio, GPIOA);	/* flush posted write */
-}
-
-static int i810ddc_getscl(void *data)
-{
-        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
-        struct i810fb_par         *par = chan->par;
-	u8                      *mmio = par->mmio_start_virtual;
-	
-	i810_writel(mmio, GPIOA, SCL_DIR_MASK);
-	i810_writel(mmio, GPIOA, 0);
-	return (0 != (i810_readl(mmio, GPIOA) & SCL_VAL_IN));
-}
 
-static int i810ddc_getsda(void *data)
-{
-        struct i810fb_i2c_chan    *chan = (struct i810fb_i2c_chan *)data;
-        struct i810fb_par         *par = chan->par;
-	u8                      *mmio = par->mmio_start_virtual;
-	
-	i810_writel(mmio, GPIOA, SDA_DIR_MASK);
-	i810_writel(mmio, GPIOA, 0);
-	return (0 != (i810_readl(mmio, GPIOA) & SDA_VAL_IN));
-}
-
-#define I2C_ALGO_DDC_I810   0x0e0000
-#define I2C_ALGO_I2C_I810   0x0f0000
-static int i810_setup_i2c_bus(struct i810fb_i2c_chan *chan, const char *name,
-			      int conn)
+#define I2C_ALGO_I810   0x0f0000
+static int i810_setup_i2c_bus(struct i810fb_i2c_chan *chan, const char *name)
 {
         int rc;
 
@@ -139,22 +94,11 @@ static int i810_setup_i2c_bus(struct i81
         chan->adapter.owner             = THIS_MODULE;
         chan->adapter.algo_data         = &chan->algo;
         chan->adapter.dev.parent        = &chan->par->dev->dev;
-	switch (conn) {
-	case 1:
-		chan->adapter.id                = I2C_ALGO_DDC_I810;
-		chan->algo.setsda               = i810ddc_setsda;
-		chan->algo.setscl               = i810ddc_setscl;
-		chan->algo.getsda               = i810ddc_getsda;
-		chan->algo.getscl               = i810ddc_getscl;
-		break;
-	case 2:
-		chan->adapter.id                = I2C_ALGO_I2C_I810;
-		chan->algo.setsda               = i810i2c_setsda;
-		chan->algo.setscl               = i810i2c_setscl;
-		chan->algo.getsda               = i810i2c_getsda;
-		chan->algo.getscl               = i810i2c_getscl;
-		break;
-	}
+	chan->adapter.id                = I2C_ALGO_I810;
+	chan->algo.setsda               = i810i2c_setsda;
+	chan->algo.setscl               = i810i2c_setscl;
+	chan->algo.getsda               = i810i2c_getsda;
+	chan->algo.getscl               = i810i2c_getscl;
 	chan->algo.udelay               = 10;
 	chan->algo.mdelay               = 10;
         chan->algo.timeout              = (HZ/2);
@@ -168,11 +112,15 @@ static int i810_setup_i2c_bus(struct i81
         udelay(20);
 
         rc = i2c_bit_add_bus(&chan->adapter);
+
         if (rc == 0) 
                 dev_dbg(&chan->par->dev->dev, "I2C bus %s registered.\n",name);
-        else
+        else {
                 dev_warn(&chan->par->dev->dev, "Failed to register I2C bus "
 			 "%s.\n", name);
+		chan->par = NULL;
+	}
+
         return rc;
 }
 
@@ -180,8 +128,14 @@ void i810_create_i2c_busses(struct i810f
 {
         par->chan[0].par        = par;
 	par->chan[1].par        = par;
-	i810_setup_i2c_bus(&par->chan[0], "I810-DDC", 1);
-	i810_setup_i2c_bus(&par->chan[1], "I810-I2C", 2);
+	par->chan[2].par        = par;
+
+	par->chan[0].ddc_base = GPIOA;
+	i810_setup_i2c_bus(&par->chan[0], "I810-DDC");
+	par->chan[1].ddc_base = GPIOB;
+	i810_setup_i2c_bus(&par->chan[1], "I810-I2C");
+	par->chan[2].ddc_base = GPIOC;
+	i810_setup_i2c_bus(&par->chan[2], "I810-GPIOC");
 }
 
 void i810_delete_i2c_busses(struct i810fb_par *par)
@@ -189,9 +143,14 @@ void i810_delete_i2c_busses(struct i810f
         if (par->chan[0].par)
                 i2c_bit_del_bus(&par->chan[0].adapter);
         par->chan[0].par = NULL;
+
 	if (par->chan[1].par)
 		i2c_bit_del_bus(&par->chan[1].adapter);
 	par->chan[1].par = NULL;
+
+	if (par->chan[2].par)
+		i2c_bit_del_bus(&par->chan[2].adapter);
+	par->chan[2].par = NULL;
 }
 
 static u8 *i810_do_probe_i2c_edid(struct i810fb_i2c_chan *chan)
@@ -221,6 +180,7 @@ static u8 *i810_do_probe_i2c_edid(struct
 		DPRINTK("i810-i2c: I2C Transfer successful\n");
                 return buf;
 	}
+
         DPRINTK("i810-i2c: Unable to read EDID block.\n");
         kfree(buf);
         return NULL;
@@ -233,7 +193,7 @@ int i810_probe_i2c_connector(struct fb_i
         int i;
 
 	DPRINTK("i810-i2c: Probe DDC%i Bus\n", conn);
-	if (conn < 3) {
+	if (conn < 4) {
 		for (i = 0; i < 3; i++) {
 			/* Do the real work */
 			edid = i810_do_probe_i2c_edid(&par->chan[conn-1]);
@@ -253,5 +213,3 @@ int i810_probe_i2c_connector(struct fb_i
 
         return (edid) ? 0 : 1;
 }
-
-
diff --git a/drivers/video/i810/i810.h b/drivers/video/i810/i810.h
--- a/drivers/video/i810/i810.h
+++ b/drivers/video/i810/i810.h
@@ -249,6 +249,7 @@ struct i810fb_i2c_chan {
 	struct i810fb_par *par;
 	struct i2c_adapter adapter;
 	struct i2c_algo_bit_data algo;
+	unsigned long ddc_base;
 };
 
 struct i810fb_par {
@@ -262,7 +263,7 @@ struct i810fb_par {
 	struct heap_data         iring;
 	struct heap_data         cursor_heap;
 	struct vgastate          state;
-	struct i810fb_i2c_chan   chan[2];
+	struct i810fb_i2c_chan   chan[3];
 	atomic_t                 use_count;
 	u32 pseudo_palette[17];
 	unsigned long mmio_start_phys;
diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c
--- a/drivers/video/i810/i810_main.c
+++ b/drivers/video/i810/i810_main.c
@@ -1844,7 +1844,7 @@ static void __devinit i810fb_find_init_m
 #ifdef CONFIG_FB_I810_I2C
 	i810_create_i2c_busses(par);
 
-	for (i = 0; i < 3; i++) {
+	for (i = 0; i < 4; i++) {
 		err = i810_probe_i2c_connector(info, &par->edid, i+1);
 		if (!err)
 			break;
diff --git a/drivers/video/i810/i810_regs.h b/drivers/video/i810/i810_regs.h
--- a/drivers/video/i810/i810_regs.h
+++ b/drivers/video/i810/i810_regs.h
@@ -70,6 +70,7 @@
 #define HVSYNC                0x05000 
 #define GPIOA                 0x05010
 #define GPIOB                 0x05014 
+#define GPIOC                 0x0501C
 
 /* Clock Control and Power Management Registers (06000h 06FFFh) */
 #define DCLK_0D               0x06000


-------------------------------------------------------
SF.Net email is Sponsored by the Better Software Conference & EXPO
September 19-22, 2005 * San Francisco, CA * Development Lifecycle Practices
Agile & Plan-Driven Development * Managing Projects & Teams * Testing & QA
Security * Process Improvement & Measurement * http://www.sqe.com/bsce5sf

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2005-09-04 14:47 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-09-01 21:44 [PATCH 9/10] i810fb: Add i2c/DDC support Antonino A. Daplas
2005-09-04 13:51 ` Nicolas Boichat
2005-09-04 14:46   ` Antonino A. Daplas

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).