viafb: add initial EDID support
authorFlorian Tobias Schandinat <FlorianSchandinat@gmx.de>
Thu, 12 Jan 2012 12:52:37 +0000 (12:52 +0000)
committerFlorian Tobias Schandinat <FlorianSchandinat@gmx.de>
Mon, 13 Feb 2012 07:28:14 +0000 (07:28 +0000)
This patch adds support for using EDID data on CRT and DVP1 for
initial configuration if viafb_mode or viafb_mode1 are not present.

Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
drivers/video/via/via_aux.c
drivers/video/via/via_aux.h
drivers/video/via/via_aux_edid.c
drivers/video/via/viafbdev.c

index e728b9b..4a0a55c 100644 (file)
@@ -60,9 +60,29 @@ void via_aux_free(struct via_aux_bus *bus)
                return;
 
        list_for_each_entry_safe(pos, n, &bus->drivers, chain) {
+               if (pos->cleanup)
+                       pos->cleanup(pos);
+
                list_del(&pos->chain);
+               kfree(pos->data);
                kfree(pos);
        }
 
        kfree(bus);
 }
+
+const struct fb_videomode *via_aux_get_preferred_mode(struct via_aux_bus *bus)
+{
+       struct via_aux_drv *pos;
+       const struct fb_videomode *mode = NULL;
+
+       if (!bus)
+               return NULL;
+
+       list_for_each_entry(pos, &bus->drivers, chain) {
+               if (pos->get_preferred_mode)
+                       mode = pos->get_preferred_mode(pos);
+       }
+
+       return mode;
+}
index 5a4867a..a8de3f0 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <linux/list.h>
 #include <linux/i2c.h>
+#include <linux/fb.h>
 
 
 struct via_aux_bus {
@@ -42,11 +43,16 @@ struct via_aux_drv {
 
        const char *name;       /* human readable name of the driver */
        void *data;             /* private data of this driver */
+
+       void (*cleanup)(struct via_aux_drv *drv);
+       const struct fb_videomode* (*get_preferred_mode)
+               (struct via_aux_drv *drv);
 };
 
 
 struct via_aux_bus *via_aux_probe(struct i2c_adapter *adap);
 void via_aux_free(struct via_aux_bus *bus);
+const struct fb_videomode *via_aux_get_preferred_mode(struct via_aux_bus *bus);
 
 
 static inline bool via_aux_add(struct via_aux_drv *drv)
index 547bff5..03f7a41 100644 (file)
  */
 
 #include <linux/slab.h>
+#include <linux/fb.h>
 #include "via_aux.h"
+#include "../edid.h"
 
 
 static const char *name = "EDID";
 
 
+static void query_edid(struct via_aux_drv *drv)
+{
+       struct fb_monspecs *spec = drv->data;
+       unsigned char edid[EDID_LENGTH];
+       bool valid = false;
+
+       if (spec)
+               fb_destroy_modedb(spec->modedb);
+       else
+               spec = kmalloc(sizeof(*spec), GFP_KERNEL);
+
+       spec->version = spec->revision = 0;
+       if (via_aux_read(drv, 0x00, edid, EDID_LENGTH)) {
+               fb_edid_to_monspecs(edid, spec);
+               valid = spec->version || spec->revision;
+       }
+
+       if (!valid) {
+               kfree(spec);
+               spec = NULL;
+       } else
+               printk(KERN_DEBUG "EDID: %s %s\n", spec->manufacturer, spec->monitor);
+
+       drv->data = spec;
+}
+
+static const struct fb_videomode *get_preferred_mode(struct via_aux_drv *drv)
+{
+       struct fb_monspecs *spec = drv->data;
+       int i;
+
+       if (!spec || !spec->modedb || !(spec->misc & FB_MISC_1ST_DETAIL))
+               return NULL;
+
+       for (i = 0; i < spec->modedb_len; i++) {
+               if (spec->modedb[i].flag & FB_MODE_IS_FIRST &&
+                       spec->modedb[i].flag & FB_MODE_IS_DETAILED)
+                       return &spec->modedb[i];
+       }
+
+       return NULL;
+}
+
+static void cleanup(struct via_aux_drv *drv)
+{
+       struct fb_monspecs *spec = drv->data;
+
+       if (spec)
+               fb_destroy_modedb(spec->modedb);
+}
+
 void via_aux_edid_probe(struct via_aux_bus *bus)
 {
        struct via_aux_drv drv = {
                .bus    =       bus,
                .addr   =       0x50,
-               .name   =       name};
+               .name   =       name,
+               .cleanup        =       cleanup,
+               .get_preferred_mode     =       get_preferred_mode};
+
+       query_edid(&drv);
 
        /* as EDID devices can be connected/disconnected just add the driver */
        via_aux_add(&drv);
index 6d5b649..4791165 100644 (file)
@@ -1671,12 +1671,23 @@ static void viafb_remove_proc(struct viafb_shared *shared)
 }
 #undef IS_VT1636
 
-static int parse_mode(const char *str, u32 *xres, u32 *yres)
+static int parse_mode(const char *str, u32 devices, u32 *xres, u32 *yres)
 {
+       const struct fb_videomode *mode = NULL;
        char *ptr;
 
        if (!str) {
-               if (machine_is_olpc()) {
+               if (devices == VIA_CRT)
+                       mode = via_aux_get_preferred_mode(
+                               viaparinfo->shared->i2c_26);
+               else if (devices == VIA_DVP1)
+                       mode = via_aux_get_preferred_mode(
+                               viaparinfo->shared->i2c_31);
+
+               if (mode) {
+                       *xres = mode->xres;
+                       *yres = mode->yres;
+               } else if (machine_is_olpc()) {
                        *xres = 1200;
                        *yres = 900;
                } else {
@@ -1829,10 +1840,11 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
                        viafb_second_size * 1024 * 1024;
        }
 
-       parse_mode(viafb_mode, &default_xres, &default_yres);
+       parse_mode(viafb_mode, viaparinfo->shared->iga1_devices,
+               &default_xres, &default_yres);
        if (viafb_SAMM_ON == 1)
-               parse_mode(viafb_mode1, &viafb_second_xres,
-                       &viafb_second_yres);
+               parse_mode(viafb_mode1, viaparinfo->shared->iga2_devices,
+                       &viafb_second_xres, &viafb_second_yres);
 
        default_var.xres = default_xres;
        default_var.yres = default_yres;
@@ -2060,9 +2072,9 @@ int __init viafb_init(void)
        if (r < 0)
                return r;
 #endif
-       if (parse_mode(viafb_mode, &dummy_x, &dummy_y)
+       if (parse_mode(viafb_mode, 0, &dummy_x, &dummy_y)
                || !viafb_get_best_mode(dummy_x, dummy_y, viafb_refresh)
-               || parse_mode(viafb_mode1, &dummy_x, &dummy_y)
+               || parse_mode(viafb_mode1, 0, &dummy_x, &dummy_y)
                || !viafb_get_best_mode(dummy_x, dummy_y, viafb_refresh1)
                || viafb_bpp < 0 || viafb_bpp > 32
                || viafb_bpp1 < 0 || viafb_bpp1 > 32