diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-06-16 23:00:36 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-06-16 23:00:36 +0000 |
commit | 14dd2ec093cfabda3ae7efeeaf0e23c66ebaccc0 (patch) | |
tree | 9a9ce5cff6ef92faa6e07a82785b9a6d6838f7e4 /drivers/video | |
parent | 847290510f811c572cc2aa80c1f02a04721410b1 (diff) |
Merge with 2.4.0-test1.
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/Config.in | 14 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/fbmem.c | 5 | ||||
-rw-r--r-- | drivers/video/sisfb.c | 2982 |
4 files changed, 2996 insertions, 6 deletions
diff --git a/drivers/video/Config.in b/drivers/video/Config.in index c0482cd40..edc971196 100644 --- a/drivers/video/Config.in +++ b/drivers/video/Config.in @@ -220,7 +220,8 @@ if [ "$CONFIG_FB" = "y" ]; then "$CONFIG_FB_P9100" = "y" -o "$CONFIG_FB_ATY128" = "y" -o \ "$CONFIG_FB_RIVA" = "y" -o \ "$CONFIG_FB_SGIVW" = "y" -o "$CONFIG_FB_CYBER2000" = "y" -o \ - "$CONFIG_FB_SA1100" = "y" -o "$CONFIG_FB_3DFX" = "y" ]; then + "$CONFIG_FB_SA1100" = "y" -o "$CONFIG_FB_3DFX" = "y" -o \ + "$CONFIG_FB_SIS" = "y" ]; then define_tristate CONFIG_FBCON_CFB8 y else if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \ @@ -236,7 +237,7 @@ if [ "$CONFIG_FB" = "y" ]; then "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ "$CONFIG_FB_P9100" = "m" -o "$CONFIG_FB_ATY128" = "m" -o \ "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_CYBER2000" = "m" -o \ - "$CONFIG_FB_SA1100" = "m" ]; then + "$CONFIG_FB_SA1100" = "m" -o "$CONFIG_FB_SIS" = "m" ]; then define_tristate CONFIG_FBCON_CFB8 m fi fi @@ -250,7 +251,8 @@ if [ "$CONFIG_FB" = "y" ]; then "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \ "$CONFIG_FB_PM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" -o \ "$CONFIG_FB_RIVA" = "y" -o "$CONFIG_FB_ATY128" = "y" -o \ - "$CONFIG_FB_CYBER2000" = "y" -o "$CONFIG_FB_3DFX" = "y" ]; then + "$CONFIG_FB_CYBER2000" = "y" -o "$CONFIG_FB_3DFX" = "y" -o \ + "$CONFIG_FB_SIS" = "y" ]; then define_tristate CONFIG_FBCON_CFB16 y else if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ @@ -262,7 +264,7 @@ if [ "$CONFIG_FB" = "y" ]; then "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ "$CONFIG_FB_PM2" = "m" -o "$CONFIG_FB_SGIVW" = "m" -o \ - "$CONFIG_FB_CYBER2000" = "m" ]; then + "$CONFIG_FB_CYBER2000" = "m" -o "$CONFIG_FB_SIS" = "m" ]; then define_tristate CONFIG_FBCON_CFB16 m fi fi @@ -288,7 +290,7 @@ if [ "$CONFIG_FB" = "y" ]; then "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ "$CONFIG_FB_RIVA" = "y" -o "$CONFIG_FB_ATY128" = "y" -o \ "$CONFIG_FB_FM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" -o \ - "$CONFIG_FB_3DFX" = "y" ]; then + "$CONFIG_FB_3DFX" = "y" -o "$CONFIG_FB_SIS" = "y" ]; then define_tristate CONFIG_FBCON_CFB32 y else if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ @@ -297,7 +299,7 @@ if [ "$CONFIG_FB" = "y" ]; then "$CONFIG_FB_TGA" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ "$CONFIG_FB_ATY128" = "m" -o \ - "$CONFIG_FB_SGIVW" = "m" ]; then + "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_SIS" = "m" ]; then define_tristate CONFIG_FBCON_CFB32 m fi fi diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 244ff5872..4908aebf2 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_FB_TCX) += tcxfb.o sbusfb.o obj-$(CONFIG_FB_CGFOURTEEN) += cgfourteenfb.o sbusfb.o obj-$(CONFIG_FB_P9100) += p9100fb.o sbusfb.o obj-$(CONFIG_FB_LEO) += leofb.o sbusfb.o +obj-$(CONFIG_FB_SIS) += sisfb.o ifeq ($(CONFIG_FB_MATROX),y) SUB_DIRS += matrox diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 034ff8c1f..554d91155 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -111,6 +111,8 @@ extern int rivafb_init(void); extern int rivafb_setup(char*); extern int tdfxfb_init(void); extern int tdfxfb_setup(char*); +extern int sisfb_init(void); +extern int sisfb_setup(char*); static struct { const char *name; @@ -231,6 +233,9 @@ static struct { /* Not a real frame buffer device... */ { "resolver", NULL, resolver_video_setup }, #endif +#ifdef CONFIG_FB_SIS + { "sisfb", sisfb_init, sisfb_setup }, +#endif #ifdef CONFIG_FB_VIRTUAL /* Must be last to avoid that vfb becomes your primary display */ { "vfb", vfb_init, vfb_setup }, diff --git a/drivers/video/sisfb.c b/drivers/video/sisfb.c new file mode 100644 index 000000000..bace20cd4 --- /dev/null +++ b/drivers/video/sisfb.c @@ -0,0 +1,2982 @@ +/* + * SiS 300/630/540 frame buffer device For Kernal 2.3.x + * + * This driver is partly based on the VBE 2.0 compliant graphic + * boards framebuffer driver, which is + * + * (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de> + * + */ + +#define EXPORT_SYMTAB +#undef SISFBDEBUG + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/console.h> +#include <linux/selection.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/vt_kern.h> +#include <linux/capability.h> +#include <linux/sisfb.h> + +#include <asm/io.h> +#include <asm/mtrr.h> + +#include <video/fbcon.h> +#include <video/fbcon-cfb8.h> +#include <video/fbcon-cfb16.h> +#include <video/fbcon-cfb24.h> +#include <video/fbcon-cfb32.h> + +/* ------------------- Constant Definitions ------------------------- */ + +#define FALSE 0 +#define TRUE 1 + +/* Draw Function */ +#define FBIOGET_GLYPH 0x4620 +#define FBIOGET_HWCINFO 0x4621 +#define BR(x) (0x8200 | (x) << 2) + +#define BITBLT 0x00000000 +#define COLOREXP 0x00000001 +#define ENCOLOREXP 0x00000002 +#define MULTIPLE_SCANLINE 0x00000003 +#define LINE 0x00000004 +#define TRAPAZOID_FILL 0x00000005 +#define TRANSPARENT_BITBLT 0x00000006 + +#define SRCVIDEO 0x00000000 +#define SRCSYSTEM 0x00000010 +#define SRCAGP 0x00000020 + +#define PATFG 0x00000000 +#define PATPATREG 0x00000040 +#define PATMONO 0x00000080 + +#define X_INC 0x00010000 +#define X_DEC 0x00000000 +#define Y_INC 0x00020000 +#define Y_DEC 0x00000000 + +#define NOCLIP 0x00000000 +#define NOMERGECLIP 0x04000000 +#define CLIPENABLE 0x00040000 +#define CLIPWITHOUTMERGE 0x04040000 + +#define OPAQUE 0x00000000 +#define TRANSPARENT 0x00100000 + +#define DSTAGP 0x02000000 +#define DSTVIDEO 0x02000000 + +#define LINE_STYLE 0x00800000 +#define NO_RESET_COUNTER 0x00400000 +#define NO_LAST_PIXEL 0x00200000 + +/* capabilities */ +#define TURBO_QUEUE_CAP 0x80 +#define HW_CURSOR_CAP 0x40 + +/* VGA register Offsets */ +#define SEQ_ADR (0x14) +#define SEQ_DATA (0x15) +#define DAC_ADR (0x18) +#define DAC_DATA (0x19) +#define CRTC_ADR (0x24) +#define CRTC_DATA (0x25) + +/* SiS indexed register indexes */ +#define IND_SIS_PASSWORD (0x05) +#define IND_SIS_DRAM_SIZE (0x14) +#define IND_SIS_MODULE_ENABLE (0x1E) +#define IND_SIS_PCI_ADDRESS_SET (0x20) +#define IND_SIS_TURBOQUEUE_ADR (0x26) +#define IND_SIS_TURBOQUEUE_SET (0x27) + +/* Sis register value */ +#define SIS_PASSWORD (0x86) + +#define SIS_2D_ENABLE (0x40) + +#define SIS_MEM_MAP_IO_ENABLE (0x01) +#define SIS_PCI_ADDR_ENABLE (0x80) + +#define MMIO_SIZE 0x20000 /* 128K MMIO capability */ +#define MAX_ROM_SCAN 0x10000 + +#define RESERVED_MEM_SIZE 0x400000 /* 4M */ + +/* Mode set stuff */ +#define DEFAULT_MODE 0 + +#define ModeInfoFlag 0x07 +#define MemoryInfoFlag 0x1E0 +#define MemorySizeShift 0x05 +#define ModeVGA 0x03 +#define ModeEGA 0x02 +#define CRT1Len 17 +#define DoubleScanMode 0x8000 +#define HalfDCLK 0x1000 + +#define InterlaceMode 0x80 +#define LineCompareOff 0x400 +#define DACInfoFlag 0x18 + +#define VCLKStartFreq 25 + +#define SIS_Glamour 0x0300 +#define SIS_Trojan 0x6300 +#define SIS_Spartan 0x5300 + +/* heap stuff */ +#define OH_ALLOC_SIZE 4000 +#define SENTINEL 0x7fffffff + +#define TURBO_QUEUE_AREA_SIZE 0x80000 /* 512K */ +#define HW_CURSOR_AREA_SIZE 0x1000 /* 4K */ + +/* video connection status */ +#define VB_COMPOSITE 0x01 +#define VB_SVIDEO 0x02 +#define VB_SCART 0x04 +#define VB_LCD 0x08 +#define VB_CRT2 0x10 +#define CRT1 0x20 +#define VB_HDTV 0x40 + +/* ------------------- Global Variables ----------------------------- */ + +struct video_info ivideo; + +struct GlyInfo { + unsigned char ch; + int fontwidth; + int fontheight; + u8 gmask[72]; + int ngmask; +}; + +/* Supported SiS Chips list */ +static struct board { + u16 vendor, device; + const char *name; +} dev_list[] = { + { + PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS_300, "SIS 300"}, { + PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS_540, "SIS 540"}, { + PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS_630, "SIS 630"}, { + 0, 0, NULL} +}; + +/* card parameters */ +unsigned long rom_base; +unsigned long rom_vbase; + +/* mode */ +int video_type = FB_TYPE_PACKED_PIXELS; +int video_linelength; +int video_cmap_len; +int sisfb_off = 0; + +static struct fb_var_screeninfo default_var = { + 0, 0, 0, 0, 0, 0, 0, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, FB_ACTIVATE_NOW, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, + FB_VMODE_NONINTERLACED, + {0, 0, 0, 0, 0, 0} +}; + +static struct display disp; +static struct fb_info fb_info; +static struct { + u16 blue, green, red, pad; +} palette[256]; +static union { +#ifdef FBCON_HAS_CFB16 + u16 cfb16[16]; +#endif +#ifdef FBCON_HAS_CFB24 + u32 cfb24[16]; +#endif +#ifdef FBCON_HAS_CFB32 + u32 cfb32[16]; +#endif +} fbcon_cmap; + +static int inverse = 0; +static int currcon = 0; + +static struct display_switch sisfb_sw; + +u8 caps = 0; + +/* ModeSet stuff */ + +u16 P3c4, P3d4, P3c0, P3ce, P3c2, P3ca, P3c6, P3c7, P3c8, P3c9, P3da; +u16 CRT1VCLKLen; +u16 flag_clearbuffer; +u16 CRT1VCLKLen; +int ModeIDOffset, StandTable, CRT1Table, ScreenOffset, MCLKData, ECLKData; +int REFIndex, ModeType; +int VCLKData; +int RAMType; + +int mode_idx = -1; +u8 mode_no = 0; +u8 rate_idx = 0; + +static const struct _sisbios_mode { + char name[15]; + u8 mode_no; + u16 xres; + u16 yres; + u16 bpp; + u16 rate_idx; + u16 cols; + u16 rows; +} sisbios_mode[] = { + { + "640x480x8", 0x2E, 640, 480, 8, 1, 80, 30}, { + "640x480x16", 0x44, 640, 480, 16, 1, 80, 30}, { + "640x480x32", 0x62, 640, 480, 32, 1, 80, 30}, { + "800x600x8", 0x30, 800, 600, 8, 2, 100, 37}, { + "800x600x16", 0x47, 800, 600, 16, 2, 100, 37}, { + "800x600x32", 0x63, 800, 600, 32, 2, 100, 37}, { + "1024x768x8", 0x38, 1024, 768, 8, 2, 128, 48}, { + "1024x768x16", 0x4A, 1024, 768, 16, 2, 128, 48}, { + "1024x768x32", 0x64, 1024, 768, 32, 2, 128, 48}, { + "1280x1024x8", 0x3A, 1280, 1024, 8, 2, 160, 64}, { + "1280x1024x16", 0x4D, 1280, 1024, 16, 2, 160, 64}, { + "1280x1024x32", 0x65, 1280, 1024, 32, 2, 160, 64}, { + "1600x1200x8", 0x3C, 1600, 1200, 8, 1, 200, 75}, { + "1600x1200x16", 0x3D, 1600, 1200, 16, 1, 200, 75}, { + "1600x1200x32", 0x66, 1600, 1200, 32, 1, 200, 75}, { + "1920x1440x8", 0x68, 1920, 1440, 8, 1, 240, 75}, { + "1920x1440x16", 0x69, 1920, 1440, 16, 1, 240, 75}, { + "1920x1440x32", 0x6B, 1920, 1440, 32, 1, 240, 75}, { + "\0", 0x00, 0, 0, 0, 0, 0, 0} +}; + +static struct _vrate { + u16 idx; + u16 xres; + u16 yres; + u16 refresh; +} vrate[] = { + { + 1, 640, 480, 60}, { + 2, 640, 480, 72}, { + 3, 640, 480, 75}, { + 4, 640, 480, 85}, { + 5, 640, 480, 100}, { + 6, 640, 480, 120}, { + 7, 640, 480, 160}, { + 8, 640, 480, 200}, { + 1, 800, 600, 56}, { + 2, 800, 600, 60}, { + 3, 800, 600, 72}, { + 4, 800, 600, 75}, { + 5, 800, 600, 85}, { + 6, 800, 600, 100}, { + 7, 800, 600, 120}, { + 8, 800, 600, 160}, { + 1, 1024, 768, 56}, { + 2, 1024, 768, 60}, { + 3, 1024, 768, 72}, { + 4, 1024, 768, 75}, { + 5, 1024, 768, 85}, { + 6, 1024, 768, 100}, { + 7, 1024, 768, 120}, { + 1, 1280, 1024, 43}, { + 2, 1280, 1024, 60}, { + 3, 1280, 1024, 75}, { + 4, 1280, 1024, 85}, { + 1, 1600, 1200, 60}, { + 2, 1600, 1200, 65}, { + 3, 1600, 1200, 70}, { + 4, 1600, 1200, 75}, { + 5, 1600, 1200, 85}, { + 1, 1920, 1440, 60}, { + 0, 0, 0, 0} +}; + + +u16 DRAMType[17][5] = + { {0x0C, 0x0A, 0x02, 0x40, 0x39}, {0x0D, 0x0A, 0x01, 0x40, 0x48}, +{0x0C, 0x09, 0x02, 0x20, 0x35}, {0x0D, 0x09, 0x01, 0x20, 0x44}, +{0x0C, 0x08, 0x02, 0x10, 0x31}, {0x0D, 0x08, 0x01, 0x10, 0x40}, +{0x0C, 0x0A, 0x01, 0x20, 0x34}, {0x0C, 0x09, 0x01, 0x08, 0x32}, +{0x0B, 0x08, 0x02, 0x08, 0x21}, {0x0C, 0x08, 0x01, 0x08, 0x30}, +{0x0A, 0x08, 0x02, 0x04, 0x11}, {0x0B, 0x0A, 0x01, 0x10, 0x28}, +{0x09, 0x08, 0x02, 0x02, 0x01}, {0x0B, 0x09, 0x01, 0x08, 0x24}, +{0x0B, 0x08, 0x01, 0x04, 0x20}, {0x0A, 0x08, 0x01, 0x02, 0x10}, +{0x09, 0x08, 0x01, 0x01, 0x00} +}; + +u16 MDA_DAC[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F +}; + +u16 CGA_DAC[] = { 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15, + 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15, + 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F, + 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F, + 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15, + 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15, + 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F, + 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F +}; + +u16 EGA_DAC[] = { 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x05, 0x15, + 0x20, 0x30, 0x24, 0x34, 0x21, 0x31, 0x25, 0x35, + 0x08, 0x18, 0x0C, 0x1C, 0x09, 0x19, 0x0D, 0x1D, + 0x28, 0x38, 0x2C, 0x3C, 0x29, 0x39, 0x2D, 0x3D, + 0x02, 0x12, 0x06, 0x16, 0x03, 0x13, 0x07, 0x17, + 0x22, 0x32, 0x26, 0x36, 0x23, 0x33, 0x27, 0x37, + 0x0A, 0x1A, 0x0E, 0x1E, 0x0B, 0x1B, 0x0F, 0x1F, + 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F +}; + +u16 VGA_DAC[] = { 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15, + 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F, + 0x00, 0x05, 0x08, 0x0B, 0x0E, 0x11, 0x14, 0x18, + 0x1C, 0x20, 0x24, 0x28, 0x2D, 0x32, 0x38, 0x3F, + 0x00, 0x10, 0x1F, 0x2F, 0x3F, 0x1F, 0x27, 0x2F, + 0x37, 0x3F, 0x2D, 0x31, 0x36, 0x3A, 0x3F, 0x00, + 0x07, 0x0E, 0x15, 0x1C, 0x0E, 0x11, 0x15, 0x18, + 0x1C, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x00, 0x04, + 0x08, 0x0C, 0x10, 0x08, 0x0A, 0x0C, 0x0E, 0x10, + 0x0B, 0x0C, 0x0D, 0x0F, 0x10 +}; + +/* HEAP stuff */ + +struct OH { + struct OH *pohNext; + struct OH *pohPrev; + unsigned long ulOffset; + unsigned long ulSize; +}; + +struct OHALLOC { + struct OHALLOC *pohaNext; + struct OH aoh[1]; +}; + +struct HEAP { + struct OH ohFree; + struct OH ohUsed; + struct OH *pohFreeList; + struct OHALLOC *pohaChain; + + unsigned long ulMaxFreeSize; +}; + +struct HEAP heap; +unsigned long heap_start; +unsigned long heap_end; +unsigned long heap_size; + +unsigned int tqueue_pos; +unsigned long hwcursor_vbase; + +/* Draw Function stuff */ +u32 command_reg; + +/* -------------------- Macro definitions --------------------------- */ + +#ifdef SISFBDEBUG +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#define vgawb(reg,data) \ + (outb(data, ivideo.vga_base+reg)) +#define vgaww(reg,data) \ + (outw(data, ivideo.vga_base+reg)) +#define vgawl(reg,data) \ + (outl(data, ivideo.vga_base+reg)) +#define vgarb(reg) \ + (inb(ivideo.vga_base+reg)) + +/* ---------------------- Routine Prototype ------------------------- */ + +/* Interface used by the world */ +int sisfb_setup(char *options); +static int sisfb_open(struct fb_info *info, int user); +static int sisfb_release(struct fb_info *info, int user); +static int sisfb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int sisfb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int sisfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int sisfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int sisfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int sisfb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int sisfb_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, int con, + struct fb_info *info); + +/* Interface to the low level console driver */ +int sisfb_init(void); +static int sisfb_update_var(int con, struct fb_info *info); +static int sisfb_switch(int con, struct fb_info *info); +static void sisfb_blank(int blank, struct fb_info *info); + +/* Internal routines */ +static void crtc_to_var(struct fb_var_screeninfo *var); +static void sisfb_set_disp(int con, struct fb_var_screeninfo *var); +static int sis_getcolreg(unsigned regno, unsigned *red, unsigned *green, + unsigned *blue, unsigned *transp, + struct fb_info *fb_info); +static int sis_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *fb_info); +static void do_install_cmap(int con, struct fb_info *info); +static int do_set_var(struct fb_var_screeninfo *var, int isactive, + struct fb_info *info); + +/* set-mode routines */ +static void set_reg1(u16 port, u16 index, u16 data); +static void set_reg3(u16 port, u16 data); +static void set_reg4(u16 port, unsigned long data); +static u8 get_reg1(u16 port, u16 index); +static u8 get_reg2(u16 port); +static u32 get_reg3(u16 port); +static u16 get_modeID_length(unsigned long ROMAddr, u16 ModeNo); +static int search_modeID(unsigned long ROMAddr, u16 ModeNo); +static int check_memory_size(unsigned long ROMAddr); +static void get_mode_ptr(unsigned long ROMAddr, u16 ModeNo); +static void set_seq_regs(unsigned long ROMAddr); +static void set_misc_regs(unsigned long ROMAddr); +static void set_crtc_regs(unsigned long ROMAddr); +static void set_attregs(unsigned long ROMAddr); +static void set_grc_regs(unsigned long ROMAddr); +static void ClearExt1Regs(void); +static u16 GetRefindexLength(unsigned long ROMAddr, u16 ModeNo); +static int get_rate_ptr(unsigned long ROMAddr, u16 ModeNo); +static void set_sync(unsigned long ROMAddr); +static void set_crt1_crtc(unsigned long ROMAddr); +static void set_crt1_offset(unsigned long ROMAddr); +static u16 get_vclk_len(unsigned long ROMAddr); +static void set_crt1_vclk(unsigned long ROMAddr); +static void set_vclk_state(unsigned long ROMAddr, u16 ModeNo); +static u16 calc_delay2(unsigned long ROMAddr, u16 key); +static u16 calc_delay(unsigned long ROMAddr, u16 key); +static void set_crt1_FIFO(unsigned long ROMAddr); +static void set_crt1_FIFO2(unsigned long ROMAddr); +static void set_crt1_mode_regs(unsigned long ROMAddr, u16 ModeNo); +static void set_interlace(unsigned long ROMAddr, u16 ModeNo); +static void write_DAC(u16 dl, u16 ah, u16 al, u16 dh); +static void load_DAC(unsigned long ROMAddr); +static void display_on(void); + +static int SiSSetMode(u16 ModeNo); +static void pre_setmode(void); +static void post_setmode(void); +static void search_mode(const char *name); +static u8 search_refresh_rate(unsigned int rate); + +/* heap routines */ +static int sisfb_heap_init(void); +static struct OH *poh_new_node(void); +static struct OH *poh_allocate(unsigned long size); +static struct OH *poh_free(unsigned long base); +static void delete_node(struct OH *poh); +static void insert_node(struct OH *pohList, struct OH *poh); +static void free_node(struct OH *poh); + +/* ---------------------- Internal Routines ------------------------- */ + +inline static u32 RD32(unsigned char *base, s32 off) +{ + return readl(base + off); +} + +inline static void WR32(unsigned char *base, s32 off, u32 v) +{ + writel(v, base + off); +} + +inline static void WR16(unsigned char *base, s32 off, u16 v) +{ + writew(v, base + off); +} + +inline static void WR8(unsigned char *base, s32 off, u8 v) +{ + writeb(v, base + off); +} + +inline static u32 regrl(s32 off) +{ + return RD32(ivideo.mmio_vbase, off); +} + +inline static void regwl(s32 off, u32 v) +{ + WR32(ivideo.mmio_vbase, off, v); +} + +inline static void regww(s32 off, u16 v) +{ + WR16(ivideo.mmio_vbase, off, v); +} + +inline static void regwb(s32 off, u8 v) +{ + WR8(ivideo.mmio_vbase, off, v); +} + +/* + * Get CRTC registers to set var + */ +static void crtc_to_var(struct fb_var_screeninfo *var) +{ + u16 VRE, VBE, VRS, VBS, VDE, VT; + u16 HRE, HBE, HRS, HBS, HDE, HT; + u8 uSRdata, uCRdata, uCRdata2, uCRdata3, uMRdata; + int A, B, C, D, E, F, temp; + double hrate, drate; + + vgawb(SEQ_ADR, 0x6); + uSRdata = vgarb(SEQ_DATA); + + if (uSRdata & 0x20) + var->vmode = FB_VMODE_INTERLACED; + else + var->vmode = FB_VMODE_NONINTERLACED; + + switch ((uSRdata & 0x1c) >> 2) { + case 0: + var->bits_per_pixel = 8; + break; + case 2: + var->bits_per_pixel = 16; + break; + case 4: + var->bits_per_pixel = 32; + break; + } + + switch (var->bits_per_pixel) { + case 8: + var->red.length = 6; + var->green.length = 6; + var->blue.length = 6; + video_cmap_len = 256; + break; + case 16: /* RGB 565 */ + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + video_cmap_len = 16; + + break; + case 24: /* RGB 888 */ + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + video_cmap_len = 16; + break; + case 32: + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + video_cmap_len = 16; + break; + } + + vgawb(SEQ_ADR, 0xa); + uSRdata = vgarb(SEQ_DATA); + + vgawb(CRTC_ADR, 0x6); + uCRdata = vgarb(CRTC_DATA); + vgawb(CRTC_ADR, 0x7); + uCRdata2 = vgarb(CRTC_DATA); + VT = + (uCRdata & 0xff) | ((u16) (uCRdata2 & 0x01) << 8) | + ((u16) (uCRdata2 & 0x20) << 4) | ((u16) (uSRdata & 0x01) << + 10); + A = VT + 2; + + vgawb(CRTC_ADR, 0x12); + uCRdata = vgarb(CRTC_DATA); + VDE = + (uCRdata & 0xff) | ((u16) (uCRdata2 & 0x02) << 7) | + ((u16) (uCRdata2 & 0x40) << 3) | ((u16) (uSRdata & 0x02) << 9); + E = VDE + 1; + + vgawb(CRTC_ADR, 0x10); + uCRdata = vgarb(CRTC_DATA); + VRS = + (uCRdata & 0xff) | ((u16) (uCRdata2 & 0x04) << 6) | + ((u16) (uCRdata2 & 0x80) << 2) | ((u16) (uSRdata & 0x08) << 7); + F = VRS + 1 - E; + + vgawb(CRTC_ADR, 0x15); + uCRdata = vgarb(CRTC_DATA); + vgawb(CRTC_ADR, 0x9); + uCRdata3 = vgarb(CRTC_DATA); + VBS = + (uCRdata & 0xff) | ((u16) (uCRdata2 & 0x08) << 5) | + ((u16) (uCRdata3 & 0x20) << 4) | ((u16) (uSRdata & 0x04) << 8); + + vgawb(CRTC_ADR, 0x16); + uCRdata = vgarb(CRTC_DATA); + VBE = (uCRdata & 0xff) | ((u16) (uSRdata & 0x10) << 4); + temp = VBE - ((E - 1) & 511); + B = (temp > 0) ? temp : (temp + 512); + + vgawb(CRTC_ADR, 0x11); + uCRdata = vgarb(CRTC_DATA); + VRE = (uCRdata & 0x0f) | ((uSRdata & 0x20) >> 1); + temp = VRE - ((E + F - 1) & 31); + C = (temp > 0) ? temp : (temp + 32); + + D = B - F - C; + + var->yres = var->yres_virtual = E; + var->upper_margin = D; + var->lower_margin = F; + var->vsync_len = C; + + vgawb(SEQ_ADR, 0xb); + uSRdata = vgarb(SEQ_DATA); + + vgawb(CRTC_ADR, 0x0); + uCRdata = vgarb(CRTC_DATA); + HT = (uCRdata & 0xff) | ((u16) (uSRdata & 0x03) << 8); + A = HT + 5; + + vgawb(CRTC_ADR, 0x1); + uCRdata = vgarb(CRTC_DATA); + HDE = (uCRdata & 0xff) | ((u16) (uSRdata & 0x0C) << 6); + E = HDE + 1; + + vgawb(CRTC_ADR, 0x4); + uCRdata = vgarb(CRTC_DATA); + HRS = (uCRdata & 0xff) | ((u16) (uSRdata & 0xC0) << 2); + F = HRS - E - 3; + + vgawb(CRTC_ADR, 0x2); + uCRdata = vgarb(CRTC_DATA); + HBS = (uCRdata & 0xff) | ((u16) (uSRdata & 0x30) << 4); + + vgawb(SEQ_ADR, 0xc); + uSRdata = vgarb(SEQ_DATA); + vgawb(CRTC_ADR, 0x3); + uCRdata = vgarb(CRTC_DATA); + vgawb(CRTC_ADR, 0x5); + uCRdata2 = vgarb(CRTC_DATA); + HBE = + (uCRdata & 0x1f) | ((u16) (uCRdata2 & 0x80) >> 2) | + ((u16) (uSRdata & 0x03) << 6); + HRE = (uCRdata2 & 0x1f) | ((uSRdata & 0x04) << 3); + + temp = HBE - ((E - 1) & 255); + B = (temp > 0) ? temp : (temp + 256); + + temp = HRE - ((E + F + 3) & 63); + C = (temp > 0) ? temp : (temp + 64); + + D = B - F - C; + + var->xres = var->xres_virtual = E * 8; + var->left_margin = D * 8; + var->right_margin = F * 8; + var->hsync_len = C * 8; + + var->activate = FB_ACTIVATE_NOW; + + var->sync = 0; + + uMRdata = vgarb(0x1C); + if (uMRdata & 0x80) + var->sync &= ~FB_SYNC_VERT_HIGH_ACT; + else + var->sync |= FB_SYNC_VERT_HIGH_ACT; + + if (uMRdata & 0x40) + var->sync &= ~FB_SYNC_HOR_HIGH_ACT; + else + var->sync |= FB_SYNC_HOR_HIGH_ACT; + + VT += 2; + VT <<= 1; + HT = (HT + 5) * 8; + + hrate = (double) ivideo.refresh_rate * (double) VT / 2; + drate = hrate * HT; + var->pixclock = (u32) (1E12 / drate); +} + +static void sisfb_set_disp(int con, struct fb_var_screeninfo *var) +{ + struct fb_fix_screeninfo fix; + struct display *display; + struct display_switch *sw; + u32 flags; + + if (con >= 0) + display = &fb_display[con]; + else + display = &disp; /* used during initialization */ + + sisfb_get_fix(&fix, con, 0); + + display->screen_base = ivideo.video_vbase; + display->visual = fix.visual; + display->type = fix.type; + display->type_aux = fix.type_aux; + display->ypanstep = fix.ypanstep; + display->ywrapstep = fix.ywrapstep; + display->line_length = fix.line_length; + display->next_line = fix.line_length; + /*display->can_soft_blank = 1; */ + display->can_soft_blank = 0; + display->inverse = inverse; + display->var = *var; + + save_flags(flags); + switch (ivideo.video_bpp) { +#ifdef FBCON_HAS_CFB8 + case 8: + sw = &fbcon_cfb8; + break; +#endif +#ifdef FBCON_HAS_CFB16 + case 15: + case 16: + sw = &fbcon_cfb16; + display->dispsw_data = fbcon_cmap.cfb16; + break; +#endif +#ifdef FBCON_HAS_CFB24 + case 24: + sw = &fbcon_cfb24; + display->dispsw_data = fbcon_cmap.cfb24; + break; +#endif +#ifdef FBCON_HAS_CFB32 + case 32: + sw = &fbcon_cfb32; + display->dispsw_data = fbcon_cmap.cfb32; + break; +#endif + default: + sw = &fbcon_dummy; + return; + } + memcpy(&sisfb_sw, sw, sizeof(*sw)); + display->dispsw = &sisfb_sw; + restore_flags(flags); + + display->scrollmode = SCROLL_YREDRAW; + sisfb_sw.bmove = fbcon_redraw_bmove; + +} + +/* + * Read a single color register and split it into colors/transparent. + * Return != 0 for invalid regno. + */ + +static int sis_getcolreg(unsigned regno, unsigned *red, unsigned *green, + unsigned *blue, unsigned *transp, + struct fb_info *fb_info) +{ + if (regno >= video_cmap_len) + return 1; + + *red = palette[regno].red; + *green = palette[regno].green; + *blue = palette[regno].blue; + *transp = 0; + return 0; +} + +/* + * Set a single color register. The values supplied are already + * rounded down to the hardware's capabilities (according to the + * entries in the var structure). Return != 0 for invalid regno. + */ + +static int sis_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *fb_info) +{ + + if (regno >= video_cmap_len) + return 1; + + palette[regno].red = red; + palette[regno].green = green; + palette[regno].blue = blue; + + switch (ivideo.video_bpp) { +#ifdef FBCON_HAS_CFB8 + case 8: + vgawb(DAC_ADR, regno); + vgawb(DAC_DATA, red >> 10); + vgawb(DAC_DATA, green >> 10); + vgawb(DAC_DATA, blue >> 10); + break; +#endif +#ifdef FBCON_HAS_CFB16 + case 15: + case 16: + fbcon_cmap.cfb16[regno] = + ((red & 0xf800)) | + ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11); + break; +#endif +#ifdef FBCON_HAS_CFB24 + case 24: + red >>= 8; + green >>= 8; + blue >>= 8; + fbcon_cmap.cfb24[regno] = + (red << 16) | (green << 8) | (blue); + break; +#endif +#ifdef FBCON_HAS_CFB32 + case 32: + red >>= 8; + green >>= 8; + blue >>= 8; + fbcon_cmap.cfb32[regno] = + (red << 16) | (green << 8) | (blue); + break; +#endif + } + return 0; +} + +static void do_install_cmap(int con, struct fb_info *info) +{ + if (con != currcon) + return; + + if (fb_display[con].cmap.len) + fb_set_cmap(&fb_display[con].cmap, 1, sis_setcolreg, info); + else + fb_set_cmap(fb_default_cmap(video_cmap_len), 1, + sis_setcolreg, info); +} + +static int do_set_var(struct fb_var_screeninfo *var, int isactive, + struct fb_info *info) +{ + unsigned int htotal = + var->left_margin + var->xres + var->right_margin + + var->hsync_len; + unsigned int vtotal = + var->upper_margin + var->yres + var->lower_margin + + var->vsync_len; + double drate = 0, hrate = 0; + int found_mode = 0; + + if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) + vtotal <<= 1; + else if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) + vtotal <<= 2; + else if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) + var->yres <<= 1; + + + if (!htotal || !vtotal) { + DPRINTK("Invalid 'var' Information!\n"); + return 1; + } + + drate = 1E12 / var->pixclock; + hrate = drate / htotal; + ivideo.refresh_rate = (unsigned int) (hrate / vtotal * 2 + 0.5); + + mode_idx = 0; + while ((sisbios_mode[mode_idx].mode_no != 0) + && (sisbios_mode[mode_idx].xres <= var->xres)) { + if ((sisbios_mode[mode_idx].xres == var->xres) + && (sisbios_mode[mode_idx].yres == var->yres) + && (sisbios_mode[mode_idx].bpp == var->bits_per_pixel)) { + mode_no = sisbios_mode[mode_idx].mode_no; + found_mode = 1; + break; + } + mode_idx++; + } + + if (!found_mode) { + printk("sisfb does not support mode %dx%d-%d\n", var->xres, + var->yres, var->bits_per_pixel); + return 1; + } + + if (search_refresh_rate(ivideo.refresh_rate) == 0) { /* not supported rate */ + rate_idx = sisbios_mode[mode_idx].rate_idx; + ivideo.refresh_rate = 60; + } + + + if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) + && isactive) { + pre_setmode(); + + if (SiSSetMode(mode_no)) { + DPRINTK("sisfb: set mode[0x%x]: failed\n", + mode_no); + return 1; + } + + post_setmode(); + + ivideo.video_bpp = sisbios_mode[mode_idx].bpp; + ivideo.video_width = sisbios_mode[mode_idx].xres; + ivideo.video_height = sisbios_mode[mode_idx].yres; + video_linelength = + ivideo.video_width * (ivideo.video_bpp >> 3); + + DPRINTK("Current Mode: %dx%d-%d line_length=%d\n", + ivideo.video_width, ivideo.video_height, + ivideo.video_bpp, video_linelength); + } + + return 0; +} + +/* ---------------------- Draw Funtions ----------------------------- */ + +static void sis_get_glyph(struct GlyInfo *gly) +{ + struct display *p = &fb_display[currcon]; + u16 c; + u8 *cdat; + int widthb; + u8 *gbuf = gly->gmask; + int size; + + + gly->fontheight = fontheight(p); + gly->fontwidth = fontwidth(p); + widthb = (fontwidth(p) + 7) / 8; + + c = gly->ch & p->charmask; + if (fontwidth(p) <= 8) + cdat = p->fontdata + c * fontheight(p); + else + cdat = p->fontdata + (c * fontheight(p) << 1); + + size = fontheight(p) * widthb; + memcpy(gbuf, cdat, size); + gly->ngmask = size; +} + + +/* ---------------------- HEAP Routines ----------------------------- */ + +/* + * Heap Initialization + */ + +static int sisfb_heap_init(void) +{ + struct OH *poh; + u8 jTemp, tq_state; + + heap_start = (unsigned long) ivideo.video_vbase + RESERVED_MEM_SIZE; + //heap_start = (unsigned long)ivideo.video_vbase + (video_size - RESERVED_MEM_SIZE); + heap_end = (unsigned long) ivideo.video_vbase + ivideo.video_size; + heap_size = heap_end - heap_start; + + + /* Setting for Turbo Queue */ + if (heap_size >= TURBO_QUEUE_AREA_SIZE) { + tqueue_pos = + (ivideo.video_size - + TURBO_QUEUE_AREA_SIZE) / (64 * 1024); + jTemp = (u8) (tqueue_pos & 0xff); + vgawb(SEQ_ADR, IND_SIS_TURBOQUEUE_SET); + tq_state = vgarb(SEQ_DATA); + tq_state |= 0xf0; + tq_state &= 0xfc; + tq_state |= (u8) (tqueue_pos >> 8); + vgawb(SEQ_DATA, tq_state); + vgawb(SEQ_ADR, IND_SIS_TURBOQUEUE_ADR); + vgawb(SEQ_DATA, jTemp); + + caps |= TURBO_QUEUE_CAP; + + heap_end -= TURBO_QUEUE_AREA_SIZE; + heap_size -= TURBO_QUEUE_AREA_SIZE; + } + + /* Setting for HW cursor(4K) */ + if (heap_size >= HW_CURSOR_AREA_SIZE) { + heap_end -= HW_CURSOR_AREA_SIZE; + heap_size -= HW_CURSOR_AREA_SIZE; + hwcursor_vbase = heap_end; + + caps |= HW_CURSOR_CAP; + } + + heap.pohaChain = NULL; + heap.pohFreeList = NULL; + + poh = poh_new_node(); + + if (poh == NULL) + return 1; + + /* The first node describles the entire heap size */ + poh->pohNext = &heap.ohFree; + poh->pohPrev = &heap.ohFree; + poh->ulSize = heap_end - heap_start + 1; + poh->ulOffset = heap_start - (unsigned long) ivideo.video_vbase; + + DPRINTK("sisfb:Heap start:0x%p, end:0x%p, len=%dk\n", + (char *) heap_start, (char *) heap_end, + (unsigned int) poh->ulSize / 1024); + + DPRINTK("sisfb:First Node offset:0x%x, size:%dk\n", + (unsigned int) poh->ulOffset, (unsigned int) poh->ulSize / 1024); + + /* The second node in our free list sentinel */ + heap.ohFree.pohNext = poh; + heap.ohFree.pohPrev = poh; + heap.ohFree.ulSize = 0; + heap.ulMaxFreeSize = poh->ulSize; + + /* Initialize the discardable list */ + heap.ohUsed.pohNext = &heap.ohUsed; + heap.ohUsed.pohPrev = &heap.ohUsed; + heap.ohUsed.ulSize = SENTINEL; + + return 0; +} + +/* + * Allocates a basic memory unit in which we'll pack our data structures. + */ + +static struct OH *poh_new_node(void) +{ + int i; + unsigned long cOhs; + struct OHALLOC *poha; + struct OH *poh; + + if (heap.pohFreeList == NULL) { + poha = kmalloc(OH_ALLOC_SIZE, GFP_KERNEL); + + poha->pohaNext = heap.pohaChain; + heap.pohaChain = poha; + + cOhs = + (OH_ALLOC_SIZE - + sizeof(struct OHALLOC)) / sizeof(struct OH) + 1; + + poh = &poha->aoh[0]; + for (i = cOhs - 1; i != 0; i--) { + poh->pohNext = poh + 1; + poh = poh + 1; + } + + poh->pohNext = NULL; + heap.pohFreeList = &poha->aoh[0]; + } + + poh = heap.pohFreeList; + heap.pohFreeList = poh->pohNext; + + return (poh); +} + +/* + * Allocates space, return NULL when failed + */ + +static struct OH *poh_allocate(unsigned long size) +{ + struct OH *pohThis; + struct OH *pohRoot; + int bAllocated = 0; + + if (size > heap.ulMaxFreeSize) { + DPRINTK("sisfb: Can't allocate %dk size on offscreen\n", + (unsigned int) size / 1024); + return (NULL); + } + + pohThis = heap.ohFree.pohNext; + + while (pohThis != &heap.ohFree) { + if (size <= pohThis->ulSize) { + bAllocated = 1; + break; + } + pohThis = pohThis->pohNext; + } + + if (!bAllocated) { + DPRINTK("sisfb: Can't allocate %dk size on offscreen\n", + (unsigned int) size / 1024); + return (NULL); + } + + if (size == pohThis->ulSize) { + pohRoot = pohThis; + delete_node(pohThis); + } else { + pohRoot = poh_new_node(); + + if (pohRoot == NULL) { + return (NULL); + } + + pohRoot->ulOffset = pohThis->ulOffset; + pohRoot->ulSize = size; + + pohThis->ulOffset += size; + pohThis->ulSize -= size; + } + + heap.ulMaxFreeSize -= size; + + pohThis = &heap.ohUsed; + insert_node(pohThis, pohRoot); + + return (pohRoot); +} + +/* + * To remove a node from a list. + */ + +static void delete_node(struct OH *poh) +{ + struct OH *pohPrev; + struct OH *pohNext; + + + pohPrev = poh->pohPrev; + pohNext = poh->pohNext; + + pohPrev->pohNext = pohNext; + pohNext->pohPrev = pohPrev; + + return; +} + +/* + * To insert a node into a list. + */ + +static void insert_node(struct OH *pohList, struct OH *poh) +{ + struct OH *pohTemp; + + pohTemp = pohList->pohNext; + + pohList->pohNext = poh; + pohTemp->pohPrev = poh; + + poh->pohPrev = pohList; + poh->pohNext = pohTemp; +} + +/* + * Frees an off-screen heap allocation. + */ + +static struct OH *poh_free(unsigned long base) +{ + + struct OH *pohThis; + struct OH *pohFreed; + struct OH *pohPrev; + struct OH *pohNext; + unsigned long ulUpper; + unsigned long ulLower; + int foundNode = 0; + + pohFreed = heap.ohUsed.pohNext; + + while (pohFreed != &heap.ohUsed) { + if (pohFreed->ulOffset == base) { + foundNode = 1; + break; + } + + pohFreed = pohFreed->pohNext; + } + + if (!foundNode) + return (NULL); + + heap.ulMaxFreeSize += pohFreed->ulSize; + + pohPrev = pohNext = NULL; + ulUpper = pohFreed->ulOffset + pohFreed->ulSize; + ulLower = pohFreed->ulOffset; + + pohThis = heap.ohFree.pohNext; + + while (pohThis != &heap.ohFree) { + if (pohThis->ulOffset == ulUpper) { + pohNext = pohThis; + } + else if ((pohThis->ulOffset + pohThis->ulSize) == + ulLower) { + pohPrev = pohThis; + } + pohThis = pohThis->pohNext; + } + + delete_node(pohFreed); + + if (pohPrev && pohNext) { + pohPrev->ulSize += (pohFreed->ulSize + pohNext->ulSize); + delete_node(pohNext); + free_node(pohFreed); + free_node(pohNext); + return (pohPrev); + } + + if (pohPrev) { + pohPrev->ulSize += pohFreed->ulSize; + free_node(pohFreed); + return (pohPrev); + } + + if (pohNext) { + pohNext->ulSize += pohFreed->ulSize; + pohNext->ulOffset = pohFreed->ulOffset; + free_node(pohFreed); + return (pohNext); + } + + insert_node(&heap.ohFree, pohFreed); + + return (pohFreed); +} + +/* + * Frees our basic data structure allocation unit by adding it to a free + * list. + */ + +static void free_node(struct OH *poh) +{ + if (poh == NULL) { + return; + } + + poh->pohNext = heap.pohFreeList; + heap.pohFreeList = poh; + + return; +} + +void sis_malloc(struct sis_memreq *req) +{ + struct OH *poh; + + poh = poh_allocate(req->size); + + if (poh == NULL) { + req->offset = 0; + req->size = 0; + DPRINTK("sisfb: VMEM Allocation Failed\n"); + } else { + DPRINTK("sisfb: VMEM Allocation Successed : 0x%p\n", + (char *) (poh->ulOffset + + (unsigned long) ivideo.video_vbase)); + + req->offset = poh->ulOffset; + req->size = poh->ulSize; + } + +} + +void sis_free(unsigned long base) +{ + struct OH *poh; + + poh = poh_free(base); + + if (poh == NULL) { + DPRINTK("sisfb: poh_free() failed at base 0x%x\n", + (unsigned int) base); + } + +} + + + +/* ---------------------- SetMode Routines -------------------------- */ + +static void set_reg1(u16 port, u16 index, u16 data) +{ + outb((u8) (index & 0xff), port); + port++; + outb((u8) (data & 0xff), port); +} + +static void set_reg3(u16 port, u16 data) +{ + outb((u8) (data & 0xff), port); +} + +static void set_reg4(u16 port, unsigned long data) +{ + outl((u32) (data & 0xffffffff), port); +} + +static u8 get_reg1(u16 port, u16 index) +{ + u8 data; + + outb((u8) (index & 0xff), port); + port += 1; + data = inb(port); + return (data); +} + +static u8 get_reg2(u16 port) +{ + u8 data; + + data = inb(port); + + return (data); +} + +static u32 get_reg3(u16 port) +{ + u32 data; + + data = inl(port); + return (data); +} + +static u16 get_modeID_length(unsigned long ROMAddr, u16 ModeNo) +{ + unsigned char ModeID; + u16 modeidlength; + u16 usModeIDOffset; + + modeidlength = 0; + usModeIDOffset = *((u16 *) (ROMAddr + 0x20A)); + ModeID = *((unsigned char *) (ROMAddr + usModeIDOffset)); + while (ModeID != 0x2E) { + modeidlength++; + usModeIDOffset = usModeIDOffset + 1; + ModeID = *((unsigned char *) (ROMAddr + usModeIDOffset)); + } + return (modeidlength); +} + +static int search_modeID(unsigned long ROMAddr, u16 ModeNo) +{ + unsigned char ModeID; + u16 usIDLength; + unsigned int count = 0; + + ModeIDOffset = *((u16 *) (ROMAddr + 0x20A)); + ModeID = *((unsigned char *) (ROMAddr + ModeIDOffset)); + usIDLength = get_modeID_length(ROMAddr, ModeNo); + while (ModeID != 0xff && ModeID != ModeNo) { + ModeIDOffset = ModeIDOffset + usIDLength; + ModeID = *((unsigned char *) (ROMAddr + ModeIDOffset)); + if (count++ >= 0xff) + break; + } + if (ModeID == 0xff) + return (FALSE); + else + return (TRUE); +} + +static int check_memory_size(unsigned long ROMAddr) +{ + u16 memorysize; + u16 modeflag; + u16 temp; + + modeflag = *((u16 *) (ROMAddr + ModeIDOffset + 0x01)); + ModeType = modeflag & ModeInfoFlag; + + memorysize = modeflag & MemoryInfoFlag; + memorysize = memorysize >> MemorySizeShift; + memorysize++; + + temp = get_reg1(P3c4, 0x14); + temp = temp & 0x3F; + temp++; + + if (temp < memorysize) + return (FALSE); + else + return (TRUE); +} + +static void get_mode_ptr(unsigned long ROMAddr, u16 ModeNo) +{ + unsigned char index; + + StandTable = *((u16 *) (ROMAddr + 0x202)); + + if (ModeNo <= 13) + index = + *((unsigned char *) (ROMAddr + ModeIDOffset + 0x03)); + else { + if (ModeType <= 0x02) + index = 0x1B; + else + index = 0x0F; + } + + StandTable = StandTable + 64 * index; + +} + +static void set_seq_regs(unsigned long ROMAddr) +{ + unsigned char SRdata; + u16 i; + + set_reg1(P3c4, 0x00, 0x03); + StandTable = StandTable + 0x05; + SRdata = *((unsigned char *) (ROMAddr + StandTable)); + SRdata = SRdata | 0x20; + set_reg1(P3c4, 0x01, SRdata); + + for (i = 02; i <= 04; i++) { + StandTable++; + SRdata = *((unsigned char *) (ROMAddr + StandTable)); + set_reg1(P3c4, i, SRdata); + } +} + +static void set_misc_regs(unsigned long ROMAddr) +{ + unsigned char Miscdata; + + StandTable++; + Miscdata = *((unsigned char *) (ROMAddr + StandTable)); + set_reg3(P3c2, Miscdata); +} + +static void set_crtc_regs(unsigned long ROMAddr) +{ + unsigned char CRTCdata; + u16 i; + + CRTCdata = (unsigned char) get_reg1(P3d4, 0x11); + CRTCdata = CRTCdata & 0x7f; + set_reg1(P3d4, 0x11, CRTCdata); + + for (i = 0; i <= 0x18; i++) { + StandTable++; + CRTCdata = *((unsigned char *) (ROMAddr + StandTable)); + set_reg1(P3d4, i, CRTCdata); + } +} + +static void set_attregs(unsigned long ROMAddr) +{ + unsigned char ARdata; + u16 i; + + for (i = 0; i <= 0x13; i++) { + StandTable++; + ARdata = *((unsigned char *) (ROMAddr + StandTable)); + + get_reg2(P3da); + set_reg3(P3c0, i); + set_reg3(P3c0, ARdata); + } + get_reg2(P3da); + set_reg3(P3c0, 0x14); + set_reg3(P3c0, 0x00); + + get_reg2(P3da); + set_reg3(P3c0, 0x20); +} + +static void set_grc_regs(unsigned long ROMAddr) +{ + unsigned char GRdata; + u16 i; + + for (i = 0; i <= 0x08; i++) { + StandTable++; + GRdata = *((unsigned char *) (ROMAddr + StandTable)); + set_reg1(P3ce, i, GRdata); + } + if (ModeType > ModeVGA) { + GRdata = (unsigned char) get_reg1(P3ce, 0x05); + GRdata = GRdata & 0xBF; + set_reg1(P3ce, 0x05, GRdata); + } +} + +static void ClearExt1Regs(void) +{ + u16 i; + + for (i = 0x0A; i <= 0x0E; i++) + set_reg1(P3c4, i, 0x00); +} + +static u16 GetRefindexLength(unsigned long ROMAddr, u16 ModeNo) +{ + unsigned char ModeID; + unsigned char temp; + u16 refindexlength; + u16 usModeIDOffset; + u16 usREFIndex; + u16 usIDLength; + + usModeIDOffset = *((u16 *) (ROMAddr + 0x20A)); + ModeID = *((unsigned char *) (ROMAddr + usModeIDOffset)); + usIDLength = get_modeID_length(ROMAddr, ModeNo); + while (ModeID != 0x40) { + usModeIDOffset = usModeIDOffset + usIDLength; + ModeID = *((unsigned char *) (ROMAddr + usModeIDOffset)); + } + + refindexlength = 1; + usREFIndex = *((u16 *) (ROMAddr + usModeIDOffset + 0x04)); + usREFIndex++; + temp = *((unsigned char *) (ROMAddr + usREFIndex)); + while (temp != 0xFF) { + refindexlength++; + usREFIndex++; + temp = *((unsigned char *) (ROMAddr + usREFIndex)); + } + return (refindexlength); +} + +static int get_rate_ptr(unsigned long ROMAddr, u16 ModeNo) +{ + short index; + u16 temp; + u16 ulRefIndexLength; + + if (ModeNo < 0x14) + return (FALSE); + + index = get_reg1(P3d4, 0x33); + index = index & 0x0F; + if (index != 0) + index--; + + REFIndex = *((u16 *) (ROMAddr + ModeIDOffset + 0x04)); + + ulRefIndexLength = GetRefindexLength(ROMAddr, ModeNo); + + do { + temp = *((u16 *) (ROMAddr + REFIndex)); + if (temp == 0xFFFF) + break; + temp = temp & ModeInfoFlag; + if (temp < ModeType) + break; + + REFIndex = REFIndex + ulRefIndexLength; + index--; + } while (index >= 0); + + REFIndex = REFIndex - ulRefIndexLength; + return (TRUE); +} + +static void set_sync(unsigned long ROMAddr) +{ + u16 sync; + u16 temp; + + sync = *((u16 *) (ROMAddr + REFIndex)); + sync = sync & 0xC0; + temp = 0x2F; + temp = temp | sync; + set_reg3(P3c2, temp); +} + +static void set_crt1_crtc(unsigned long ROMAddr) +{ + unsigned char index; + unsigned char data; + u16 i; + + index = *((unsigned char *) (ROMAddr + REFIndex + 0x02)); + CRT1Table = *((u16 *) (ROMAddr + 0x204)); + CRT1Table = CRT1Table + index * CRT1Len; + + data = (unsigned char) get_reg1(P3d4, 0x11); + data = data & 0x7F; + set_reg1(P3d4, 0x11, data); + + CRT1Table--; + for (i = 0; i <= 0x05; i++) { + CRT1Table++; + data = *((unsigned char *) (ROMAddr + CRT1Table)); + set_reg1(P3d4, i, data); + } + for (i = 0x06; i <= 0x07; i++) { + CRT1Table++; + data = *((unsigned char *) (ROMAddr + CRT1Table)); + set_reg1(P3d4, i, data); + } + for (i = 0x10; i <= 0x12; i++) { + CRT1Table++; + data = *((unsigned char *) (ROMAddr + CRT1Table)); + set_reg1(P3d4, i, data); + } + for (i = 0x15; i <= 0x16; i++) { + CRT1Table++; + data = *((unsigned char *) (ROMAddr + CRT1Table)); + set_reg1(P3d4, i, data); + } + for (i = 0x0A; i <= 0x0C; i++) { + CRT1Table++; + data = *((unsigned char *) (ROMAddr + CRT1Table)); + set_reg1(P3c4, i, data); + } + + CRT1Table++; + data = *((unsigned char *) (ROMAddr + CRT1Table)); + data = data & 0xE0; + set_reg1(P3c4, 0x0E, data); + + data = (unsigned char) get_reg1(P3d4, 0x09); + data = data & 0xDF; + i = *((unsigned char *) (ROMAddr + CRT1Table)); + i = i & 0x01; + i = i << 5; + data = data | i; + i = *((u16 *) (ROMAddr + ModeIDOffset + 0x01)); + i = i & DoubleScanMode; + if (i) + data = data | 0x80; + set_reg1(P3d4, 0x09, data); + + if (ModeType > 0x03) + set_reg1(P3d4, 0x14, 0x4F); +} + +static void set_crt1_offset(unsigned long ROMAddr) +{ + u16 temp, ah, al; + u16 temp2, i; + u16 DisplayUnit; + + temp = *((unsigned char *) (ROMAddr + ModeIDOffset + 0x03)); + temp = temp >> 4; + ScreenOffset = *((u16 *) (ROMAddr + 0x206)); + temp = *((unsigned char *) (ROMAddr + ScreenOffset + temp)); + + temp2 = *((u16 *) (ROMAddr + REFIndex + 0x00)); + temp2 = temp2 & InterlaceMode; + if (temp2) + temp = temp << 1; + temp2 = ModeType - ModeEGA; + switch (temp2) { + case 0: + temp2 = 1; + break; + case 1: + temp2 = 2; + break; + case 2: + temp2 = 4; + break; + case 3: + temp2 = 4; + break; + case 4: + temp2 = 6; + break; + case 5: + temp2 = 8; + break; + } + temp = temp * temp2; + DisplayUnit = temp; + + temp2 = temp; + temp = temp >> 8; + temp = temp & 0x0F; + i = get_reg1(P3c4, 0x0E); + i = i & 0xF0; + i = i | temp; + set_reg1(P3c4, 0x0E, i); + + temp = (unsigned char) temp2; + temp = temp & 0xFF; + set_reg1(P3d4, 0x13, temp); + + temp2 = *((u16 *) (ROMAddr + REFIndex + 0x00)); + temp2 = temp2 & InterlaceMode; + if (temp2) + DisplayUnit >>= 1; + + DisplayUnit = DisplayUnit << 5; + ah = (DisplayUnit & 0xff00) >> 8; + al = DisplayUnit & 0x00ff; + if (al == 0) + ah = ah + 1; + else + ah = ah + 2; + set_reg1(P3c4, 0x10, ah); +} + +static u16 get_vclk_len(unsigned long ROMAddr) +{ + u16 VCLKDataStart, vclklabel, temp; + VCLKDataStart = *((u16 *) (ROMAddr + 0x208)); + for (temp = 0;; temp++) { + vclklabel = *((u16 *) (ROMAddr + VCLKDataStart + temp)); + if (vclklabel == VCLKStartFreq) { + temp = temp + 2; + return (temp); + } + } + return (0); +} + +static void set_crt1_vclk(unsigned long ROMAddr) +{ + u16 i; + unsigned char index, data; + + index = *((unsigned char *) (ROMAddr + REFIndex + 0x03)); + CRT1VCLKLen = get_vclk_len(ROMAddr); + data = index * CRT1VCLKLen; + VCLKData = *((u16 *) (ROMAddr + 0x208)); + VCLKData = VCLKData + data; + + set_reg1(P3c4, 0x31, 0); + + for (i = 0x2B; i <= 0x2C; i++) { + data = *((unsigned char *) (ROMAddr + VCLKData)); + set_reg1(P3c4, i, data); + VCLKData++; + } + set_reg1(P3c4, 0x2D, 0x80); +} + +static void set_vclk_state(unsigned long ROMAddr, u16 ModeNo) +{ + u16 data, data2; + u16 VCLK; + unsigned char index; + + index = *((unsigned char *) (ROMAddr + REFIndex + 0x03)); + CRT1VCLKLen = get_vclk_len(ROMAddr); + data = index * CRT1VCLKLen; + VCLKData = *((u16 *) (ROMAddr + 0x208)); + VCLKData = VCLKData + data + (CRT1VCLKLen - 2); + + VCLK = *((u16 *) (ROMAddr + VCLKData)); + if (ModeNo <= 0x13) + VCLK = 0; + + data = get_reg1(P3c4, 0x07); + data = data & 0x7B; + if (VCLK >= 150) + data = data | 0x80; + set_reg1(P3c4, 0x07, data); + + data = get_reg1(P3c4, 0x32); + data = data & 0xD7; + if (VCLK >= 150) + data = data | 0x08; + set_reg1(P3c4, 0x32, data); + + data2 = 0x03; + if (VCLK > 135) + data2 = 0x02; + if (VCLK > 160) + data2 = 0x01; + if (VCLK > 260) + data2 = 0x00; + data = get_reg1(P3c4, 0x07); + data = data & 0xFC; + data = data | data2; + set_reg1(P3c4, 0x07, data); +} + +static u16 calc_delay2(unsigned long ROMAddr, u16 key) +{ + u16 data, index; + unsigned char LatencyFactor[] = { + 88, 80, 78, 72, 70, 00, + 00, 79, 77, 71, 69, 49, + 88, 80, 78, 72, 70, 00, + 00, 72, 70, 64, 62, 44 + }; + index = 0; + data = get_reg1(P3c4, 0x14); + if (data & 0x80) + index = index + 12; + + data = get_reg1(P3c4, 0x15); + data = (data & 0xf0) >> 4; + if (data & 0x01) + index = index + 6; + + data = data >> 1; + index = index + data; + data = LatencyFactor[index]; + + return (data); +} + +static u16 calc_delay(unsigned long ROMAddr, u16 key) +{ + u16 data, data2, temp0, temp1; + unsigned char ThLowA[] = { + 61, 3, 52, 5, 68, 7, 100, 11, + 43, 3, 42, 5, 54, 7, 78, 11, + 34, 3, 37, 5, 47, 7, 67, 11 + }; + unsigned char ThLowB[] = { + 81, 4, 72, 6, 88, 8, 120, 12, + 55, 4, 54, 6, 66, 8, 90, 12, + 42, 4, 45, 6, 55, 8, 75, 12 + }; + unsigned char ThTiming[] = { 1, 2, 2, 3, 0, 1, 1, 2 }; + + data = get_reg1(P3c4, 0x16); + data = data >> 6; + data2 = get_reg1(P3c4, 0x14); + data2 = (data2 >> 4) & 0x0C; + data = data | data2; + data = data < 1; + if (key == 0) { + temp0 = (u16) ThLowA[data]; + temp1 = (u16) ThLowA[data + 1]; + } else { + temp0 = (u16) ThLowB[data]; + temp1 = (u16) ThLowB[data + 1]; + } + + data2 = 0; + data = get_reg1(P3c4, 0x18); + if (data & 0x02) + data2 = data2 | 0x01; + if (data & 0x20) + data2 = data2 | 0x02; + if (data & 0x40) + data2 = data2 | 0x04; + + data = temp1 * ThTiming[data2] + temp0; + return (data); +} + + +static void set_crt1_FIFO(unsigned long ROMAddr) +{ + u16 index, data, VCLK, data2, MCLKOffset, MCLK, colorth = 1; + u16 ah, bl, A, B; + + index = *((unsigned char *) (ROMAddr + REFIndex + 0x03)); + CRT1VCLKLen = get_vclk_len(ROMAddr); + data = index * CRT1VCLKLen; + VCLKData = *((u16 *) (ROMAddr + 0x208)); + VCLKData = VCLKData + data + (CRT1VCLKLen - 2); + VCLK = *((u16 *) (ROMAddr + VCLKData)); + + MCLKOffset = *((u16 *) (ROMAddr + 0x20C)); + index = get_reg1(P3c4, 0x3A); + index = index & 07; + MCLKOffset = MCLKOffset + index * 5; + MCLK = *((unsigned char *) (ROMAddr + MCLKOffset + 0x03)); + + data2 = ModeType - 0x02; + switch (data2) { + case 0: + colorth = 1; + break; + case 1: + colorth = 2; + break; + case 2: + colorth = 4; + break; + case 3: + colorth = 4; + break; + case 4: + colorth = 6; + break; + case 5: + colorth = 8; + break; + } + + do { + B = (calc_delay(ROMAddr, 0) * VCLK * colorth); + B = B / (16 * MCLK); + B++; + + A = (calc_delay(ROMAddr, 1) * VCLK * colorth); + A = A / (16 * MCLK); + A++; + + if (A < 4) + A = 0; + else + A = A - 4; + + if (A > B) + bl = A; + else + bl = B; + + bl++; + if (bl > 0x13) { + data = get_reg1(P3c4, 0x16); + data = data >> 6; + if (data != 0) { + data--; + data = data << 6; + data2 = get_reg1(P3c4, 0x16); + data2 = (data2 & 0x3f) | data; + set_reg1(P3c4, 0x16, data2); + } else + bl = 0x13; + } + } while (bl > 0x13); + + ah = bl; + ah = ah << 4; + ah = ah | 0x0f; + set_reg1(P3c4, 0x08, ah); + + data = bl; + data = data & 0x10; + data = data << 1; + data2 = get_reg1(P3c4, 0x0F); + data2 = data2 & 0x9f; + data2 = data2 | data; + set_reg1(P3c4, 0x0F, data2); + + data = bl + 3; + if (data > 0x0f) + data = 0x0f; + set_reg1(P3c4, 0x3b, 0x00); + data2 = get_reg1(P3c4, 0x09); + data2 = data2 & 0xF0; + data2 = data2 | data; + set_reg1(P3c4, 0x09, data2); +} + + +static void set_crt1_FIFO2(unsigned long ROMAddr) +{ + u16 index, data, VCLK, data2, MCLKOffset, MCLK, colorth = 1; + u16 ah, bl, B; + unsigned long eax; + + index = *((unsigned char *) (ROMAddr + REFIndex + 0x03)); + CRT1VCLKLen = get_vclk_len(ROMAddr); + data = index * CRT1VCLKLen; + VCLKData = *((u16 *) (ROMAddr + 0x208)); + VCLKData = VCLKData + data + (CRT1VCLKLen - 2); + VCLK = *((u16 *) (ROMAddr + VCLKData)); + + MCLKOffset = *((u16 *) (ROMAddr + 0x20C)); + index = get_reg1(P3c4, 0x1A); + index = index & 07; + MCLKOffset = MCLKOffset + index * 5; + MCLK = *((u16 *) (ROMAddr + MCLKOffset + 0x03)); + + data2 = ModeType - 0x02; + switch (data2) { + case 0: + colorth = 1; + break; + case 1: + colorth = 1; + break; + case 2: + colorth = 2; + break; + case 3: + colorth = 2; + break; + case 4: + colorth = 3; + break; + case 5: + colorth = 4; + break; + } + + do { + B = (calc_delay2(ROMAddr, 0) * VCLK * colorth); + if (B % (16 * MCLK) == 0) { + B = B / (16 * MCLK); + bl = B + 1; + } else { + B = B / (16 * MCLK); + bl = B + 2; + } + + if (bl > 0x13) { + data = get_reg1(P3c4, 0x15); + data = data & 0xf0; + if (data != 0xb0) { + data = data + 0x20; + if (data == 0xa0) + data = 0x30; + data2 = get_reg1(P3c4, 0x15); + data2 = (data2 & 0x0f) | data; + set_reg1(P3c4, 0x15, data2); + } else + bl = 0x13; + } + } while (bl > 0x13); + + data2 = get_reg1(P3c4, 0x15); + data2 = (data2 & 0xf0) >> 4; + data2 = data2 << 24; + + set_reg4(0xcf8, 0x80000050); + eax = get_reg3(0xcfc); + eax = eax & 0x0f0ffffff; + eax = eax | data2; + set_reg4(0xcfc, eax); + + ah = bl; + ah = ah << 4; + ah = ah | 0x0f; + set_reg1(P3c4, 0x08, ah); + + data = bl; + data = data & 0x10; + data = data << 1; + data2 = get_reg1(P3c4, 0x0F); + data2 = data2 & 0x9f; + data2 = data2 | data; + set_reg1(P3c4, 0x0F, data2); + + data = bl + 3; + if (data > 0x0f) + data = 0x0f; + set_reg1(P3c4, 0x3b, 0x00); + data2 = get_reg1(P3c4, 0x09); + data2 = data2 & 0xF0; + data2 = data2 | data; + set_reg1(P3c4, 0x09, data2); +} + +static void set_crt1_mode_regs(unsigned long ROMAddr, u16 ModeNo) +{ + + u16 data, data2, data3; + + if (ModeNo > 0x13) + data = *((u16 *) (ROMAddr + REFIndex + 0x00)); + else + data = 0; + + data2 = 0; + if (ModeNo > 0x13) + if (ModeType > 0x02) { + data2 = data2 | 0x02; + data3 = ModeType - ModeVGA; + data3 = data3 << 2; + data2 = data2 | data3; + } + + data = data & InterlaceMode; + if (data) + data2 = data2 | 0x20; + set_reg1(P3c4, 0x06, data2); + + data = get_reg1(P3c4, 0x01); + data = data & 0xF7; + data2 = *((u16 *) (ROMAddr + ModeIDOffset + 0x01)); + data2 = data2 & HalfDCLK; + if (data2) + data = data | 0x08; + set_reg1(P3c4, 0x01, data); + + data = get_reg1(P3c4, 0x0F); + data = data & 0xF7; + data2 = *((u16 *) (ROMAddr + ModeIDOffset + 0x01)); + data2 = data2 & LineCompareOff; + if (data2) + data = data | 0x08; + set_reg1(P3c4, 0x0F, data); + + data = get_reg1(P3c4, 0x21); + data = data & 0x1F; + if (ModeType == 0x00) + data = data | 0x60; + else if (ModeType <= 0x02) + data = data | 0x00; + else + data = data | 0xA0; + set_reg1(P3c4, 0x21, data); +} + + +static void set_interlace(unsigned long ROMAddr, u16 ModeNo) +{ + unsigned long Temp; + u16 data, Temp2; + + Temp = (unsigned long) get_reg1(P3d4, 0x01); + Temp++; + Temp = Temp * 8; + + if (Temp == 1024) + data = 0x0035; + else if (Temp == 1280) + data = 0x0048; + else + data = 0x0000; + + Temp2 = *((u16 *) (ROMAddr + REFIndex + 0x00)); + Temp2 &= InterlaceMode; + if (Temp2 == 0) + data = 0x0000; + + set_reg1(P3d4, 0x19, data); + + Temp = (unsigned long) get_reg1(P3d4, 0x1A); + Temp2 = (u16) (Temp & 0xFC); + set_reg1(P3d4, 0x1A, (u16) Temp); + + Temp = (unsigned long) get_reg1(P3c4, 0x0f); + Temp2 = (u16) Temp & 0xBF; + if (ModeNo == 0x37) + Temp2 = Temp2 | 0x40; + set_reg1(P3d4, 0x1A, (u16) Temp2); +} + +static void write_DAC(u16 dl, u16 ah, u16 al, u16 dh) +{ + u16 temp; + u16 bh, bl; + + bh = ah; + bl = al; + if (dl != 0) { + temp = bh; + bh = dh; + dh = temp; + if (dl == 1) { + temp = bl; + bl = dh; + dh = temp; + } else { + temp = bl; + bl = bh; + bh = temp; + } + } + set_reg3(P3c9, (u16) dh); + set_reg3(P3c9, (u16) bh); + set_reg3(P3c9, (u16) bl); +} + + +static void load_DAC(unsigned long ROMAddr) +{ + u16 data, data2; + u16 time, i, j, k; + u16 m, n, o; + u16 si, di, bx, dl; + u16 al, ah, dh; + u16 *table = VGA_DAC; + + data = *((u16 *) (ROMAddr + ModeIDOffset + 0x01)); + data = data & DACInfoFlag; + time = 64; + if (data == 0x00) + table = MDA_DAC; + if (data == 0x08) + table = CGA_DAC; + if (data == 0x10) + table = EGA_DAC; + if (data == 0x18) { + time = 256; + table = VGA_DAC; + } + if (time == 256) + j = 16; + else + j = time; + + set_reg3(P3c6, 0xFF); + set_reg3(P3c8, 0x00); + + for (i = 0; i < j; i++) { + data = table[i]; + for (k = 0; k < 3; k++) { + data2 = 0; + if (data & 0x01) + data2 = 0x2A; + if (data & 0x02) + data2 = data2 + 0x15; + set_reg3(P3c9, data2); + data = data >> 2; + } + } + + if (time == 256) { + for (i = 16; i < 32; i++) { + data = table[i]; + for (k = 0; k < 3; k++) + set_reg3(P3c9, data); + } + si = 32; + for (m = 0; m < 9; m++) { + di = si; + bx = si + 0x04; + dl = 0; + for (n = 0; n < 3; n++) { + for (o = 0; o < 5; o++) { + dh = table[si]; + ah = table[di]; + al = table[bx]; + si++; + write_DAC(dl, ah, al, dh); + } + si = si - 2; + for (o = 0; o < 3; o++) { + dh = table[bx]; + ah = table[di]; + al = table[si]; + si--; + write_DAC(dl, ah, al, dh); + } + dl++; + } + si = si + 5; + } + } +} + +static void display_on(void) +{ + u16 data; + + data = get_reg1(P3c4, 0x01); + data = data & 0xDF; + set_reg1(P3c4, 0x01, data); +} + +static int SiSSetMode(u16 ModeNo) +{ + unsigned long temp; + u16 cr30flag, cr31flag; + unsigned long ROMAddr = rom_vbase; + u16 BaseAddr = (u16) ivideo.vga_base; + + P3c4 = BaseAddr + 0x14; + P3d4 = BaseAddr + 0x24; + P3c0 = BaseAddr + 0x10; + P3ce = BaseAddr + 0x1e; + P3c2 = BaseAddr + 0x12; + P3ca = BaseAddr + 0x1a; + P3c6 = BaseAddr + 0x16; + P3c7 = BaseAddr + 0x17; + P3c8 = BaseAddr + 0x18; + P3c9 = BaseAddr + 0x19; + P3da = BaseAddr + 0x2A; + + temp = search_modeID(ROMAddr, ModeNo); + + if (temp == 0) + return (0); + + temp = check_memory_size(ROMAddr); + if (temp == 0) + return (0); + + cr30flag = (unsigned char) get_reg1(P3d4, 0x30); + if (((cr30flag & 0x01) == 1) || ((cr30flag & 0x02) == 0)) { + get_mode_ptr(ROMAddr, ModeNo); + set_seq_regs(ROMAddr); + set_misc_regs(ROMAddr); + set_crtc_regs(ROMAddr); + set_attregs(ROMAddr); + set_grc_regs(ROMAddr); + ClearExt1Regs(); + temp = get_rate_ptr(ROMAddr, ModeNo); + if (temp) { + set_sync(ROMAddr); + set_crt1_crtc(ROMAddr); + set_crt1_offset(ROMAddr); + set_crt1_vclk(ROMAddr); + set_vclk_state(ROMAddr, ModeNo); + if ((ivideo.chip_id == SIS_Trojan) + || (ivideo.chip_id == SIS_Spartan)) + set_crt1_FIFO2(ROMAddr); + else /* SiS 300 */ + set_crt1_FIFO(ROMAddr); + } + set_crt1_mode_regs(ROMAddr, ModeNo); + if ((ivideo.chip_id == SIS_Trojan) + || (ivideo.chip_id == + SIS_Spartan)) set_interlace(ROMAddr, ModeNo); + load_DAC(ROMAddr); + + /* clear OnScreen */ + memset((char *) ivideo.video_vbase, 0, + video_linelength * ivideo.video_height); + } + cr31flag = (unsigned char) get_reg1(P3d4, 0x31); + + display_on(); + + return (0); +} + +static void pre_setmode(void) +{ + vgawb(CRTC_ADR, 0x30); + vgawb(CRTC_DATA, 0x00); + + vgawb(CRTC_ADR, 0x31); + vgawb(CRTC_DATA, 0x60); + + DPRINTK("Setting CR33 = 0x%x\n", rate_idx & 0x0f); + + /* set CRT1 refresh rate */ + vgawb(CRTC_ADR, 0x33); + vgawb(CRTC_DATA, rate_idx & 0x0f); +} + +static void post_setmode(void) +{ + u8 uTemp; + + /* turn on CRT1 */ + vgawb(CRTC_ADR, 0x17); + uTemp = vgarb(CRTC_DATA); + uTemp |= 0x80; + vgawb(CRTC_DATA, uTemp); + + /* disable 24-bit palette RAM and Gamma correction */ + vgawb(SEQ_ADR, 0x07); + uTemp = vgarb(SEQ_DATA); + uTemp &= ~0x04; + vgawb(SEQ_DATA, uTemp); +} + +static void search_mode(const char *name) +{ + int i = 0; + + if (name == NULL) + return; + + while (sisbios_mode[i].mode_no != 0) { + if (!strcmp(name, sisbios_mode[i].name)) { + mode_idx = i; + break; + } + i++; + } + if (mode_idx < 0) + DPRINTK("Invalid user mode : %s\n", name); +} + +static u8 search_refresh_rate(unsigned int rate) +{ + u16 xres, yres; + int i = 0; + + xres = sisbios_mode[mode_idx].xres; + yres = sisbios_mode[mode_idx].yres; + + while ((vrate[i].idx != 0) && (vrate[i].xres <= xres)) { + if ((vrate[i].xres == xres) && (vrate[i].yres == yres) + && (vrate[i].refresh == rate)) { + rate_idx = vrate[i].idx; + return rate_idx; + } + i++; + } + + DPRINTK("sisfb: Unsupported rate %d in %dx%d mode\n", rate, xres, + yres); + + return 0; +} + + + +/* ------------------ Public Routines ------------------------------- */ + +/* + * Open/Release the frame buffer device + */ + +static int sisfb_open(struct fb_info *info, int user) +{ + MOD_INC_USE_COUNT; + return (0); +} + +static int sisfb_release(struct fb_info *info, int user) +{ + MOD_DEC_USE_COUNT; + return (0); +} + +/* + * Get the Fixed Part of the Display + */ + +static int sisfb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) +{ + DPRINTK("sisfb: sisfb_get_fix:[%d]\n", con); + + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + strcpy(fix->id, fb_info.modename); + + fix->smem_start = ivideo.video_base; + fix->smem_len = RESERVED_MEM_SIZE; /* reserved for Xserver */ + fix->type = video_type; + fix->type_aux = 0; + if (ivideo.video_bpp == 8) + fix->visual = FB_VISUAL_PSEUDOCOLOR; + else + fix->visual = FB_VISUAL_TRUECOLOR; + fix->xpanstep = 0; + fix->ypanstep = 0; + fix->ywrapstep = 0; + fix->line_length = video_linelength; + fix->mmio_start = ivideo.mmio_base; + fix->mmio_len = MMIO_SIZE; + fix->accel = FB_ACCEL_SIS_GLAMOUR; + fix->reserved[0] = ivideo.video_size & 0xFFFF; + fix->reserved[1] = (ivideo.video_size >> 16) & 0xFFFF; + fix->reserved[2] = caps; /* capabilities */ + + return 0; +} + +/* + * Get the User Defined Part of the Display + */ + +static int sisfb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + DPRINTK("sisfb: sisfb_get_var:[%d]\n", con); + + if (con == -1) + memcpy(var, &default_var, + sizeof(struct fb_var_screeninfo)); + else + *var = fb_display[con].var; + return 0; +} + +/* + * Set the User Defined Part of the Display + */ + +static int sisfb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + int err; + unsigned int cols, rows; + + fb_display[con].var.activate = FB_ACTIVATE_NOW; + + /* Set mode */ + if (do_set_var(var, con == currcon, info)) { + crtc_to_var(var); /* return current mode to user */ + return -EINVAL; + } + + /* get actual setting value */ + crtc_to_var(var); + + /* update display of current console */ + sisfb_set_disp(con, var); + + if (info->changevar) + (*info->changevar) (con); + + if ((err = fb_alloc_cmap(&fb_display[con].cmap, 0, 0))) + return err; + + do_install_cmap(con, info); + + /* inform console to update struct display */ + cols = sisbios_mode[mode_idx].cols; + rows = sisbios_mode[mode_idx].rows; + vc_resize_con(rows, cols, fb_display[con].conp->vc_num); + + return 0; +} + + +/* + * Get the Colormap + */ + +static int sisfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + DPRINTK("sisfb: sisfb_get_cmap:[%d]\n", con); + + if (con == currcon) + return fb_get_cmap(cmap, kspc, sis_getcolreg, info); + else if (fb_display[con].cmap.len) /* non default colormap? */ + fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); + else + fb_copy_cmap(fb_default_cmap(video_cmap_len), + cmap, kspc ? 0 : 2); + return 0; +} + +/* + * Set the Colormap + */ + +static int sisfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + int err; + + if (!fb_display[con].cmap.len) { /* no colormap allocated */ + err = fb_alloc_cmap(&fb_display[con].cmap, video_cmap_len, 0); + if (err) + return err; + } + if (con == currcon) /* current console */ + return fb_set_cmap(cmap, kspc, sis_setcolreg, info); + else + fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); + return 0; +} + +/* + * Pan or Wrap the Display + */ + +static int sisfb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + /* not support virtual screen yet */ + return -EINVAL; +} + +static int sisfb_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, int con, + struct fb_info *info) +{ + switch (cmd) { + case FBIO_ALLOC: + if(!capable(CAP_SYS_RAWIO)) + return -EPERM; + sis_malloc((struct sis_memreq *) arg); + break; + case FBIO_FREE: + if(!capable(CAP_SYS_RAWIO)) + return -EPERM; + sis_free(*(unsigned long *) arg); + break; + case FBIOGET_GLYPH: + sis_get_glyph((struct GlyInfo *) arg); + break; + case FBIOGET_HWCINFO: + { + unsigned long *hwc_offset = (unsigned long *) arg; + + if (caps | HW_CURSOR_CAP) + *hwc_offset = hwcursor_vbase - + (unsigned long) ivideo.video_vbase; + else + *hwc_offset = 0; + + break; + } + default: + return -EINVAL; + } + return 0; +} + +static int sisfb_mmap(struct fb_info *info, struct file *file, + struct vm_area_struct *vma) +{ + struct fb_var_screeninfo var; + unsigned long start; + unsigned long off; + u32 len; + + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + off = vma->vm_pgoff << PAGE_SHIFT; + + /* frame buffer memory */ + start = (unsigned long) ivideo.video_base; + len = PAGE_ALIGN((start & ~PAGE_MASK) + ivideo.video_size); + + if (off >= len) { + /* memory mapped io */ + off -= len; + sisfb_get_var(&var, currcon, info); + if (var.accel_flags) + return -EINVAL; + start = (unsigned long) ivideo.mmio_base; + len = PAGE_ALIGN((start & ~PAGE_MASK) + MMIO_SIZE); + } + + start &= PAGE_MASK; + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + vma->vm_pgoff = off >> PAGE_SHIFT; + +#if defined(__i386__) + if (boot_cpu_data.x86 > 3) + pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; +#endif + if (io_remap_page_range(vma->vm_start, off, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) return -EAGAIN; + return 0; +} + +static struct fb_ops sisfb_ops = { + sisfb_open, + sisfb_release, + sisfb_get_fix, + sisfb_get_var, + sisfb_set_var, + sisfb_get_cmap, + sisfb_set_cmap, + sisfb_pan_display, + sisfb_ioctl, + sisfb_mmap +}; + +int sisfb_setup(char *options) +{ + char *this_opt; + + fb_info.fontname[0] = '\0'; + ivideo.refresh_rate = 0; + + if (!options || !*options) + return 0; + + for (this_opt = strtok(options, ","); this_opt; + this_opt = strtok(NULL, ",")) { + if (!*this_opt) + continue; + + if (!strcmp(this_opt, "inverse")) { + inverse = 1; + fb_invert_cmaps(); + } else if (!strncmp(this_opt, "font:", 5)) { + strcpy(fb_info.fontname, this_opt + 5); + } else if (!strncmp(this_opt, "mode:", 5)) { + search_mode(this_opt + 5); + } else if (!strncmp(this_opt, "vrate:", 6)) { + ivideo.refresh_rate = + simple_strtoul(this_opt + 6, NULL, 0); + } else if (!strncmp(this_opt, "off", 3)) { + sisfb_off = 1; + } else + DPRINTK("invalid parameter %s\n", this_opt); + } + return 0; +} + +static int sisfb_update_var(int con, struct fb_info *info) +{ + return 0; +} + +/* + * Switch Console (called by fbcon.c) + */ + +static int sisfb_switch(int con, struct fb_info *info) +{ + int cols, rows; + + DPRINTK("sisfb: switch console from [%d] to [%d]\n", currcon, con); + + /* update colormap of current console */ + if (fb_display[currcon].cmap.len) + fb_get_cmap(&fb_display[currcon].cmap, 1, sis_getcolreg, info); + + fb_display[con].var.activate = FB_ACTIVATE_NOW; + + /* same mode, needn't change mode actually */ + + if (!memcmp(&fb_display[con].var, &fb_display[currcon].var, sizeof(struct fb_var_screeninfo))) + { + currcon = con; + return 1; + } + + currcon = con; + + do_set_var(&fb_display[con].var, 1, info); + + sisfb_set_disp(con, &fb_display[con].var); + + /* Install new colormap */ + do_install_cmap(con, info); + + cols = sisbios_mode[mode_idx].cols; + rows = sisbios_mode[mode_idx].rows; + vc_resize_con(rows, cols, fb_display[con].conp->vc_num); + + sisfb_update_var(con, info); + + return 1; + +} + +/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ + +static void sisfb_blank(int blank, struct fb_info *info) +{ + u8 CRData; + + vgawb(CRTC_ADR, 0x17); + CRData = vgarb(CRTC_DATA); + + if (blank > 0) /* turn off CRT1 */ + CRData &= 0x7f; + else /* turn on CRT1 */ + CRData |= 0x80; + + vgawb(CRTC_ADR, 0x17); + vgawb(CRTC_DATA, CRData); +} + +int __init sisfb_init(void) +{ + struct pci_dev *pdev; + struct board *b; + int pdev_valid = 0; + unsigned char jTemp; + + if (sisfb_off) + return -ENXIO; + + pci_for_each_dev(pdev) { + for (b = dev_list; b->vendor; b++) + { + if ((b->vendor == pdev->vendor) + && (b->device == pdev->device)) + { + pdev_valid = 1; + strcpy(fb_info.modename, b->name); + ivideo.chip_id = pdev->device; + break; + } + } + + if (pdev_valid) + break; + } + + if (!pdev_valid) + return -1; + + ivideo.video_base = pdev->resource[0].start & ~0x7FFFFF; + ivideo.mmio_base = pdev->resource[1].start & ~0x3FFF; + ivideo.vga_base = (pdev->resource[2].start & 0xFFFFFC) + 0x30; + rom_base = 0x000C0000; + + request_region(rom_base, 32, "sisfb"); + + /* set passwd */ + vgawb(SEQ_ADR, IND_SIS_PASSWORD); + vgawb(SEQ_DATA, SIS_PASSWORD); + + /* Enable MMIO & PCI linear address */ + vgawb(SEQ_ADR, IND_SIS_PCI_ADDRESS_SET); + jTemp = vgarb(SEQ_DATA); + jTemp |= SIS_PCI_ADDR_ENABLE; + jTemp |= SIS_MEM_MAP_IO_ENABLE; + vgawb(SEQ_DATA, jTemp); + + /* get video ram size by SR14 */ + vgawb(SEQ_ADR, IND_SIS_DRAM_SIZE); + ivideo.video_size = ((int) ((vgarb(SEQ_DATA) & 0x3f) + 1) << 20); + + if (mode_idx < 0) + mode_idx = DEFAULT_MODE; /* 0:640x480x8 */ + + mode_no = sisbios_mode[mode_idx].mode_no; + + if (ivideo.refresh_rate != 0) + search_refresh_rate(ivideo.refresh_rate); + + if (rate_idx == 0) { + rate_idx = sisbios_mode[mode_idx].rate_idx; /* set to default refresh rate 60MHz */ + ivideo.refresh_rate = 60; + } + + ivideo.video_bpp = sisbios_mode[mode_idx].bpp; + ivideo.video_width = sisbios_mode[mode_idx].xres; + ivideo.video_height = sisbios_mode[mode_idx].yres; + video_linelength = ivideo.video_width * (ivideo.video_bpp >> 3); + + if (!request_mem_region(ivideo.video_base, ivideo.video_size, "sisfb FB")) + { + printk(KERN_ERR + "sisfb: abort, cannot reserve video memory at 0x%lx\n", + ivideo.video_base); + return -1; + } + + if (!request_mem_region(ivideo.mmio_base, MMIO_SIZE, "sisfb MMIO")) + { + printk(KERN_ERR + "sisfb: abort, cannot reserve mmio memory at 0x%lx\n", + ivideo.mmio_base); + return -1; + } + + ivideo.video_vbase = ioremap(ivideo.video_base, ivideo.video_size); + ivideo.mmio_vbase = ioremap(ivideo.mmio_base, MMIO_SIZE); + rom_vbase = (unsigned long) ioremap(rom_base, MAX_ROM_SCAN); + + printk(KERN_INFO + "sisfb: framebuffer at 0x%lx, mapped to 0x%p, size %dk\n", + ivideo.video_base, ivideo.video_vbase, + ivideo.video_size / 1024); + printk(KERN_INFO "sisfb: mode is %dx%dx%d, linelength=%d\n", + ivideo.video_width, ivideo.video_height, ivideo.video_bpp, + video_linelength); + + /* enable 2D engine */ + vgawb(SEQ_ADR, IND_SIS_MODULE_ENABLE); + jTemp = vgarb(SEQ_DATA); + jTemp |= SIS_2D_ENABLE; + vgawb(SEQ_DATA, jTemp); + + pre_setmode(); + + if (SiSSetMode(mode_no)) { + DPRINTK("sisfb: set mode[0x%x]: failed\n", 0x30); + return -1; + } + + post_setmode(); + + crtc_to_var(&default_var); + + fb_info.changevar = NULL; + fb_info.node = -1; + fb_info.fbops = &sisfb_ops; + fb_info.disp = &disp; + fb_info.switch_con = &sisfb_switch; + fb_info.updatevar = &sisfb_update_var; + fb_info.blank = &sisfb_blank; + fb_info.flags = FBINFO_FLAG_DEFAULT; + + sisfb_set_disp(-1, &default_var); + + if (sisfb_heap_init()) { + DPRINTK("sisfb: Failed to enable offscreen heap\n"); + } + + /* to avoid the inversed bgcolor bug of the initial state */ + vc_resize_con(1, 1, 0); + + if (register_framebuffer(&fb_info) < 0) + return -EINVAL; + + ivideo.status = CRT1; + + printk(KERN_INFO "fb%d: %s frame buffer device\n", + GET_FB_IDX(fb_info.node), fb_info.modename); + + return 0; +} + +#ifdef MODULE + +static char *mode = NULL; +static unsigned int rate = 0; + +MODULE_PARM(mode, "s"); +MODULE_PARM(rate, "i"); + +int init_module(void) +{ + if (mode) + search_mode(mode); + + ivideo.refresh_rate = rate; + + sisfb_init(); + + return 0; +} + +void cleanup_module(void) +{ + unregister_framebuffer(&fb_info); +} +#endif /* MODULE */ + + +EXPORT_SYMBOL(sis_malloc); +EXPORT_SYMBOL(sis_free); + +EXPORT_SYMBOL(ivideo); |