Skip to contentSkip to conversation

i810fb: Add i2c/DDC support

PATCH In review Antonino A. Daplas 2 participants 3 messages Sep 1, 2005 – Sep 4, 2005 Framebuffer layer

lore
Share & tools
lore
#0 lore raw

PATCH 9/10 i810fb: Add i2c/DDC support

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*

---
 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(-)
drivers/video/Kconfig +5 -0
@@ -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
drivers/video/i810/Makefile +1 -1
@@ -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
 
drivers/video/i810/i810-i2c.c new file +257 -0

257 lines not on this page · raw message

drivers/video/i810/i810.h +13 -0
@@ -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;
drivers/video/i810/i810_main.c +130 -45

287 lines not on this page · raw message

drivers/video/i810/i810_main.h +7 -0
@@ -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

#1 lore raw

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

Quoted 8 lines · Antonino A. Daplas

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*

[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

#2 lore raw
Quoted 8 lines · Nicolas Boichat

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.

Quoted 4 lines

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

drivers/video/i810/i810-i2c.c +41 -83
@@ -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;
 }
-
-
drivers/video/i810/i810.h +2 -1
@@ -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;
drivers/video/i810/i810_main.c +1 -1
@@ -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;
drivers/video/i810/i810_regs.h +1 -0
@@ -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