/* ----------------------------------------------------------------------- * * * 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 <stdio.h> #include <stdlib.h> #include <unistd.h> #define __USE_GNU #include <string.h> #include <sys/io.h> #include <sys/cpu.h> #include <sys/pci.h> #include <unistd.h> #include <assert.h> #include <stdbool.h> #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) { return pci_readl(0x80000000); } 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: map->b1 = pci_readb(0x8000005a); pci_writeb(0x33, 0x8000005a); 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: map->b1 = pci_readb(0x80000091); map->b2 = pci_readb(0x80000092); pci_writeb(0x33, 0x80000091); pci_writeb(0x33, 0x80000092); 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: pci_writeb(map->b1, 0x8000005a); 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: pci_writeb(map->b1, 0x80000091); pci_writeb(map->b2, 0x80000092); 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; }