diff options
author | Florian Tobias Schandinat <FlorianSchandinat@gmx.de> | 2012-01-12 12:52:37 +0000 |
---|---|---|
committer | Florian Tobias Schandinat <FlorianSchandinat@gmx.de> | 2012-02-13 07:28:14 +0000 |
commit | 5dc5f61813a9c3ab7dd0a6982ad044834134db5a (patch) | |
tree | 4a50b6713eed9d2d01d1e4cdbc8ecd1a4393e035 /drivers/video/via | |
parent | 2c4c8a8a73b64a8ea86ad85d8a59a5914d2f81ea (diff) |
viafb: add initial EDID support
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>
Diffstat (limited to 'drivers/video/via')
-rw-r--r-- | drivers/video/via/via_aux.c | 20 | ||||
-rw-r--r-- | drivers/video/via/via_aux.h | 6 | ||||
-rw-r--r-- | drivers/video/via/via_aux_edid.c | 59 | ||||
-rw-r--r-- | drivers/video/via/viafbdev.c | 26 |
4 files changed, 103 insertions, 8 deletions
diff --git a/drivers/video/via/via_aux.c b/drivers/video/via/via_aux.c index e728b9bec235..4a0a55cdac3d 100644 --- a/drivers/video/via/via_aux.c +++ b/drivers/video/via/via_aux.c @@ -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; +} diff --git a/drivers/video/via/via_aux.h b/drivers/video/via/via_aux.h index 5a4867a2dcc3..a8de3f038cea 100644 --- a/drivers/video/via/via_aux.h +++ b/drivers/video/via/via_aux.h @@ -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) diff --git a/drivers/video/via/via_aux_edid.c b/drivers/video/via/via_aux_edid.c index 547bff53a448..03f7a41c8a3f 100644 --- a/drivers/video/via/via_aux_edid.c +++ b/drivers/video/via/via_aux_edid.c @@ -22,18 +22,75 @@ */ #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); diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 6d5b64923236..47911658f684 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -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 |