From 5dc5f61813a9c3ab7dd0a6982ad044834134db5a Mon Sep 17 00:00:00 2001 From: Florian Tobias Schandinat Date: Thu, 12 Jan 2012 12:52:37 +0000 Subject: 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 --- drivers/video/via/via_aux.c | 20 ++++++++++++++ drivers/video/via/via_aux.h | 6 ++++ drivers/video/via/via_aux_edid.c | 59 +++++++++++++++++++++++++++++++++++++++- drivers/video/via/viafbdev.c | 26 +++++++++++++----- 4 files changed, 103 insertions(+), 8 deletions(-) (limited to 'drivers/video/via') 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 #include +#include 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 +#include #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 -- cgit v1.2.3-59-g8ed1b