diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e0c7edf6c82b..b20f0f81dc01 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -448,6 +448,15 @@ config FB_CYBER2000_DDC Say Y here if you want DDC support for your CyberPro graphics card. This is only I2C bus support, driver does not use EDID. +config FB_CYBER2000_I2C + bool "CyberPro 2000/2010/5000 I2C support" + depends on FB_CYBER2000 && I2C && ARCH_NETWINDER + select I2C_ALGOBIT + help + Enable support for the I2C video decoder interface on the + Integraphics CyberPro 20x0 and 5000 VGA chips. This is used + on the Netwinder machines for the SAA7111 video capture. + config FB_APOLLO bool depends on (FB = y) && APOLLO diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c index eeccdb8f4848..27cb3b23c3da 100644 --- a/drivers/video/cyber2000fb.c +++ b/drivers/video/cyber2000fb.c @@ -47,7 +47,6 @@ #include #include #include - #include #include @@ -99,6 +98,11 @@ struct cfb_info { struct i2c_adapter ddc_adapter; struct i2c_algo_bit_data ddc_algo; #endif + +#ifdef CONFIG_FB_CYBER2000_I2C + struct i2c_adapter i2c_adapter; + struct i2c_algo_bit_data i2c_algo; +#endif }; static char *default_font = "Acorn8x8"; @@ -1131,6 +1135,11 @@ int cyber2000fb_attach(struct cyberpro_info *info, int idx) { if (int_cfb_info != NULL) { info->dev = int_cfb_info->dev; +#ifdef CONFIG_FB_CYBER2000_I2C + info->i2c = &int_cfb_info->i2c_adapter; +#else + info->i2c = NULL; +#endif info->regs = int_cfb_info->regs; info->fb = int_cfb_info->fb.screen_base; info->fb_size = int_cfb_info->fb.fix.smem_len; @@ -1251,6 +1260,86 @@ static int __devinit cyber2000fb_setup_ddc_bus(struct cfb_info *cfb) } #endif /* CONFIG_FB_CYBER2000_DDC */ +#ifdef CONFIG_FB_CYBER2000_I2C +static void cyber2000fb_i2c_setsda(void *data, int state) +{ + struct cfb_info *cfb = data; + unsigned int latch2; + + spin_lock(&cfb->reg_b0_lock); + latch2 = cyber2000_grphr(EXT_LATCH2, cfb); + latch2 &= EXT_LATCH2_I2C_CLKEN; + if (state) + latch2 |= EXT_LATCH2_I2C_DATEN; + cyber2000_grphw(EXT_LATCH2, latch2, cfb); + spin_unlock(&cfb->reg_b0_lock); +} + +static void cyber2000fb_i2c_setscl(void *data, int state) +{ + struct cfb_info *cfb = data; + unsigned int latch2; + + spin_lock(&cfb->reg_b0_lock); + latch2 = cyber2000_grphr(EXT_LATCH2, cfb); + latch2 &= EXT_LATCH2_I2C_DATEN; + if (state) + latch2 |= EXT_LATCH2_I2C_CLKEN; + cyber2000_grphw(EXT_LATCH2, latch2, cfb); + spin_unlock(&cfb->reg_b0_lock); +} + +static int cyber2000fb_i2c_getsda(void *data) +{ + struct cfb_info *cfb = data; + int ret; + + spin_lock(&cfb->reg_b0_lock); + ret = !!(cyber2000_grphr(EXT_LATCH2, cfb) & EXT_LATCH2_I2C_DAT); + spin_unlock(&cfb->reg_b0_lock); + + return ret; +} + +static int cyber2000fb_i2c_getscl(void *data) +{ + struct cfb_info *cfb = data; + int ret; + + spin_lock(&cfb->reg_b0_lock); + ret = !!(cyber2000_grphr(EXT_LATCH2, cfb) & EXT_LATCH2_I2C_CLK); + spin_unlock(&cfb->reg_b0_lock); + + return ret; +} + +static int __devinit cyber2000fb_i2c_register(struct cfb_info *cfb) +{ + strlcpy(cfb->i2c_adapter.name, cfb->fb.fix.id, + sizeof(cfb->i2c_adapter.name)); + cfb->i2c_adapter.owner = THIS_MODULE; + cfb->i2c_adapter.algo_data = &cfb->i2c_algo; + cfb->i2c_adapter.dev.parent = &cfb->dev->dev; + cfb->i2c_algo.setsda = cyber2000fb_i2c_setsda; + cfb->i2c_algo.setscl = cyber2000fb_i2c_setscl; + cfb->i2c_algo.getsda = cyber2000fb_i2c_getsda; + cfb->i2c_algo.getscl = cyber2000fb_i2c_getscl; + cfb->i2c_algo.udelay = 5; + cfb->i2c_algo.timeout = msecs_to_jiffies(100); + cfb->i2c_algo.data = cfb; + + return i2c_bit_add_bus(&cfb->i2c_adapter); +} + +static void cyber2000fb_i2c_unregister(struct cfb_info *cfb) +{ + i2c_del_adapter(&cfb->i2c_adapter); +} +#else +#define cyber2000fb_i2c_register(cfb) (0) +#define cyber2000fb_i2c_unregister(cfb) do { } while (0) +#endif + /* * These parameters give * 640x480, hsync 31.5kHz, vsync 60Hz @@ -1520,7 +1609,14 @@ static int __devinit cyberpro_common_probe(struct cfb_info *cfb) if (cfb->dev) cfb->fb.device = &cfb->dev->dev; + + err = cyber2000fb_i2c_register(cfb); + if (err) + goto failed; + err = register_framebuffer(&cfb->fb); + if (err) + cyber2000fb_i2c_unregister(cfb); failed: #ifdef CONFIG_FB_CYBER2000_DDC @@ -1530,6 +1626,16 @@ static int __devinit cyberpro_common_probe(struct cfb_info *cfb) return err; } +static void __devexit cyberpro_common_remove(struct cfb_info *cfb) +{ + unregister_framebuffer(&cfb->fb); +#ifdef CONFIG_FB_CYBER2000_DDC + if (cfb->ddc_registered) + i2c_del_adapter(&cfb->ddc_adapter); +#endif + cyber2000fb_i2c_unregister(cfb); +} + static void cyberpro_common_resume(struct cfb_info *cfb) { cyberpro_init_hw(cfb); @@ -1769,19 +1875,7 @@ static void __devexit cyberpro_pci_remove(struct pci_dev *dev) struct cfb_info *cfb = pci_get_drvdata(dev); if (cfb) { - /* - * If unregister_framebuffer fails, then - * we will be leaving hooks that could cause - * oopsen laying around. - */ - if (unregister_framebuffer(&cfb->fb)) - printk(KERN_WARNING "%s: danger Will Robinson, " - "danger danger! Oopsen imminent!\n", - cfb->fb.fix.id); -#ifdef CONFIG_FB_CYBER2000_DDC - if (cfb->ddc_registered) - i2c_del_adapter(&cfb->ddc_adapter); -#endif + cyberpro_common_remove(cfb); iounmap(cfb->region); cyberpro_free_fb_info(cfb); diff --git a/drivers/video/cyber2000fb.h b/drivers/video/cyber2000fb.h index de4fc43e51c1..814cce5acf12 100644 --- a/drivers/video/cyber2000fb.h +++ b/drivers/video/cyber2000fb.h @@ -465,6 +465,7 @@ struct cfb_info; struct cyberpro_info { struct pci_dev *dev; + struct i2c_adapter *i2c; unsigned char __iomem *regs; char __iomem *fb; char dev_name[32];