[PATCH 4/4] tridentfb: Add DDC support

2015-02-07 Thread Ondrej Zary
Add DDC support for Trident cards.

Tested on TGUI9440, TGUI9680, 3DImage 9750, Blade3D 9880 and Blade XP.

Signed-off-by: Ondrej Zary 
---
 drivers/video/fbdev/Kconfig |9 ++
 drivers/video/fbdev/tridentfb.c |  192 ++-
 2 files changed, 196 insertions(+), 5 deletions(-)

diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 4916c97..08a7a04 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -1682,6 +1682,15 @@ config FB_TRIDENT
  To compile this driver as a module, choose M here: the
  module will be called tridentfb.
 
+config FB_TRIDENT_DDC
+   bool "DDC for Trident support"
+   depends on FB_TRIDENT
+   select FB_DDC
+   select FB_MODE_HELPERS
+   default y
+   help
+ Say Y here if you want DDC support for your Trident graphics card.
+
 config FB_ARK
tristate "ARK 2000PV support"
depends on FB && PCI
diff --git a/drivers/video/fbdev/tridentfb.c b/drivers/video/fbdev/tridentfb.c
index 01b43e9..59471a0 100644
--- a/drivers/video/fbdev/tridentfb.c
+++ b/drivers/video/fbdev/tridentfb.c
@@ -25,6 +25,9 @@
 #include 
 #include 
 
