From 5d4ade0221c2387345d0a82422866bb8b937cb09 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 22 Jan 2010 12:21:37 -0800 Subject: [PATCH] vesa: allow arbitrary resolutions on some Intel chipsets Add some code from the tool "915resolution" to allow arbitrary resolutions to be set on some Intel chipsets. Signed-off-by: H. Peter Anvin --- com32/lib/Makefile | 1 + com32/lib/sys/vesa/i915resolution.c | 803 ++++++++++++++++++++++++++++++++++++ com32/lib/sys/vesa/initvesa.c | 9 +- com32/lib/sys/vesa/video.h | 2 + 4 files changed, 813 insertions(+), 2 deletions(-) create mode 100644 com32/lib/sys/vesa/i915resolution.c diff --git a/com32/lib/Makefile b/com32/lib/Makefile index ff5887b..bee7e8a 100644 --- a/com32/lib/Makefile +++ b/com32/lib/Makefile @@ -64,6 +64,7 @@ LIBOBJS = \ sys/vesacon_write.o sys/vesaserial_write.o \ sys/vesa/initvesa.o sys/vesa/drawtxt.o sys/vesa/background.o \ sys/vesa/alphatbl.o sys/vesa/screencpy.o sys/vesa/fmtpixel.o \ + sys/vesa/i915resolution.o \ \ pci/cfgtype.o pci/scan.o \ pci/readb.o pci/readw.o pci/readl.o pci/readbios.o \ diff --git a/com32/lib/sys/vesa/i915resolution.c b/com32/lib/sys/vesa/i915resolution.c new file mode 100644 index 0000000..a9b28cc --- /dev/null +++ b/com32/lib/sys/vesa/i915resolution.c @@ -0,0 +1,803 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2010 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * Based on: + * + * 915 resolution by steve tomljenovic + * + * This was tested only on Sony VGN-FS550. Use at your own risk + * + * This code is based on the techniques used in : + * + * - 855patch. Many thanks to Christian Zietz (czietz gmx net) + * for demonstrating how to shadow the VBIOS into system RAM + * and then modify it. + * + * - 1280patch by Andrew Tipton (andrewtipton null li). + * + * - 855resolution by Alain Poirier + * + * This source code is into the public domain. + */ + +#include +#include +#include +#define __USE_GNU +#include +#include +#include +#include +#include +#include +#include "video.h" +#include "debug.h" + +#define VBIOS_START 0xc0000 +#define VBIOS_SIZE 0x10000 + +#define MODE_TABLE_OFFSET_845G 617 + +#define VERSION "0.5.3" + +#define ATI_SIGNATURE1 "ATI MOBILITY RADEON" +#define ATI_SIGNATURE2 "ATI Technologies Inc" +#define NVIDIA_SIGNATURE "NVIDIA Corp" +#define INTEL_SIGNATURE "Intel Corp" + +typedef unsigned char * address; + +typedef enum { + CT_UNKWN, CT_830, CT_845G, CT_855GM, CT_865G, CT_915G, CT_915GM, + CT_945G, CT_945GM, CT_946GZ, CT_G965, CT_Q965, CT_945GME, + CHIPSET_TYPES +} chipset_type; + +typedef enum { + BT_UNKWN, BT_1, BT_2, BT_3 +} bios_type; + +static int freqs[] = { 60, 75, 85 }; + +typedef struct { + uint8_t mode; + uint8_t bits_per_pixel; + uint16_t resolution; + uint8_t unknown; +} __attribute__((packed)) vbios_mode; + +typedef struct { + uint16_t clock; /* Clock frequency in 10 kHz */ + uint8_t x1; + uint8_t x_total; + uint8_t x2; + uint8_t y1; + uint8_t y_total; + uint8_t y2; +} __attribute__((packed)) vbios_resolution_type1; + +typedef struct { + uint32_t clock; + + uint16_t x1; + uint16_t htotal; + uint16_t x2; + uint16_t hblank; + uint16_t hsyncstart; + uint16_t hsyncend; + + uint16_t y1; + uint16_t vtotal; + uint16_t y2; + uint16_t vblank; + uint16_t vsyncstart; + uint16_t vsyncend; +} __attribute__((packed)) vbios_modeline_type2; + +typedef struct { + uint8_t xchars; + uint8_t ychars; + uint8_t unknown[4]; + + vbios_modeline_type2 modelines[]; +} __attribute__((packed)) vbios_resolution_type2; + +typedef struct { + uint32_t clock; + + uint16_t x1; + uint16_t htotal; + uint16_t x2; + uint16_t hblank; + uint16_t hsyncstart; + uint16_t hsyncend; + + uint16_t y1; + uint16_t vtotal; + uint16_t y2; + uint16_t vblank; + uint16_t vsyncstart; + uint16_t vsyncend; + + uint16_t timing_h; + uint16_t timing_v; + + uint8_t unknown[6]; +} __attribute__((packed)) vbios_modeline_type3; + +typedef struct { + unsigned char unknown[6]; + + vbios_modeline_type3 modelines[]; +} __attribute__((packed)) vbios_resolution_type3; + + +typedef struct { + unsigned int chipset_id; + chipset_type chipset; + bios_type bios; + + address bios_ptr; + + vbios_mode * mode_table; + unsigned int mode_table_size; + + uint8_t b1, b2; + + bool unlocked; +} vbios_map; + +#if 0 /* Debugging hacks */ +static void good_marker(int x) +{ + ((uint16_t *)0xb8000)[x] = 0x2f30 - ((x & 0xf0) << 4) + (x & 0x0f); +} + +static void bad_marker(int x) +{ + ((uint16_t *)0xb8000)[x] = 0x4f30 - ((x & 0xf0) << 4) + (x & 0x0f); +} + +static void status(const char *fmt, ...) +{ + va_list ap; + char msg[81], *p; + int i; + uint16_t *q; + + memset(msg, 0, sizeof msg); + va_start(ap, fmt); + vsnprintf(msg, sizeof msg, fmt, ap); + va_end(ap); + p = msg; + q = (uint16_t *)0xb8000 + 80; + for (i = 0; i < 80; i++) + *q++ = *p++ + 0x1f00; +} +#else +static inline void good_marker(int x) { (void)x; } +static inline void bad_marker(int x) { (void)x; } +static inline void status(const char *fmt, ...) { (void)fmt; } +#endif + +static unsigned int get_chipset_id(void) { + outl(0x80000000, 0xcf8); + return inl(0xcfc); +} + +static chipset_type get_chipset(unsigned int id) { + chipset_type type; + + switch (id) { + case 0x35758086: + type = CT_830; + break; + + case 0x25608086: + type = CT_845G; + break; + + case 0x35808086: + type = CT_855GM; + break; + + case 0x25708086: + type = CT_865G; + break; + + case 0x25808086: + type = CT_915G; + break; + + case 0x25908086: + type = CT_915GM; + break; + + case 0x27708086: + type = CT_945G; + break; + + case 0x27a08086: + type = CT_945GM; + break; + + case 0x29708086: + type = CT_946GZ; + break; + + case 0x29a08086: + type = CT_G965; + break; + + case 0x29908086: + type = CT_Q965; + break; + + case 0x27ac8086: + type = CT_945GME; + break; + + default: + type = CT_UNKWN; + break; + } + + return type; +} + + +static vbios_resolution_type1 * map_type1_resolution(vbios_map * map, + uint16_t res) +{ + vbios_resolution_type1 * ptr = ((vbios_resolution_type1*)(map->bios_ptr + res)); + return ptr; +} + +static vbios_resolution_type2 * map_type2_resolution(vbios_map * map, + uint16_t res) +{ + vbios_resolution_type2 * ptr = ((vbios_resolution_type2*)(map->bios_ptr + res)); + return ptr; +} + +static vbios_resolution_type3 * map_type3_resolution(vbios_map * map, + uint16_t res) +{ + vbios_resolution_type3 * ptr = ((vbios_resolution_type3*)(map->bios_ptr + res)); + return ptr; +} + + +static bool detect_bios_type(vbios_map * map, int entry_size) +{ + unsigned int i; + uint16_t r1, r2; + + r1 = r2 = 32000; + + for (i = 0; i < map->mode_table_size; i++) { + if (map->mode_table[i].resolution <= r1) { + r1 = map->mode_table[i].resolution; + } else if (map->mode_table[i].resolution <= r2) { + r2 = map->mode_table[i].resolution; + } + } + + return ((r2-r1-6) % entry_size) == 0; +} + +static inline void close_vbios(vbios_map *map) +{ + (void)map; +} + +static vbios_map * open_vbios(void) +{ + static vbios_map _map; + vbios_map * const map = &_map; + + memset(&_map, 0, sizeof _map); + + /* + * Determine chipset + */ + map->chipset_id = get_chipset_id(); + good_marker(0x10); + map->chipset = get_chipset(map->chipset_id); + good_marker(0x11); + + /* + * Map the video bios to memory + */ + map->bios_ptr = (void *)VBIOS_START; + + /* + * check if we have ATI Radeon + */ + + if (memmem(map->bios_ptr, VBIOS_SIZE, ATI_SIGNATURE1, strlen(ATI_SIGNATURE1)) || + memmem(map->bios_ptr, VBIOS_SIZE, ATI_SIGNATURE2, strlen(ATI_SIGNATURE2)) ) { + debug("ATI chipset detected. 915resolution only works with Intel 800/900 series graphic chipsets.\r\n"); + return NULL; + } + + /* + * check if we have NVIDIA + */ + + if (memmem(map->bios_ptr, VBIOS_SIZE, NVIDIA_SIGNATURE, strlen(NVIDIA_SIGNATURE))) { + debug("NVIDIA chipset detected. 915resolution only works with Intel 800/900 series graphic chipsets.\r\n"); + return NULL; + } + + /* + * check if we have Intel + */ + + if (map->chipset == CT_UNKWN && memmem(map->bios_ptr, VBIOS_SIZE, INTEL_SIGNATURE, strlen(INTEL_SIGNATURE))) { + debug("Intel chipset detected. However, 915resolution was unable to determine the chipset type.\r\n"); + + debug("Chipset Id: %x\r\n", map->chipset_id); + + debug("Please report this problem to stomljen@yahoo.com\r\n"); + + close_vbios(map); + return NULL; + } + + /* + * check for others + */ + + if (map->chipset == CT_UNKWN) { + debug("Unknown chipset type and unrecognized bios.\r\n"); + debug("915resolution only works with Intel 800/900 series graphic chipsets.\r\n"); + + debug("Chipset Id: %x\r\n", map->chipset_id); + close_vbios(map); + return NULL; + } + + /* + * Figure out where the mode table is + */ + good_marker(0x12); + + { + address p = map->bios_ptr + 16; + address limit = map->bios_ptr + VBIOS_SIZE - (3 * sizeof(vbios_mode)); + + while (p < limit && map->mode_table == 0) { + vbios_mode * mode_ptr = (vbios_mode *) p; + + if (((mode_ptr[0].mode & 0xf0) == 0x30) && ((mode_ptr[1].mode & 0xf0) == 0x30) && + ((mode_ptr[2].mode & 0xf0) == 0x30) && ((mode_ptr[3].mode & 0xf0) == 0x30)) { + + map->mode_table = mode_ptr; + } + + p++; + } + + if (map->mode_table == 0) { + debug("Unable to locate the mode table.\r\n"); + close_vbios(map); + return NULL; + } + } + good_marker(0x13); + + /* + * Determine size of mode table + */ + + { + vbios_mode * mode_ptr = map->mode_table; + + while (mode_ptr->mode != 0xff) { + map->mode_table_size++; + mode_ptr++; + } + } + good_marker(0x14); + status("mode_table_size = %d", map->mode_table_size); + + /* + * Figure out what type of bios we have + * order of detection is important + */ + + if (detect_bios_type(map, sizeof(vbios_modeline_type3))) { + map->bios = BT_3; + } + else if (detect_bios_type(map, sizeof(vbios_modeline_type2))) { + map->bios = BT_2; + } + else if (detect_bios_type(map, sizeof(vbios_resolution_type1))) { + map->bios = BT_1; + } + else { + debug("Unable to determine bios type.\r\n"); + debug("Mode Table Offset: $C0000 + $%x\r\n", ((unsigned int)map->mode_table) - ((unsigned int)map->bios_ptr)); + debug("Mode Table Entries: %u\r\n", map->mode_table_size); + bad_marker(0x15); + return NULL; + } + good_marker(0x15); + + return map; +} + +static void unlock_vbios(vbios_map * map) +{ + assert(!map->unlocked); + + map->unlocked = true; + + switch (map->chipset) { + case CT_UNKWN: + case CHIPSET_TYPES: /* Shut up gcc */ + break; + case CT_830: + case CT_855GM: + outl(0x8000005a, 0xcf8); + map->b1 = inb(0xcfe); + + outl(0x8000005a, 0xcf8); + outb(0x33, 0xcfe); + break; + case CT_845G: + case CT_865G: + case CT_915G: + case CT_915GM: + case CT_945G: + case CT_945GM: + case CT_945GME: + case CT_946GZ: + case CT_G965: + case CT_Q965: + outl(0x80000090, 0xcf8); + map->b1 = inb(0xcfd); + map->b2 = inb(0xcfe); + + outl(0x80000090, 0xcf8); + outb(0x33, 0xcfd); + outb(0x33, 0xcfe); + break; + } + +#if DEBUG + { + unsigned int t = inl(0xcfc); + debug("unlock PAM: (0x%08x)\r\n", t); + } +#endif +} + +static void relock_vbios(vbios_map * map) +{ + assert(map->unlocked); + map->unlocked = false; + + switch (map->chipset) { + case CT_UNKWN: + case CHIPSET_TYPES: /* Shut up gcc */ + break; + case CT_830: + case CT_855GM: + outl(0x8000005a, 0xcf8); + outb(map->b1, 0xcfe); + break; + case CT_845G: + case CT_865G: + case CT_915G: + case CT_915GM: + case CT_945G: + case CT_945GM: + case CT_945GME: + case CT_946GZ: + case CT_G965: + case CT_Q965: + outl(0x80000090, 0xcf8); + outb(map->b1, 0xcfd); + outb(map->b2, 0xcfe); + break; + } + +#if DEBUG + { + unsigned int t = inl(0xcfc); + debug("relock PAM: (0x%08x)\r\n", t); + } +#endif +} + +#if 0 +static void list_modes(vbios_map *map, unsigned int raw) +{ + unsigned int i, x, y; + + for (i=0; i < map->mode_table_size; i++) { + switch(map->bios) { + case BT_1: + { + vbios_resolution_type1 * res = map_type1_resolution(map, map->mode_table[i].resolution); + + x = ((((unsigned int) res->x2) & 0xf0) << 4) | res->x1; + y = ((((unsigned int) res->y2) & 0xf0) << 4) | res->y1; + + if (x != 0 && y != 0) { + debug("Mode %02x : %dx%d, %d bits/pixel\r\n", map->mode_table[i].mode, x, y, map->mode_table[i].bits_per_pixel); + } + + if (raw) + { + debug("Mode %02x (raw) :\r\n\t%02x %02x\r\n\t%02x\r\n\t%02x\r\n\t%02x\r\n\t%02x\r\n\t%02x\r\n\t%02x\r\n", map->mode_table[i].mode, res->unknow1[0],res->unknow1[1], res->x1,res->x_total,res->x2,res->y1,res->y_total,res->y2); + } + + } + break; + case BT_2: + { + vbios_resolution_type2 * res = map_type2_resolution(map, map->mode_table[i].resolution); + + x = res->modelines[0].x1+1; + y = res->modelines[0].y1+1; + + if (x != 0 && y != 0) { + debug("Mode %02x : %dx%d, %d bits/pixel\r\n", map->mode_table[i].mode, x, y, map->mode_table[i].bits_per_pixel); + } + } + break; + case BT_3: + { + vbios_resolution_type3 * res = map_type3_resolution(map, map->mode_table[i].resolution); + + x = res->modelines[0].x1+1; + y = res->modelines[0].y1+1; + + if (x != 0 && y != 0) { + debug("Mode %02x : %dx%d, %d bits/pixel\r\n", map->mode_table[i].mode, x, y, map->mode_table[i].bits_per_pixel); + } + } + break; + case BT_UNKWN: + break; + } + } +} +#endif + +static void gtf_timings(int x, int y, int freq, uint32_t *clock, + uint16_t *hsyncstart, uint16_t *hsyncend, uint16_t *hblank, + uint16_t *vsyncstart, uint16_t *vsyncend, uint16_t *vblank) +{ + int hbl, vbl, vfreq; + + vbl = y + (y+1)/(20000.0/(11*freq) - 1) + 1.5; + vfreq = vbl * freq; + hbl = 16 * (int)(x * (30.0 - 300000.0 / vfreq) / + (70.0 + 300000.0 / vfreq) / 16.0 + 0.5); + + *vsyncstart = y; + *vsyncend = y + 3; + *vblank = vbl - 1; + *hsyncstart = x + hbl / 2 - (x + hbl + 50) / 100 * 8 - 1; + *hsyncend = x + hbl / 2 - 1; + *hblank = x + hbl - 1; + *clock = (x + hbl) * vfreq / 1000; +} + +static int set_mode(vbios_map * map, unsigned int mode, + unsigned int x, unsigned int y, unsigned int bp, + unsigned int htotal, unsigned int vtotal) +{ + int xprev, yprev; + unsigned int i, j; + int rv = -1; + + for (i=0; i < map->mode_table_size; i++) { + if (map->mode_table[i].mode == mode) { + switch(map->bios) { + case BT_1: + { + vbios_resolution_type1 * res = map_type1_resolution(map, map->mode_table[i].resolution); + uint32_t clock; + uint16_t hsyncstart, hsyncend, hblank; + uint16_t vsyncstart, vsyncend, vblank; + + if (bp) { + map->mode_table[i].bits_per_pixel = bp; + } + + gtf_timings(x, y, freqs[0], &clock, + &hsyncstart, &hsyncend, &hblank, + &vsyncstart, &vsyncend, &vblank); + + status("x = %d, y = %d, clock = %lu, h = %d %d %d, v = %d %d %d\n", + x, y, clock, + hsyncstart, hsyncend, hblank, + vsyncstart, vsyncend, vblank); + + htotal = htotal ? htotal : (unsigned int)hblank+1; + vtotal = vtotal ? vtotal : (unsigned int)vblank+1; + + res->clock = clock/10; /* Units appear to be 10 kHz */ + res->x2 = (((htotal-x) >> 8) & 0x0f) | ((x >> 4) & 0xf0); + res->x1 = (x & 0xff); + + res->y2 = (((vtotal-y) >> 8) & 0x0f) | ((y >> 4) & 0xf0); + res->y1 = (y & 0xff); + if (htotal) + res->x_total = ((htotal-x) & 0xff); + + if (vtotal) + res->y_total = ((vtotal-y) & 0xff); + + rv = 0; + } + break; + case BT_2: + { + vbios_resolution_type2 * res = map_type2_resolution(map, map->mode_table[i].resolution); + + res->xchars = x / 8; + res->ychars = y / 16 - 1; + xprev = res->modelines[0].x1; + yprev = res->modelines[0].y1; + + for(j=0; j < 3; j++) { + vbios_modeline_type2 * modeline = &res->modelines[j]; + + if (modeline->x1 == xprev && modeline->y1 == yprev) { + modeline->x1 = modeline->x2 = x-1; + modeline->y1 = modeline->y2 = y-1; + + gtf_timings(x, y, freqs[j], &modeline->clock, + &modeline->hsyncstart, &modeline->hsyncend, + &modeline->hblank, &modeline->vsyncstart, + &modeline->vsyncend, &modeline->vblank); + + if (htotal) + modeline->htotal = htotal; + else + modeline->htotal = modeline->hblank; + + if (vtotal) + modeline->vtotal = vtotal; + else + modeline->vtotal = modeline->vblank; + } + } + + rv = 0; + } + break; + case BT_3: + { + vbios_resolution_type3 * res = map_type3_resolution(map, map->mode_table[i].resolution); + + xprev = res->modelines[0].x1; + yprev = res->modelines[0].y1; + + for (j=0; j < 3; j++) { + vbios_modeline_type3 * modeline = &res->modelines[j]; + + if (modeline->x1 == xprev && modeline->y1 == yprev) { + modeline->x1 = modeline->x2 = x-1; + modeline->y1 = modeline->y2 = y-1; + + gtf_timings(x, y, freqs[j], &modeline->clock, + &modeline->hsyncstart, &modeline->hsyncend, + &modeline->hblank, &modeline->vsyncstart, + &modeline->vsyncend, &modeline->vblank); + if (htotal) + modeline->htotal = htotal; + else + modeline->htotal = modeline->hblank; + if (vtotal) + modeline->vtotal = vtotal; + else + modeline->vtotal = modeline->vblank; + + modeline->timing_h = y-1; + modeline->timing_v = x-1; + } + } + + rv = 0; + } + break; + case BT_UNKWN: + break; + } + } + } + + return rv; +} + +static inline void display_map_info(vbios_map * map) { +#ifdef DEBUG + static const char * bios_type_names[] = + {"UNKNOWN", "TYPE 1", "TYPE 2", "TYPE 3"}; + static const char * chipset_type_names[] = { + "UNKNOWN", "830", "845G", "855GM", "865G", "915G", "915GM", "945G", + "945GM", "946GZ", "G965", "Q965", "945GME" + }; + + debug("Chipset: %s\r\n", chipset_type_names[map->chipset]); + debug("BIOS: %s\r\n", bios_type_names[map->bios]); + + debug("Mode Table Offset: $C0000 + $%x\r\n", + ((unsigned int)map->mode_table) - ((unsigned int)map->bios_ptr)); + debug("Mode Table Entries: %u\r\n", map->mode_table_size); +#endif + (void)map; +} + +int __vesacon_i915resolution(int x, int y) +{ + vbios_map * map; + unsigned int mode = 0x52; /* 800x600x32 mode in known BIOSes */ + unsigned int bp = 32; /* 32 bits per pixel */ + int rv = 0; + + good_marker(0); + + map = open_vbios(); + if (!map) + return -1; + + good_marker(1); + + display_map_info(map); + + debug("\r\n"); + + if (mode && x && y) { + good_marker(2); + cli(); + good_marker(3); + unlock_vbios(map); + good_marker(4); + rv = set_mode(map, mode, x, y, bp, 0, 0); + if (rv) + bad_marker(5); + else + good_marker(5); + relock_vbios(map); + good_marker(6); + sti(); + + debug("Patch mode %02x to resolution %dx%d complete\r\n", mode, x, y); + } + close_vbios(map); + + return rv; +} diff --git a/com32/lib/sys/vesa/initvesa.c b/com32/lib/sys/vesa/initvesa.c index f1224a1..0a436f4 100644 --- a/com32/lib/sys/vesa/initvesa.c +++ b/com32/lib/sys/vesa/initvesa.c @@ -318,8 +318,13 @@ int __vesacon_init(int x, int y) return 10; rv = vesacon_set_mode(x, y); - if (rv) - return rv; + if (rv) { + /* Try to see if we can just patch the BIOS... */ + if (__vesacon_i915resolution(x, y)) + return rv; + if (vesacon_set_mode(x, y)) + return rv; + } init_text_display(); diff --git a/com32/lib/sys/vesa/video.h b/com32/lib/sys/vesa/video.h index 764228a..d14494b 100644 --- a/com32/lib/sys/vesa/video.h +++ b/com32/lib/sys/vesa/video.h @@ -92,4 +92,6 @@ void __vesacon_set_cursor(int, int, bool); void __vesacon_copy_to_screen(size_t, const uint32_t *, size_t); void __vesacon_init_copy_to_screen(void); +int __vesacon_i915resolution(int x, int y); + #endif /* LIB_SYS_VESA_VIDEO_H */ -- 2.7.4