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>
This commit is contained in:
parent
2c4c8a8a73
commit
5dc5f61813
|
@ -60,9 +60,29 @@ void via_aux_free(struct via_aux_bus *bus)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
list_for_each_entry_safe(pos, n, &bus->drivers, chain) {
|
list_for_each_entry_safe(pos, n, &bus->drivers, chain) {
|
||||||
|
if (pos->cleanup)
|
||||||
|
pos->cleanup(pos);
|
||||||
|
|
||||||
list_del(&pos->chain);
|
list_del(&pos->chain);
|
||||||
|
kfree(pos->data);
|
||||||
kfree(pos);
|
kfree(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(bus);
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/fb.h>
|
||||||
|
|
||||||
|
|
||||||
struct via_aux_bus {
|
struct via_aux_bus {
|
||||||
|
@ -42,11 +43,16 @@ struct via_aux_drv {
|
||||||
|
|
||||||
const char *name; /* human readable name of the driver */
|
const char *name; /* human readable name of the driver */
|
||||||
void *data; /* private data of this 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);
|
struct via_aux_bus *via_aux_probe(struct i2c_adapter *adap);
|
||||||
void via_aux_free(struct via_aux_bus *bus);
|
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)
|
static inline bool via_aux_add(struct via_aux_drv *drv)
|
||||||
|
|
|
@ -22,18 +22,75 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/fb.h>
|
||||||
#include "via_aux.h"
|
#include "via_aux.h"
|
||||||
|
#include "../edid.h"
|
||||||
|
|
||||||
|
|
||||||
static const char *name = "EDID";
|
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)
|
void via_aux_edid_probe(struct via_aux_bus *bus)
|
||||||
{
|
{
|
||||||
struct via_aux_drv drv = {
|
struct via_aux_drv drv = {
|
||||||
.bus = bus,
|
.bus = bus,
|
||||||
.addr = 0x50,
|
.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 */
|
/* as EDID devices can be connected/disconnected just add the driver */
|
||||||
via_aux_add(&drv);
|
via_aux_add(&drv);
|
||||||
|
|
|
@ -1671,12 +1671,23 @@ static void viafb_remove_proc(struct viafb_shared *shared)
|
||||||
}
|
}
|
||||||
#undef IS_VT1636
|
#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;
|
char *ptr;
|
||||||
|
|
||||||
if (!str) {
|
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;
|
*xres = 1200;
|
||||||
*yres = 900;
|
*yres = 900;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1829,10 +1840,11 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
|
||||||
viafb_second_size * 1024 * 1024;
|
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)
|
if (viafb_SAMM_ON == 1)
|
||||||
parse_mode(viafb_mode1, &viafb_second_xres,
|
parse_mode(viafb_mode1, viaparinfo->shared->iga2_devices,
|
||||||
&viafb_second_yres);
|
&viafb_second_xres, &viafb_second_yres);
|
||||||
|
|
||||||
default_var.xres = default_xres;
|
default_var.xres = default_xres;
|
||||||
default_var.yres = default_yres;
|
default_var.yres = default_yres;
|
||||||
|
@ -2060,9 +2072,9 @@ int __init viafb_init(void)
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
#endif
|
#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)
|
|| !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_get_best_mode(dummy_x, dummy_y, viafb_refresh1)
|
||||||
|| viafb_bpp < 0 || viafb_bpp > 32
|
|| viafb_bpp < 0 || viafb_bpp > 32
|
||||||
|| viafb_bpp1 < 0 || viafb_bpp1 > 32
|
|| viafb_bpp1 < 0 || viafb_bpp1 > 32
|
||||||
|
|
Loading…
Reference in New Issue