tdfxfb: make use of DDC information about connected monitor
authorKrzysztof Helt <krzysztof.h1@wp.pl>
Tue, 7 Apr 2009 02:01:04 +0000 (19:01 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 7 Apr 2009 15:31:09 +0000 (08:31 -0700)
Read DDC information from a connected monitor and use it to select initial
mode (if the mode is not specified).

Also, use the information to protect against modes outside the monitor
specs.

Signed-off-by: Krzysztof Helt <krzysztof.h1@wp.pl>
Tested-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/video/tdfxfb.c

index bc6f916c53be14a25397aa85a9fa4a2d017f0f3c..89f231dc443f0b191701cae247aca0f29c4785f2 100644 (file)
@@ -487,6 +487,12 @@ static int tdfxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
                return -EINVAL;
        }
 
+       if (info->monspecs.hfmax && info->monspecs.vfmax &&
+           info->monspecs.dclkmax && fb_validate_mode(var, info) < 0) {
+               DPRINTK("mode outside monitor's specs\n");
+               return -EINVAL;
+       }
+
        var->xres = (var->xres + 15) & ~15; /* could sometimes be 8 */
        lpitch = var->xres * ((var->bits_per_pixel + 7) >> 3);
 
@@ -1355,6 +1361,23 @@ static void tdfxfb_delete_i2c_busses(struct tdfx_par *par)
                i2c_del_adapter(&par->chan[1].adapter);
        par->chan[1].par = NULL;
 }
+
+static int tdfxfb_probe_i2c_connector(struct tdfx_par *par,
+                                     struct fb_monspecs *specs)
+{
+       u8 *edid = NULL;
+
+       DPRINTK("Probe DDC Bus\n");
+       if (par->chan[0].par)
+               edid = fb_ddc_read(&par->chan[0].adapter);
+
+       if (edid) {
+               fb_edid_to_monspecs(edid, specs);
+               kfree(edid);
+               return 0;
+       }
+       return 1;
+}
 #endif /* CONFIG_FB_3DFX_I2C */
 
 /**
@@ -1372,6 +1395,8 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
        struct tdfx_par *default_par;
        struct fb_info *info;
        int err, lpitch;
+       struct fb_monspecs *specs;
+       bool found;
 
        err = pci_enable_device(pdev);
        if (err) {
@@ -1474,15 +1499,49 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
        if (hwcursor)
                info->fix.smem_len = (info->fix.smem_len - 1024) &
                                        (PAGE_MASK << 1);
+       specs = &info->monspecs;
+       found = false;
+       info->var.bits_per_pixel = 8;
 #ifdef CONFIG_FB_3DFX_I2C
        tdfxfb_create_i2c_busses(info);
+       err = tdfxfb_probe_i2c_connector(default_par, specs);
+
+       if (!err) {
+               if (specs->modedb == NULL)
+                       DPRINTK("Unable to get Mode Database\n");
+               else {
+                       const struct fb_videomode *m;
+
+                       fb_videomode_to_modelist(specs->modedb,
+                                                specs->modedb_len,
+                                                &info->modelist);
+                       m = fb_find_best_display(specs, &info->modelist);
+                       if (m) {
+                               fb_videomode_to_var(&info->var, m);
+                               /* fill all other info->var's fields */
+                               if (tdfxfb_check_var(&info->var, info) < 0)
+                                       info->var = tdfx_var;
+                               else
+                                       found = true;
+                       }
+               }
+       }
 #endif
-       if (!mode_option)
+       if (!mode_option && !found)
                mode_option = "640x480@60";
 
-       err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
-       if (!err || err == 4)
-               info->var = tdfx_var;
+       if (mode_option) {
+               err = fb_find_mode(&info->var, info, mode_option,
+                                  specs->modedb, specs->modedb_len,
+                                  NULL, info->var.bits_per_pixel);
+               if (!err || err == 4)
+                       info->var = tdfx_var;
+       }
+
+       if (found) {
+               fb_destroy_modedb(specs->modedb);
+               specs->modedb = NULL;
+       }
 
        /* maximize virtual vertical length */
        lpitch = info->var.xres_virtual * ((info->var.bits_per_pixel + 7) >> 3);