Skip to content

Commit

Permalink
viafb: add initial EDID support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Florian Tobias Schandinat authored and Florian Tobias Schandinat committed Feb 13, 2012
1 parent 2c4c8a8 commit 5dc5f61
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 8 deletions.
20 changes: 20 additions & 0 deletions drivers/video/via/via_aux.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
6 changes: 6 additions & 0 deletions drivers/video/via/via_aux.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include <linux/list.h>
#include <linux/i2c.h>
#include <linux/fb.h>


struct via_aux_bus {
Expand All @@ -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)
Expand Down
59 changes: 58 additions & 1 deletion drivers/video/via/via_aux_edid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
26 changes: 19 additions & 7 deletions drivers/video/via/viafbdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 5dc5f61

Please sign in to comment.