summaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-06-16 23:00:36 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-06-16 23:00:36 +0000
commit14dd2ec093cfabda3ae7efeeaf0e23c66ebaccc0 (patch)
tree9a9ce5cff6ef92faa6e07a82785b9a6d6838f7e4 /drivers/video
parent847290510f811c572cc2aa80c1f02a04721410b1 (diff)
Merge with 2.4.0-test1.
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/Config.in14
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/fbmem.c5
-rw-r--r--drivers/video/sisfb.c2982
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);