diff --git a/src/include/edid.h b/src/include/edid.h index 3de4dbc44d..72ae7f323c 100644 --- a/src/include/edid.h +++ b/src/include/edid.h @@ -21,8 +21,13 @@ #define EDID_H enum edid_modes { - EDID_MODE_AUTO = 0, EDID_MODE_640x480_60Hz, + EDID_MODE_720x480_60Hz, + EDID_MODE_1280x720_60Hz, + EDID_MODE_1920x1080_60Hz, + NUM_KNOWN_MODES, + + EDID_MODE_AUTO }; struct edid_mode { @@ -75,6 +80,7 @@ struct edid { unsigned int panel_bits_per_pixel; /* used to compute timing for graphics chips. */ struct edid_mode mode; + u8 mode_is_supported[NUM_KNOWN_MODES]; unsigned int link_clock; /* 3 variables needed for coreboot framebuffer. * In most cases, they are the same as the ha @@ -89,5 +95,6 @@ struct edid { /* Defined in src/lib/edid.c */ int decode_edid(unsigned char *edid, int size, struct edid *out); void set_vbe_mode_info_valid(struct edid *edid, uintptr_t fb_addr); +int set_display_mode(struct edid *edid, enum edid_modes mode); #endif /* EDID_H */ diff --git a/src/lib/edid.c b/src/lib/edid.c index ed3565cb57..be3d90602a 100644 --- a/src/lib/edid.c +++ b/src/lib/edid.c @@ -976,6 +976,52 @@ static void dump_breakdown(unsigned char *edid) printk(BIOS_SPEW, "\n"); } +/* + * Lookup table of some well-known modes that can be useful in case the + * auto-detected mode is unsuitable. + * ha = hdisplay; va = vdisplay; + * hbl = htotal - hdisplay; vbl = vtotal - vdisplay; + * hso = hsync_start - hdsiplay; vso = vsync_start - vdisplay; + * hspw = hsync_end - hsync_start; vspw = vsync_end - vsync_start; + */ +static struct edid_mode known_modes[NUM_KNOWN_MODES] = { + [EDID_MODE_640x480_60Hz] = { + .name = "640x480@60Hz", .pixel_clock = 25175, .refresh = 60, + .ha = 640, .hbl = 160, .hso = 16, .hborder = 96, + .va = 480, .vbl = 45, .vso = 10, .vspw = 2, + .phsync = '-', .pvsync = '-' }, + [EDID_MODE_720x480_60Hz] = { + .name = "720x480@60Hz", .pixel_clock = 27000, .refresh = 60, + .ha = 720, .hbl = 78, .hso = 16, .hborder = 62, + .va = 480, .vbl = 45, .vso = 9, .vspw = 6, + .phsync = '-', .pvsync = '-' }, + [EDID_MODE_1280x720_60Hz] = { + .name = "1280x720@60Hz", .pixel_clock = 74250, .refresh = 60, + .ha = 1280, .hbl = 370, .hso = 110, .hborder = 40, + .va = 720, .vbl = 30, .vso = 5, .vspw = 20, + .phsync = '+', .pvsync = '+' }, + [EDID_MODE_1920x1080_60Hz] = { + .name = "1920x1080@60Hz", .pixel_clock = 148500, .refresh = 60, + .ha = 1920, .hbl = 280, .hso = 88, .hborder = 44, + .va = 1080, .vbl = 45, .vso = 4, .vspw = 5, + .phsync = '+', .pvsync = '+' }, +}; + +int set_display_mode(struct edid *edid, enum edid_modes mode) +{ + if (mode == EDID_MODE_AUTO) + return 0; + + if (edid->mode_is_supported[mode]) { + printk(BIOS_DEBUG, "Forcing mode %s\n", known_modes[mode].name); + edid->mode = known_modes[mode]; + return 0; + } + + printk(BIOS_ERR, "Requested display mode not supported.\n"); + return -1; +} + /* * Given a raw edid bloc, decode it into a form * that other parts of coreboot can use -- mainly @@ -986,7 +1032,7 @@ static void dump_breakdown(unsigned char *edid) */ int decode_edid(unsigned char *edid, int size, struct edid *out) { - int analog, i; + int analog, i, j; struct edid_context c = { .has_valid_cvt = 1, .has_valid_dummy_block = 1, @@ -1210,6 +1256,14 @@ int decode_edid(unsigned char *edid, int size, struct edid *out) printk(BIOS_SPEW, " %dx%d@%dHz\n", established_timings[i].x, established_timings[i].y, established_timings[i].refresh); } + + for (j = 0; j < NUM_KNOWN_MODES; j++) { + if (known_modes[j].ha == established_timings[i].x && + known_modes[j].va == established_timings[i].y && + known_modes[j].refresh == established_timings[i].refresh) + out->mode_is_supported[j] = 1; + } + } printk(BIOS_SPEW, "Standard timings supported:\n"); @@ -1245,6 +1299,11 @@ int decode_edid(unsigned char *edid, int size, struct edid *out) refresh = 60 + (b2 & 0x3f); printk(BIOS_SPEW, " %dx%d@%dHz\n", x, y, refresh); + for (j = 0; j < NUM_KNOWN_MODES; j++) { + if (known_modes[j].ha == x && known_modes[j].va == y && + known_modes[j].refresh == refresh) + out->mode_is_supported[j] = 1; + } } /* detailed timings */