diff mbox

[v3] s3fb: add DDC support

Message ID 201104032236.18936.linux@rainbow-software.org (mailing list archive)
State Accepted
Headers show

Commit Message

Ondrej Zary April 3, 2011, 8:36 p.m. UTC
Add I2C support for the DDC bus and also default mode initialization by
reading monitor EDID to the s3fb driver.

Tested on Trio64V+ (2 cards), Trio64V2/DX, Virge (3 cards),
Virge/DX (3 cards), Virge/GX2, Trio3D/2X (4 cards), Trio3D.

Will probably not work on Trio32 - my 2 cards have DDC support in BIOS that
looks different from the other cards but the DDC pins on the VGA connector
are not connected.

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

Comments

Ondrej Zajicek April 4, 2011, 8:33 a.m. UTC | #1
On Sun, Apr 03, 2011 at 10:36:15PM +0200, Ondrej Zary wrote:
> Add I2C support for the DDC bus and also default mode initialization by
> reading monitor EDID to the s3fb driver.
> 
> Tested on Trio64V+ (2 cards), Trio64V2/DX, Virge (3 cards),
> Virge/DX (3 cards), Virge/GX2, Trio3D/2X (4 cards), Trio3D.
> 
> Will probably not work on Trio32 - my 2 cards have DDC support in BIOS that
> looks different from the other cards but the DDC pins on the VGA connector
> are not connected.
> 
> Signed-off-by: Ondrej Zary <linux@rainbow-software.org>

Acked-by: Ondrej Zajicek <santiago@crfreenet.org>
Paul Mundt April 4, 2011, 8:42 a.m. UTC | #2
On Mon, Apr 04, 2011 at 10:33:16AM +0200, Ondrej Zajicek wrote:
> On Sun, Apr 03, 2011 at 10:36:15PM +0200, Ondrej Zary wrote:
> > Add I2C support for the DDC bus and also default mode initialization by
> > reading monitor EDID to the s3fb driver.
> > 
> > Tested on Trio64V+ (2 cards), Trio64V2/DX, Virge (3 cards),
> > Virge/DX (3 cards), Virge/GX2, Trio3D/2X (4 cards), Trio3D.
> > 
> > Will probably not work on Trio32 - my 2 cards have DDC support in BIOS that
> > looks different from the other cards but the DDC pins on the VGA connector
> > are not connected.
> > 
> > Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
> 
> Acked-by: Ondrej Zajicek <santiago@crfreenet.org>
> 
Is there much reason to leave this optional? Or are you simply waiting
for testing feedback from Trio32 users before default-enabling it?

It would generally be nice to avoid these sorts of config options for a
specific driver if it already is quite capable of handling the
unsupported cases and has a reasonable fallback on defaults via the error
path.
--
To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ondrej Zary April 4, 2011, 9:25 a.m. UTC | #3
On Monday 04 April 2011, Paul Mundt wrote:
> On Mon, Apr 04, 2011 at 10:33:16AM +0200, Ondrej Zajicek wrote:
> > On Sun, Apr 03, 2011 at 10:36:15PM +0200, Ondrej Zary wrote:
> > > Add I2C support for the DDC bus and also default mode initialization by
> > > reading monitor EDID to the s3fb driver.
> > >
> > > Tested on Trio64V+ (2 cards), Trio64V2/DX, Virge (3 cards),
> > > Virge/DX (3 cards), Virge/GX2, Trio3D/2X (4 cards), Trio3D.
> > >
> > > Will probably not work on Trio32 - my 2 cards have DDC support in BIOS
> > > that looks different from the other cards but the DDC pins on the VGA
> > > connector are not connected.
> > >
> > > Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
> >
> > Acked-by: Ondrej Zajicek <santiago@crfreenet.org>
>
> Is there much reason to leave this optional? Or are you simply waiting
> for testing feedback from Trio32 users before default-enabling it?
>
> It would generally be nice to avoid these sorts of config options for a
> specific driver if it already is quite capable of handling the
> unsupported cases and has a reasonable fallback on defaults via the error
> path.

If the config option is not desired, I'll remove it, I don't like it either. 
Just did it like some other fb drivers do. The only reason why someone would 
want to disable DDC is probably to have I2C-less kernel.