+#include 
+#include 
+
 struct tridentfb_par {
void __iomem *io_virt;  /* iospace virtual memory address */
u32 pseudo_pal[16];
@@ -40,6 +43,11 @@ struct tridentfb_par {
(struct tridentfb_par *par, const char*,
 u32, u32, u32, u32, u32, u32);
unsigned char eng_oper; /* engine operation... */
+#ifdef CONFIG_FB_TRIDENT_DDC
+   bool ddc_registered;
+   struct i2c_adapter ddc_adapter;
+   struct i2c_algo_bit_data ddc_algo;
+#endif
 };
 
 static struct fb_fix_screeninfo tridentfb_fix = {
@@ -53,7 +61,7 @@ static struct fb_fix_screeninfo tridentfb_fix = {
 /* defaults which are normally overriden by user values */
 
 /* video mode */
-static char *mode_option = "640x480-8@60";
+static char *mode_option;
 static int bpp = 8;
 
 static int noaccel;
@@ -174,6 +182,124 @@ static inline u32 readmmr(struct tridentfb_par *par, u16 
r)
return fb_readl(par->io_virt + r);
 }
 
+#ifdef CONFIG_FB_TRIDENT_DDC
+
+#define DDC_SDA_TGUI   BIT(0)
+#define DDC_SCL_TGUI   BIT(1)
+#define DDC_SCL_DRIVE_TGUI BIT(2)
+#define DDC_SDA_DRIVE_TGUI BIT(3)
+#define DDC_MASK_TGUI  (DDC_SCL_DRIVE_TGUI | DDC_SDA_DRIVE_TGUI)
+
+static void tridentfb_ddc_setscl_tgui(void *data, int val)
+{
+   struct tridentfb_par *par = data;
+   u8 reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK_TGUI;
+
+   if (val)
+   reg &= ~DDC_SCL_DRIVE_TGUI; /* disable drive - don't drive hi */
+   else
+   reg |= DDC_SCL_DRIVE_TGUI; /* drive low */
+
+   vga_mm_wcrt(par->io_virt, I2C, reg);
+}
+
+static void tridentfb_ddc_setsda_tgui(void *data, int val)
+{
+   struct tridentfb_par *par = data;
+   u8 reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK_TGUI;
+
+   if (val)
+   reg &= ~DDC_SDA_DRIVE_TGUI; /* disable drive - don't drive hi */
+   else
+   reg |= DDC_SDA_DRIVE_TGUI; /* drive low */
+
+   vga_mm_wcrt(par->io_virt, I2C, reg);
+}
+
+static int tridentfb_ddc_getsda_tgui(void *data)
+{
+   struct tridentfb_par *par = data;
+
+   return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SDA_TGUI);
+}
+
+#define DDC_SDA_IN BIT(0)
+#define DDC_SCL_OUTBIT(1)
+#define DDC_SDA_OUTBIT(3)
+#define DDC_SCL_IN BIT(6)
+#define DDC_MASK   (DDC_SCL_OUT | DDC_SDA_OUT)
+
+static void tridentfb_ddc_setscl(void *data, int val)
+{
+   struct tridentfb_par *par = data;
+   unsigned char reg;
+
+   reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK;
+   if (val)
+   reg |= DDC_SCL_OUT;
+   else
+   reg &= ~DDC_SCL_OUT;
+   vga_mm_wcrt(par->io_virt, I2C, reg);
+}
+
+static void tridentfb_ddc_setsda(void *data, int val)
+{
+   struct tridentfb_par *par = data;
+   unsigned char reg;
+
+   reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK;
+   if (!val)
+   reg |= DDC_SDA_OUT;
+   else
+   reg &= ~DDC_SDA_OUT;
+   vga_mm_wcrt(par->io_virt, I2C, reg);
+}
+
+static int tridentfb_ddc_getscl(void *data)
+{
+   struct tridentfb_par *par = data;
+
+   return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SCL_IN);
+}
+
+static int tridentfb_ddc_getsda(void *data)
+{
+   struct tridentfb_par *par = data;
+
+   return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SDA_IN);
+}
+
+static int tridentfb_setup_ddc_bus(struct fb_info *info)
+{
+   struct tridentfb_par *par = info->par;
+
+   strlcpy(par->ddc_adapter.name, info->fix.id,
+   sizeof(par->ddc_adapter.name));
+   par->ddc_adapter.owner  = THIS_MODULE;
+   par->ddc_adapter.class  = I2C_CLASS_DDC;
+   par->ddc_adapter.algo_data  = >ddc_algo;
+   par->ddc_adapter.dev.parent = info->device;
+   if 

[PATCH 4/4] tridentfb: Add DDC support

2015-02-07 Thread Ondrej Zary
Add DDC support for Trident cards.

Tested on TGUI9440, TGUI9680, 3DImage 9750, Blade3D 9880 and Blade XP.

Signed-off-by: Ondrej Zary li...@rainbow-software.org
---
 drivers/video/fbdev/Kconfig |9 ++
 drivers/video/fbdev/tridentfb.c |  192 ++-
 2 files changed, 196 insertions(+), 5 deletions(-)

diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 4916c97..08a7a04 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -1682,6 +1682,15 @@ config FB_TRIDENT
  To compile this driver as a module, choose M here: the
  module will be called tridentfb.
 
+config FB_TRIDENT_DDC
+   bool DDC for Trident support
+   depends on FB_TRIDENT
+   select FB_DDC
+   select FB_MODE_HELPERS
+   default y
+   help
+ Say Y here if you want DDC support for your Trident graphics card.
+
 config FB_ARK
tristate ARK 2000PV support
depends on FB  PCI
diff --git a/drivers/video/fbdev/tridentfb.c b/drivers/video/fbdev/tridentfb.c
index 01b43e9..59471a0 100644
--- a/drivers/video/fbdev/tridentfb.c
+++ b/drivers/video/fbdev/tridentfb.c
@@ -25,6 +25,9 @@
 #include video/vga.h
 #include video/trident.h
 
+#include linux/i2c.h
+#include linux/i2c-algo-bit.h
+
 struct tridentfb_par {
void __iomem *io_virt;  /* iospace virtual memory address */
u32 pseudo_pal[16];
@@ -40,6 +43,11 @@ struct tridentfb_par {
(struct tridentfb_par *par, const char*,
 u32, u32, u32, u32, u32, u32);
unsigned char eng_oper; /* engine operation... */
+#ifdef CONFIG_FB_TRIDENT_DDC
+   bool ddc_registered;
+   struct i2c_adapter ddc_adapter;
+   struct i2c_algo_bit_data ddc_algo;
+#endif
 };
 
 static struct fb_fix_screeninfo tridentfb_fix = {
@@ -53,7 +61,7 @@ static struct fb_fix_screeninfo tridentfb_fix = {
 /* defaults which are normally overriden by user values */
 
 /* video mode */
-static char *mode_option = 640x480-8@60;
+static char *mode_option;
 static int bpp = 8;
 
 static int noaccel;
@@ -174,6 +182,124 @@ static inline u32 readmmr(struct tridentfb_par *par, u16 
r)
return fb_readl(par-io_virt + r);
 }
 
+#ifdef CONFIG_FB_TRIDENT_DDC
+
+#define DDC_SDA_TGUI   BIT(0)
+#define DDC_SCL_TGUI   BIT(1)
+#define DDC_SCL_DRIVE_TGUI BIT(2)
+#define DDC_SDA_DRIVE_TGUI BIT(3)
+#define DDC_MASK_TGUI  (DDC_SCL_DRIVE_TGUI | DDC_SDA_DRIVE_TGUI)
+
+static void tridentfb_ddc_setscl_tgui(void *data, int val)
+{
+   struct tridentfb_par *par = data;
+   u8 reg = vga_mm_rcrt(par-io_virt, I2C)  DDC_MASK_TGUI;
+
+   if (val)
+   reg = ~DDC_SCL_DRIVE_TGUI; /* disable drive - don't drive hi */
+   else
+   reg |= DDC_SCL_DRIVE_TGUI; /* drive low */
+
+   vga_mm_wcrt(par-io_virt, I2C, reg);
+}
+
+static void tridentfb_ddc_setsda_tgui(void *data, int val)
+{
+   struct tridentfb_par *par = data;
+   u8 reg = vga_mm_rcrt(par-io_virt, I2C)  DDC_MASK_TGUI;
+
+   if (val)
+   reg = ~DDC_SDA_DRIVE_TGUI; /* disable drive - don't drive hi */
+   else
+   reg |= DDC_SDA_DRIVE_TGUI; /* drive low */
+
+   vga_mm_wcrt(par-io_virt, I2C, reg);
+}
+
+static int tridentfb_ddc_getsda_tgui(void *data)
+{
+   struct tridentfb_par *par = data;
+
+   return !!(vga_mm_rcrt(par-io_virt, I2C)  DDC_SDA_TGUI);
+}
+
+#define DDC_SDA_IN BIT(0)
+#define DDC_SCL_OUTBIT(1)
+#define DDC_SDA_OUTBIT(3)
+#define DDC_SCL_IN BIT(6)
+#define DDC_MASK   (DDC_SCL_OUT | DDC_SDA_OUT)
+
+static void tridentfb_ddc_setscl(void *data, int val)
+{
+   struct tridentfb_par *par = data;
+   unsigned char reg;
+
+   reg = vga_mm_rcrt(par-io_virt, I2C)  DDC_MASK;
+   if (val)
+   reg |= DDC_SCL_OUT;
+   else
+   reg = ~DDC_SCL_OUT;
+   vga_mm_wcrt(par-io_virt, I2C, reg);
+}
+
+static void tridentfb_ddc_setsda(void *data, int val)
+{
+   struct tridentfb_par *par = data;
+   unsigned char reg;
+
+   reg = vga_mm_rcrt(par-io_virt, I2C)  DDC_MASK;
+   if (!val)
+   reg |= DDC_SDA_OUT;
+   else
+   reg = ~DDC_SDA_OUT;
+   vga_mm_wcrt(par-io_virt, I2C, reg);
+}
+
+static int tridentfb_ddc_getscl(void *data)
+{
+   struct tridentfb_par *par = data;
+
+   return !!(vga_mm_rcrt(par-io_virt, I2C)  DDC_SCL_IN);
+}
+
+static int tridentfb_ddc_getsda(void *data)
+{
+   struct tridentfb_par *par = data;
+
+   return !!(vga_mm_rcrt(par-io_virt, I2C)  DDC_SDA_IN);
+}
+
+static int tridentfb_setup_ddc_bus(struct fb_info *info)
+{
+   struct tridentfb_par *par = info-par;
+
+   strlcpy(par-ddc_adapter.name, info-fix.id,
+   sizeof(par-ddc_adapter.name));
+   par-ddc_adapter.owner  = THIS_MODULE;
+   par-ddc_adapter.class  = I2C_CLASS_DDC;
+   par-ddc_adapter.algo_data  = par-ddc_algo;
+