2011-04-03 20:36:35

by Ondrej Zary

[permalink] [raw]
Subject: [PATCH v3] s3fb: add DDC support

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 <[email protected]>

--- 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


--
Ondrej Zary


2011-04-04 08:21:05

by Ondrej Zajicek

[permalink] [raw]
Subject: Re: [PATCH v3] s3fb: add DDC support

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 <[email protected]>

Acked-by: Ondrej Zajicek <[email protected]>

--
Elen sila lumenn' omentielvo

Ondrej 'SanTiago' Zajicek (email: [email protected])
OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net)
"To err is human -- to blame it on a computer is even more so."


Attachments:
(No filename) (825.00 B)
signature.asc (197.00 B)
Digital signature
Download all attachments

2011-04-04 08:42:26

by Paul Mundt

[permalink] [raw]
Subject: Re: [PATCH v3] s3fb: add DDC support

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 <[email protected]>
>
> Acked-by: Ondrej Zajicek <[email protected]>
>
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.

2011-04-04 09:25:46

by Ondrej Zary

[permalink] [raw]
Subject: Re: [PATCH v3] s3fb: add DDC support

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 <[email protected]>
> >
> > Acked-by: Ondrej Zajicek <[email protected]>
>
> 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.

--
Ondrej Zary

2011-04-04 20:45:12

by Ondrej Zary

[permalink] [raw]
Subject: Re: [PATCH v3] s3fb: add DDC support

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 <[email protected]>

---
This is the CONFIG-less version.

--- 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-04 22:22:11.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,10 @@ struct s3fb_info {
struct mutex open_lock;
unsigned int ref_count;
u32 pseudo_palette[16];
+ u8 __iomem *mmio;
+ bool ddc_registered;
+ struct i2c_adapter ddc_adapter;
+ struct i2c_algo_bit_data ddc_algo;
};


@@ -105,6 +113,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 +151,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 +180,116 @@ MODULE_PARM_DESC(fasttext, "Enable S3 fa

/* ------------------------------------------------------------------------- */

+#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);
+}
+
+
+/* ------------------------------------------------------------------------- */
+
/* Set font in S3 fast text mode */

static void s3fb_settile_fast(struct fb_info *info, struct fb_tilemap *map)
@@ -994,6 +1115,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 +1239,63 @@ 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;
+
+ /* 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;
+ }
+ }
+ }
+ }
+
+ 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 +1341,10 @@ err_reg_fb:
fb_dealloc_cmap(&info->cmap);
err_alloc_cmap:
err_find_mode:
+ if (par->ddc_registered)
+ i2c_del_adapter(&par->ddc_adapter);
+ if (par->mmio)
+ iounmap(par->mmio);
pci_iounmap(dev, info->screen_base);
err_iomap:
pci_release_regions(dev);
@@ -1202,6 +1376,11 @@ static void __devexit s3_pci_remove(stru
unregister_framebuffer(info);
fb_dealloc_cmap(&info->cmap);

+ if (par->ddc_registered)
+ i2c_del_adapter(&par->ddc_adapter);
+ if (par->mmio)
+ iounmap(par->mmio);
+
pci_iounmap(dev, info->screen_base);
pci_release_regions(dev);
/* pci_disable_device(dev); */


--
Ondrej Zary

2011-04-06 17:02:27

by Paul Mundt

[permalink] [raw]
Subject: Re: [PATCH v3] s3fb: add DDC support

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 <[email protected]>
> > >
> > > Acked-by: Ondrej Zajicek <[email protected]>
> >
> > 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.

2011-04-07 17:02:12

by Paul Mundt

[permalink] [raw]
Subject: Re: [PATCH v3] s3fb: add DDC support

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 <[email protected]>
>
This no longer builds with the latest i2c changes merged, please respin
this against the current kernel and resubmit.

2011-04-11 21:45:04

by Ondrej Zary

[permalink] [raw]
Subject: [PATCH v4] s3fb: add DDC support

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 <[email protected]>

--- linux-2.6.39-rc2-/drivers/video/s3fb.c 2011-04-11 23:29:44.000000000 +0200
+++ linux-2.6.39-rc2/drivers/video/s3fb.c 2011-04-11 23:10:35.000000000 +0200
@@ -25,6 +25,9 @@
#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-algo-bit.h>
+
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
@@ -36,6 +39,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 +114,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 +152,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 +181,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 +1119,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)) {
@@ -1110,12 +1236,69 @@ 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);
+ 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);
+ if (info->var.yres_virtual < info->var.yres) {
+ dev_err(info->device, "virtual vertical size smaller than real\n");
goto err_find_mode;
}

@@ -1164,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);
@@ -1195,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.39-rc2-orig/drivers/video/Kconfig 2011-04-06 03:30:43.000000000 +0200
+++ linux-2.6.39-rc2/drivers/video/Kconfig 2011-04-11 22:41:18.000000000 +0200
@@ -1463,6 +1463,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


--
Ondrej Zary