If the EDID cannot be read (old monitor connected or DDC pins not connected 
like on Trio32 cards) or the card does not have enough memory (1280x1024 LCD 
on a 1MB card), the driver falls back to 640x480-8@60.
Paul Mundt April 6, 2011, 5:01 p.m. UTC | #4
On Mon, Apr 04, 2011 at 11:25:36AM +0200, Ondrej Zary wrote:
> On Monday 04 April 2011, Paul Mundt wrote:
> > On Mon, Apr 04, 2011 at 10:33:16AM +0200, Ondrej Zajicek wrote:
> > > On Sun, Apr 03, 2011 at 10:36:15PM +0200, Ondrej Zary wrote:
> > > > Add I2C support for the DDC bus and also default mode initialization by
> > > > reading monitor EDID to the s3fb driver.
> > > >
> > > > Tested on Trio64V+ (2 cards), Trio64V2/DX, Virge (3 cards),
> > > > Virge/DX (3 cards), Virge/GX2, Trio3D/2X (4 cards), Trio3D.
> > > >
> > > > Will probably not work on Trio32 - my 2 cards have DDC support in BIOS
> > > > that looks different from the other cards but the DDC pins on the VGA
> > > > connector are not connected.
> > > >
> > > > Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
> > >
> > > Acked-by: Ondrej Zajicek <santiago@crfreenet.org>
> >
> > Is there much reason to leave this optional? Or are you simply waiting
> > for testing feedback from Trio32 users before default-enabling it?
> >
> > It would generally be nice to avoid these sorts of config options for a
> > specific driver if it already is quite capable of handling the
> > unsupported cases and has a reasonable fallback on defaults via the error
> > path.
> 
> If the config option is not desired, I'll remove it, I don't like it either. 
> Just did it like some other fb drivers do. The only reason why someone would 
> want to disable DDC is probably to have I2C-less kernel.
> 
Oh, that's right, i2c is one of those pain in the ass subsystems that
refuses to provide a stubbed implementation of the API for i2c disabled
systems. Lets just stick with the first version for now.
--
To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Paul Mundt April 7, 2011, 5:01 p.m. UTC | #5
On Sun, Apr 03, 2011 at 10:36:15PM +0200, Ondrej Zary wrote:
> Add I2C support for the DDC bus and also default mode initialization by
> reading monitor EDID to the s3fb driver.
> 
> Tested on Trio64V+ (2 cards), Trio64V2/DX, Virge (3 cards),
> Virge/DX (3 cards), Virge/GX2, Trio3D/2X (4 cards), Trio3D.
> 
> Will probably not work on Trio32 - my 2 cards have DDC support in BIOS that
> looks different from the other cards but the DDC pins on the VGA connector
> are not connected.
> 
> Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
> 
This no longer builds with the latest i2c changes merged, please respin
this against the current kernel and resubmit.
--
To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

--- linux-2.6.38-rc4-/drivers/video/s3fb.c	2011-03-31 20:57:57.000000000 +0200
+++ linux-2.6.38-rc4/drivers/video/s3fb.c	2011-04-03 20:32:46.000000000 +0200
@@ -25,6 +25,10 @@ 
 #include <linux/console.h> /* Why should fb driver call console functions? because console_lock() */
 #include <video/vga.h>
 
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+
 #ifdef CONFIG_MTRR
 #include <asm/mtrr.h>
 #endif
@@ -36,6 +40,12 @@  struct s3fb_info {
 	struct mutex open_lock;
 	unsigned int ref_count;
 	u32 pseudo_palette[16];
+#ifdef CONFIG_FB_S3_DDC
+	u8 __iomem *mmio;
+	bool ddc_registered;
+	struct i2c_adapter ddc_adapter;
+	struct i2c_algo_bit_data ddc_algo;
+#endif
 };
 
 
@@ -105,6 +115,9 @@  static const char * const s3_names[] = {
 #define CHIP_UNDECIDED_FLAG	0x80
 #define CHIP_MASK		0xFF
 
+#define MMIO_OFFSET		0x1000000
+#define MMIO_SIZE		0x10000
+
 /* CRT timing register sets */
 
 static const struct vga_regset s3_h_total_regs[]        = {{0x00, 0, 7}, {0x5D, 0, 0}, VGA_REGSET_END};
@@ -140,7 +153,7 @@  static const struct svga_timing_regs s3_
 /* Module parameters */
 
 
-static char *mode_option __devinitdata = "640x480-8@60";
+static char *mode_option __devinitdata;
 
 #ifdef CONFIG_MTRR
 static int mtrr __devinitdata = 1;
@@ -169,6 +182,119 @@  MODULE_PARM_DESC(fasttext, "Enable S3 fa
 
 /* ------------------------------------------------------------------------- */
 
+#ifdef CONFIG_FB_S3_DDC
+
+#define DDC_REG		0xaa		/* Trio 3D/1X/2X */
+#define DDC_MMIO_REG	0xff20		/* all other chips */
+#define DDC_SCL_OUT	(1 << 0)
+#define DDC_SDA_OUT	(1 << 1)
+#define DDC_SCL_IN	(1 << 2)
+#define DDC_SDA_IN	(1 << 3)
+#define DDC_DRIVE_EN	(1 << 4)
+
+static bool s3fb_ddc_needs_mmio(int chip)
+{
+	return !(chip == CHIP_360_TRIO3D_1X  ||
+		 chip == CHIP_362_TRIO3D_2X  ||
+		 chip == CHIP_368_TRIO3D_2X);
+}
+
+static u8 s3fb_ddc_read(struct s3fb_info *par)
+{
+	if (s3fb_ddc_needs_mmio(par->chip))
+		return readb(par->mmio + DDC_MMIO_REG);
+	else
+		return vga_rcrt(par->state.vgabase, DDC_REG);
+}
+
+static void s3fb_ddc_write(struct s3fb_info *par, u8 val)
+{
+	if (s3fb_ddc_needs_mmio(par->chip))
+		writeb(val, par->mmio + DDC_MMIO_REG);
+	else
+		vga_wcrt(par->state.vgabase, DDC_REG, val);
+}
+
+static void s3fb_ddc_setscl(void *data, int val)
+{
+	struct s3fb_info *par = data;
+	unsigned char reg;
+
+	reg = s3fb_ddc_read(par) | DDC_DRIVE_EN;
+	if (val)
+		reg |= DDC_SCL_OUT;
+	else
+		reg &= ~DDC_SCL_OUT;
+	s3fb_ddc_write(par, reg);
+}
+
+static void s3fb_ddc_setsda(void *data, int val)
+{
+	struct s3fb_info *par = data;
+	unsigned char reg;
+
+	reg = s3fb_ddc_read(par) | DDC_DRIVE_EN;
+	if (val)
+		reg |= DDC_SDA_OUT;
+	else
+		reg &= ~DDC_SDA_OUT;
+	s3fb_ddc_write(par, reg);
+}
+
+static int s3fb_ddc_getscl(void *data)
+{
+	struct s3fb_info *par = data;
+
+	return !!(s3fb_ddc_read(par) & DDC_SCL_IN);
+}
+
+static int s3fb_ddc_getsda(void *data)
+{
+	struct s3fb_info *par = data;
+
+	return !!(s3fb_ddc_read(par) & DDC_SDA_IN);
+}
+
+static int __devinit s3fb_setup_ddc_bus(struct fb_info *info)
+{
+	struct s3fb_info *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;
+	par->ddc_adapter.dev.parent	= info->device;
+	par->ddc_algo.setsda		= s3fb_ddc_setsda;
+	par->ddc_algo.setscl		= s3fb_ddc_setscl;
+	par->ddc_algo.getsda		= s3fb_ddc_getsda;
+	par->ddc_algo.getscl		= s3fb_ddc_getscl;
+	par->ddc_algo.udelay		= 10;
+	par->ddc_algo.timeout		= 20;
+	par->ddc_algo.data		= par;
+
+	i2c_set_adapdata(&par->ddc_adapter, par);
+
+	/*
+	 * some Virge cards have external MUX to switch chip I2C bus between
+	 * DDC and extension pins - switch it do DDC
+	 */
+/*	vga_wseq(par->state.vgabase, 0x08, 0x06); - not needed, already unlocked */
+	if (par->chip == CHIP_357_VIRGE_GX2 ||
+	    par->chip == CHIP_359_VIRGE_GX2P)
+		svga_wseq_mask(par->state.vgabase, 0x0d, 0x01, 0x03);
+	else
+		svga_wseq_mask(par->state.vgabase, 0x0d, 0x00, 0x03);
+	/* some Virge need this or the DDC is ignored */
+	svga_wcrt_mask(par->state.vgabase, 0x5c, 0x03, 0x03);
+
+	return i2c_bit_add_bus(&par->ddc_adapter);
+}
+#endif /* CONFIG_FB_S3_DDC */
+
+
+/* ------------------------------------------------------------------------- */
+
 /* Set font in S3 fast text mode */
 
 static void s3fb_settile_fast(struct fb_info *info, struct fb_tilemap *map)
@@ -994,6 +1120,7 @@  static int __devinit s3_pci_probe(struct
 	struct s3fb_info *par;
 	int rc;
 	u8 regval, cr38, cr39;
+	bool found = false;
 
 	/* Ignore secondary VGA device because there is no VGA arbitration */
 	if (! svga_primary_device(dev)) {
@@ -1117,15 +1244,64 @@  static int __devinit s3_pci_probe(struct
 	info->fix.ypanstep = 0;
 	info->fix.accel = FB_ACCEL_NONE;
 	info->pseudo_palette = (void*) (par->pseudo_palette);
+	info->var.bits_per_pixel = 8;
+
+#ifdef CONFIG_FB_S3_DDC
+	/* Enable MMIO if needed */
+	if (s3fb_ddc_needs_mmio(par->chip)) {
+		par->mmio = ioremap(info->fix.smem_start + MMIO_OFFSET, MMIO_SIZE);
+		if (par->mmio)
+			svga_wcrt_mask(par->state.vgabase, 0x53, 0x08, 0x08);	/* enable MMIO */
+		else
+			dev_err(info->device, "unable to map MMIO at 0x%lx, disabling DDC",
+				info->fix.smem_start + MMIO_OFFSET);
+	}
+	if (!s3fb_ddc_needs_mmio(par->chip) || par->mmio)
+		if (s3fb_setup_ddc_bus(info) == 0) {
+			u8 *edid = fb_ddc_read(&par->ddc_adapter);
+			par->ddc_registered = true;
+			if (edid) {
+				fb_edid_to_monspecs(edid, &info->monspecs);
+				kfree(edid);
+				if (!info->monspecs.modedb)
+					dev_err(info->device, "error getting mode database\n");
+				else {
+					const struct fb_videomode *m;
+
+					fb_videomode_to_modelist(info->monspecs.modedb,
+								 info->monspecs.modedb_len,
+								 &info->modelist);
+					m = fb_find_best_display(&info->monspecs, &info->modelist);
+					if (m) {
+						fb_videomode_to_var(&info->var, m);
+						/* fill all other info->var's fields */
+						if (s3fb_check_var(&info->var, info) == 0)
+							found = true;
+					}
+				}
+			}
+		}
+#endif
+	if (!mode_option && !found)
+		mode_option = "640x480-8@60";
 
 	/* Prepare startup mode */
-	rc = fb_find_mode(&(info->var), info, mode_option, NULL, 0, NULL, 8);
-	if (! ((rc == 1) || (rc == 2))) {
-		rc = -EINVAL;
-		dev_err(info->device, "mode %s not found\n", mode_option);
-		goto err_find_mode;
+	if (mode_option) {
+		rc = fb_find_mode(&info->var, info, mode_option,
+				   info->monspecs.modedb, info->monspecs.modedb_len,
+				   NULL, info->var.bits_per_pixel);
+		if (!rc || rc == 4) {
+			rc = -EINVAL;
+			dev_err(info->device, "mode %s not found\n", mode_option);
+			fb_destroy_modedb(info->monspecs.modedb);
+			info->monspecs.modedb = NULL;
+			goto err_find_mode;
+		}
 	}
 
+	fb_destroy_modedb(info->monspecs.modedb);
+	info->monspecs.modedb = NULL;
+
 	/* maximize virtual vertical size for fast scrolling */
 	info->var.yres_virtual = info->fix.smem_len * 8 /
 			(info->var.bits_per_pixel * info->var.xres_virtual);
@@ -1171,6 +1347,12 @@  err_reg_fb:
 	fb_dealloc_cmap(&info->cmap);
 err_alloc_cmap:
 err_find_mode:
+#ifdef CONFIG_FB_S3_DDC
+	if (par->ddc_registered)
+		i2c_del_adapter(&par->ddc_adapter);
+	if (par->mmio)
+		iounmap(par->mmio);
+#endif
 	pci_iounmap(dev, info->screen_base);
 err_iomap:
 	pci_release_regions(dev);
@@ -1202,6 +1384,13 @@  static void __devexit s3_pci_remove(stru
 		unregister_framebuffer(info);
 		fb_dealloc_cmap(&info->cmap);
 
+#ifdef CONFIG_FB_S3_DDC
+		if (par->ddc_registered)
+			i2c_del_adapter(&par->ddc_adapter);
+		if (par->mmio)
+			iounmap(par->mmio);
+#endif
+
 		pci_iounmap(dev, info->screen_base);
 		pci_release_regions(dev);
 /*		pci_disable_device(dev); */
--- linux-2.6.38-rc4-orig/drivers/video/Kconfig	2011-02-08 01:03:55.000000000 +0100
+++ linux-2.6.38-rc4/drivers/video/Kconfig	2011-04-03 20:28:26.000000000 +0200
@@ -1508,6 +1508,14 @@  config FB_S3
 	---help---
 	  Driver for graphics boards with S3 Trio / S3 Virge chip.
 
+config FB_S3_DDC
+	bool "DDC for S3 support"
+	depends on FB_S3
+	select FB_DDC
+	default y
+	help
+	  Say Y here if you want DDC support for your S3 graphics card.
+
 config FB_SAVAGE
 	tristate "S3 Savage support"
 	depends on FB && PCI && EXPERIMENTAL