linux-fbdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] driver for S3 Trio
@ 2006-11-23 23:41 Ondrej Zajicek
  0 siblings, 0 replies; only message in thread
From: Ondrej Zajicek @ 2006-11-23 23:41 UTC (permalink / raw)
  To: linux-fbdev-devel

Hello

This is first draft of (PCI) S3 Trio fbdev driver. There are already
two drivers for S3 Trio chips (cyberfb.c and S3trio.c) in kernel.
Both are broken and targetted to uncommon variants.

Features:
 * 8, 16 and 32 bpp modes
 * interlaced and doublescan modes
 * text mode (activated by bpp = 0), used by tileblit fbcon functions 
 * panning in both direction
 * no support for accelerated bitblit functions
 
I tested it only on S3 Trio32 and S3 Trio64 cards with 1 MB RAM. 
I suppose there will be problems on S3 Trio64 cards with more memory.
If there is anybody willing to test this driver on such card, i will
modified driver ASAP.

Driver is separated to s3fb.c and svgalib.c . In svgalib.c there are
generic functions which can be useful to other drivers for VGA-based
cards. 


Signed-off-by: Ondrej Zajicek <santiago@crfreenet.org>

---

diff -uprN -X linux-2.6.18/Documentation/dontdiff linux-2.6.18/drivers/video/Kconfig linux-2.6.18-feanor/drivers/video/Kconfig
--- linux-2.6.18/drivers/video/Kconfig	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18-feanor/drivers/video/Kconfig	2006-11-11 23:15:44.000000000 +0100
@@ -80,6 +80,14 @@ config FB_CFB_IMAGEBLIT
 	  blitting. This is used by drivers that don't provide their own
 	  (accelerated) version.
 
+config FB_SVGALIB
+	tristate
+	depends on FB
+	default n
+	---help---
+	  Common utility functions useful to fbdev drivers of VGA-based
+	  cards.
+
 config FB_MACMODES
        tristate
        depends on FB
@@ -1303,6 +1311,27 @@ config FB_TRIDENT_ACCEL
 	This will compile the Trident frame buffer device with
 	acceleration functions.
 
+config FB_S3
+	tristate "S3 Trio support"
+	depends on FB && PCI
+	select FB_SVGALIB
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	---help---
+	  Driver for graphics boards with S3 Trio32/Trio64 chip.
+
 config FB_PM3
 	tristate "Permedia3 support"
 	depends on FB && PCI && BROKEN
diff -uprN -X linux-2.6.18/Documentation/dontdiff linux-2.6.18/drivers/video/Makefile linux-2.6.18-feanor/drivers/video/Makefile
--- linux-2.6.18/drivers/video/Makefile	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18-feanor/drivers/video/Makefile	2006-11-11 23:07:05.000000000 +0100
@@ -18,6 +18,7 @@ obj-$(CONFIG_FB_CFB_FILLRECT)  += cfbfil
 obj-$(CONFIG_FB_CFB_COPYAREA)  += cfbcopyarea.o
 obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o
 obj-$(CONFIG_FB_MACMODES)      += macmodes.o
+obj-$(CONFIG_FB_SVGALIB)       += svgalib.o
 
 # Hardware specific drivers go first
 obj-$(CONFIG_FB_RETINAZ3)         += retz3fb.o
@@ -53,6 +54,8 @@ obj-$(CONFIG_FB_S3TRIO)           += S3t
 obj-$(CONFIG_FB_FM2)              += fm2fb.o
 obj-$(CONFIG_FB_CYBLA)            += cyblafb.o
 obj-$(CONFIG_FB_TRIDENT)          += tridentfb.o
+obj-$(CONFIG_FB_S3)               += s3fb.o
 obj-$(CONFIG_FB_STI)              += stifb.o
 obj-$(CONFIG_FB_FFB)              += ffb.o sbuslib.o
 obj-$(CONFIG_FB_CG6)              += cg6.o sbuslib.o
diff -uprN -X linux-2.6.18/Documentation/dontdiff linux-2.6.18/drivers/video/s3fb.c linux-2.6.18-feanor/drivers/video/s3fb.c
--- linux-2.6.18/drivers/video/s3fb.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18-feanor/drivers/video/s3fb.c	2006-11-23 23:24:07.000000000 +0100
@@ -0,0 +1,1001 @@
+/*
+*  linux/drivers/video/s3fb.c -- Frame buffer device driver for S3 Trio32/64
+*
+*  Copyright (c) 2006 Ondrej Zajicek <santiago@crfreenet.org>
+*
+*  This file is subject to the terms and conditions of the GNU General Public                                           
+*  License.  See the file COPYING in the main directory of this archive for                                             
+*  more details.                                                                                                        
+*
+*  Code is based on David Boucher's viafb (http://davesdomain.org.uk/viafb/)
+*  which is based on the code of neofb.
+*/
+
+#include <linux/version.h>
+#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/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/svga.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/console.h> /* Why should fb driver call console functions? strange ...*/
+#include <video/vga.h>
+
+
+struct s3fb_info {
+	struct fb_info fb;
+
+	int chip, rev, mclk_freq;
+	struct vgastate state;
+	atomic_t ref_count;
+	u32 pseudo_palette[16];
+};
+
+
+/* ------------------------------------------------------------------------- */
+
+struct svga_fb_format s3fb_formats[] = {{ 8,  {0, 8, 0},  {0, 8, 0},  {0, 8, 0}, {0, 0, 0}, 0,
+						FB_TYPE_PACKED_PIXELS,	0, FB_VISUAL_PSEUDOCOLOR, 4, 8},
+					{16,  {10, 5, 0}, {5, 5, 0},  {0, 5, 0}, {0, 0, 0}, 0,
+						FB_TYPE_PACKED_PIXELS,	0, FB_VISUAL_TRUECOLOR, 2, 4},
+					{16,  {11, 5, 0}, {5, 6, 0},  {0, 5, 0}, {0, 0, 0}, 0,
+						FB_TYPE_PACKED_PIXELS,	0, FB_VISUAL_TRUECOLOR, 2, 4},
+					{24,  {16, 8, 0}, {8, 8, 0},  {0, 8, 0}, {0, 0, 0}, 0,
+						FB_TYPE_PACKED_PIXELS,	0, FB_VISUAL_TRUECOLOR, 4, 8},
+					{32,  {16, 8, 0}, {8, 8, 0},  {0, 8, 0}, {0, 0, 0}, 0,
+						FB_TYPE_PACKED_PIXELS,	0, FB_VISUAL_TRUECOLOR, 1, 2},
+					{ 0,  {0, 8, 0},  {0, 8, 0},  {0, 8, 0}, {0, 0, 0}, 0,
+						FB_TYPE_TEXT,		5, FB_VISUAL_PSEUDOCOLOR, 8, 16},
+					SVGA_FORMAT_END};
+					
+struct svga_pll s3_pll = {3, 129, 3, 33, 0, 3,
+			  60000, 240000, 14318};
+
+int s3_memsizes[] = {4096, 0, 3072, 8192, 2048, 6144, 1024, 512};
+
+const char * s3_names[] = {"S3 Unknown", "S3 Trio32", "S3 Trio64", "S3 Trio64V+",
+			"S3 Trio64UV+", "S3 Trio64V2/DX", "S3 Trio64V2/GX",
+			"S3 Plato/PX", "S3 Aurora64VP"};
+
+#define CHIP_UNKNOWN		0x00
+#define CHIP_732_TRIO32		0x01
+#define CHIP_764_TRIO64		0x02
+#define CHIP_765_TRIO64VP	0x03
+#define CHIP_767_TRIO64UVP	0x04
+#define CHIP_775_TRIO64V2_DX	0x05
+#define CHIP_785_TRIO64V2_GX	0x06
+#define CHIP_551_PLATO_PX	0x07
+#define CHIP_M65_AURORA64VP	0x08
+
+#define CHIP_XXX_TRIO		0x80
+#define CHIP_XXX_TRIO64V2	0x81
+
+#define CHIP_UNDECIDED_FLAG	0x80
+#define CHIP_MASK		0xFF
+
+/* CRT timing register sets */
+
+struct vga_regset s3_h_total_regs[]        = {{0x00, 0, 7}, {0x5D, 0, 0}, VGA_REGSET_END};
+struct vga_regset s3_h_display_regs[]      = {{0x01, 0, 7}, {0x5D, 1, 1}, VGA_REGSET_END};
+struct vga_regset s3_h_blank_start_regs[]  = {{0x02, 0, 7}, {0x5D, 2, 2}, VGA_REGSET_END};
+struct vga_regset s3_h_blank_end_regs[]    = {{0x03, 0, 4}, {0x05, 7, 7}, VGA_REGSET_END};
+struct vga_regset s3_h_sync_start_regs[]   = {{0x04, 0, 7}, {0x5D, 4, 4}, VGA_REGSET_END};
+struct vga_regset s3_h_sync_end_regs[]     = {{0x05, 0, 4}, VGA_REGSET_END};
+
+struct vga_regset s3_v_total_regs[]        = {{0x06, 0, 7}, {0x07, 0, 0}, {0x07, 5, 5}, {0x5E, 0, 0}, VGA_REGSET_END};
+struct vga_regset s3_v_display_regs[]      = {{0x12, 0, 7}, {0x07, 1, 1}, {0x07, 6, 6}, {0x5E, 1, 1}, VGA_REGSET_END};
+struct vga_regset s3_v_blank_start_regs[]  = {{0x15, 0, 7}, {0x07, 3, 3}, {0x09, 5, 5}, {0x5E, 2, 2}, VGA_REGSET_END};
+// struct vga_regset s3_v_blank_end_regs[]    = {{0x16, 0, 6}, VGA_REGSET_END};
+struct vga_regset s3_v_blank_end_regs[]    = {{0x16, 0, 7}, VGA_REGSET_END};
+struct vga_regset s3_v_sync_start_regs[]   = {{0x10, 0, 7}, {0x07, 2, 2}, {0x07, 7, 7}, {0x5E, 4, 4}, VGA_REGSET_END};
+struct vga_regset s3_v_sync_end_regs[]     = {{0x11, 0, 3}, VGA_REGSET_END};
+
+struct vga_regset s3_line_compare_regs[]   = {{0x18, 0, 7}, {0x07, 4, 4}, {0x09, 6, 6}, {0x5E, 6, 6}, VGA_REGSET_END};
+struct vga_regset s3_start_address_regs[]  = {{0x0d, 0, 7}, {0x0c, 0, 7}, {0x31, 4, 5}, {0x51, 0, 1}, VGA_REGSET_END};
+struct vga_regset s3_offset_regs[]         = {{0x13, 0, 7}, {0x51, 4, 5}, VGA_REGSET_END}; /* set 0x43 bit 2 to 0 */
+
+struct svga_timing_regs s3_timing_regs     = {
+	s3_h_total_regs, s3_h_display_regs, s3_h_blank_start_regs,
+	s3_h_blank_end_regs, s3_h_sync_start_regs, s3_h_sync_end_regs,
+	s3_v_total_regs, s3_v_display_regs, s3_v_blank_start_regs,
+	s3_v_blank_end_regs, s3_v_sync_start_regs, s3_v_sync_end_regs,
+};
+
+
+/* ------------------------------------------------------------------------- */
+
+/* Module parameters */
+
+
+static char *mode __devinitdata = "640x480-8@60";
+
+#ifdef MODULE
+
+MODULE_AUTHOR("(c) 2006 Ondrej Zajicek <santiago@crfreenet.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("fbdev driver for S3 Trio32/64");
+
+module_param(mode, charp, 0);
+MODULE_PARM_DESC(mode, "Preferred video mode ('640x480-8@60', etc)");
+
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+/* Set font in text (tileblit) mode */
+
+void s3fb_settile(struct fb_info *info, struct fb_tilemap *map) {
+	const u8 *font = map->data;
+	u8* fb = (u8 *) info->screen_base;
+	int i, c;
+
+	if ((map->width != 8) || (map->height != 16) ||
+	    (map->depth != 1) || (map->length != 256)) {
+	    	printk(KERN_ERR "fb%d: unsupported font parameters: width %d, height %d, depth %d, length %d\n",
+			map->width, map->height, map->depth, map->length, info->node);
+		return;
+	}
+
+	fb += 2;
+	for (c = 0; c < map->length; c++) {
+		for (i = 0; i < map->height; i++) {
+			fb[i * 4] = font[i];
+		}
+		fb += 128;
+		font += map->height;
+	}
+}
+
+/* Copy area in text (tileblit) mode */
+
+void s3fb_tilecopy(struct fb_info *info, struct fb_tilearea *area) {
+	int dx, dy;
+//	int colstride = 4;
+	int colstride = 2;
+	int rowstride = colstride * (info->var.xres_virtual / 8);
+	u16 *fb = (u16 *) info->screen_base;
+	u16 *src, *dst;
+	
+	if ((area->sy > area->dy) ||
+	    ((area->sy == area->dy) && (area->sx > area->dx))) {
+		src = fb + area->sx * colstride + area->sy * rowstride;
+		dst = fb + area->dx * colstride + area->dy * rowstride;
+	    } else {
+		src = fb + (area->sx + area->width - 1) * colstride
+			 + (area->sy + area->height - 1) * rowstride;
+		dst = fb + (area->dx + area->width - 1) * colstride
+			 + (area->dy + area->height - 1) * rowstride;
+
+		colstride = -colstride;
+		rowstride = -rowstride;
+	    }
+			  
+	for (dy = 0; dy < area->height; dy++) {
+		u16* src2 = src;
+		u16* dst2 = dst;
+		for (dx = 0; dx < area->width; dx++) {
+			*dst2 = *src2;
+			src2 += colstride;
+			dst2 += colstride;
+		}
+		src += rowstride;
+		dst += rowstride;
+	}
+}
+
+/* Fill area in text (tileblit) mode */
+
+void s3fb_tilefill(struct fb_info *info, struct fb_tilerect *rect) {
+	int dx, dy;
+//	int colstride = 8;
+	int colstride = 4;
+	int rowstride = colstride * (info->var.xres_virtual / 8);
+	int attr = (0x0F & rect->bg) << 4 | (0x0F & rect->fg);
+	u8  *fb = (u8 *) info->screen_base;
+	fb += rect->sx * colstride + rect->sy * rowstride;
+
+	for (dy = 0; dy < rect->height; dy++) {
+		u8* fb2 = fb;
+		for (dx = 0; dx < rect->width; dx++) {
+			fb2[0] = rect->index;
+			fb2[1] = attr;
+			fb2 += colstride;
+		}
+		fb += rowstride;
+	}
+}
+
+/* Write text in text (tileblit) mode */
+
+void s3fb_tileblit(struct fb_info *info, struct fb_tileblit *blit) {
+	int dx, dy, i;
+//	int colstride = 8;
+	int colstride = 4;
+	int rowstride = colstride * (info->var.xres_virtual / 8);
+	int attr = (0x0F & blit->bg) << 4 | (0x0F & blit->fg);
+	u8* fb = (u8 *) info->screen_base;
+	fb += blit->sx * colstride + blit->sy * rowstride;
+
+	i=0;
+	for (dy=0; dy < blit->height; dy ++) {
+		u8* fb2 = fb;
+		for (dx = 0; dx < blit->width; dx ++) {
+			fb2[0] = blit->indices[i];
+			fb2[1] = attr;
+			fb2 += colstride;
+			i ++;
+			if (i == blit->length) return;
+		}
+		fb += rowstride;
+	}
+
+}
+
+/* Set cursor in text (tileblit) mode */
+
+void s3fb_tilecursor(struct fb_info *info, struct fb_tilecursor *cursor) {
+	u8 cs = 0x0d;
+	u8 ce = 0x0e;
+	u16 pos =  cursor->sx + (info->var.xoffset /  8)
+		+ (cursor->sy + (info->var.yoffset / 16))
+		   * (info->var.xres_virtual / 8);
+
+	if (! cursor -> mode)
+		return;
+
+	svga_wcrt_mask(0x0A, 0x20, 0x20); /* disable cursor */
+
+	if (cursor -> shape == FB_TILE_CURSOR_NONE)
+		return;
+
+	switch (cursor -> shape)
+	{
+		case FB_TILE_CURSOR_UNDERLINE:
+			cs = 0x0d;
+			break;
+		case FB_TILE_CURSOR_LOWER_THIRD:
+			cs = 0x09;
+			break;
+		case FB_TILE_CURSOR_LOWER_HALF:
+			cs = 0x07;
+			break;
+		case FB_TILE_CURSOR_TWO_THIRDS:
+			cs = 0x05;
+			break;
+		case FB_TILE_CURSOR_BLOCK:
+			cs = 0x01;
+			break;
+	}
+
+	/* set cursor position */
+	vga_wcrt(NULL, 0x0E, pos >> 8); 
+	vga_wcrt(NULL, 0x0F, pos & 0xFF);
+
+	vga_wcrt(NULL, 0x0B, ce); /* set cursor end */
+	vga_wcrt(NULL, 0x0A, cs); /* set cursor start and enable it */
+}
+
+
+static struct fb_tile_ops s3fb_tile_ops = {
+	.fb_settile	= s3fb_settile,
+	.fb_tilecopy	= s3fb_tilecopy,
+	.fb_tilefill    = s3fb_tilefill,
+	.fb_tileblit    = s3fb_tileblit,
+	.fb_tilecursor  = s3fb_tilecursor,
+};
+
+
+/* ------------------------------------------------------------------------- */
+
+
+void s3_set_pixclock(struct fb_info *info, u32 pixclock)
+{
+	u16 m, n, r;
+	u8 regval;
+	
+	svga_compute_pll(&s3_pll, 1000000000 / pixclock, &m, &n, &r, info->node);
+
+	/* Set VGA misc register  */
+	regval = vga_r(NULL, VGA_MIS_R); 
+	vga_w(NULL, VGA_MIS_W, regval | VGA_MIS_ENB_PLL_LOAD);
+
+	/* Set S3 clock registers */
+	vga_wseq(NULL, 0x12, ((n - 2) | (r << 5)));
+	vga_wseq(NULL, 0x13, m - 2);
+	
+	udelay(1000);
+
+	/* Activate clock - write 0, 1, 0 to seq/15 bit 5 */
+	regval = vga_rseq (NULL, 0x15);
+	vga_wseq(NULL, 0x15, regval & ~(1<<5));
+	vga_wseq(NULL, 0x15, regval |  (1<<5));
+	vga_wseq(NULL, 0x15, regval & ~(1<<5));
+}
+
+
+/* Open framebuffer */
+
+static int s3fb_open(struct fb_info *info, int user)
+{
+	struct s3fb_info *par = (struct s3fb_info *) info;
+	unsigned int count = atomic_read(&(par->ref_count));
+	
+	if (!count) {
+		memset(&(par->state), 0, sizeof(struct vgastate));
+		par->state.flags = VGA_SAVE_MODE | VGA_SAVE_FONTS | VGA_SAVE_CMAP;
+		par->state.num_crtc = 0x70;
+		par->state.num_seq = 0x20;
+		save_vga(&(par->state));
+	}
+
+	atomic_inc(&(par->ref_count));
+
+	return 0;
+}
+
+/* Close framebuffer */
+
+static int s3fb_release(struct fb_info *info, int user)
+{
+	struct s3fb_info *par = (struct s3fb_info *) info;
+	unsigned int count = atomic_read(&(par->ref_count));
+
+	if (!count)
+		return -EINVAL;
+		
+	if (count == 1)
+		restore_vga(&(par->state));
+
+	atomic_dec(&(par->ref_count));
+	
+	return 0;
+}
+
+/* Validate passed in var */
+
+static int s3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	int rv, mem, step;
+
+	/* Find appropriate format */
+	rv = svga_match_format (s3fb_formats, var, NULL);
+	if (rv < 0)
+	{
+		printk(KERN_ERR "fb%d: unsupported mode requested\n", info->node);
+		return rv;
+	}
+
+	/* Do not allow to have real resoulution larger than virtual */
+	if (var->xres > var->xres_virtual)
+		var->xres_virtual = var->xres;
+
+	if (var->yres > var->yres_virtual)
+		var->yres_virtual = var->yres;
+
+	/* Round up xres_virtual to have proper alignment of lines */
+	step = s3fb_formats[rv].xresstep - 1;
+	var->xres_virtual = (var->xres_virtual+step) & ~step;
+
+	/* Check whether have enough memory */
+	mem = ((var->bits_per_pixel * var->xres_virtual) >> 3) * var->yres_virtual; 
+	if (mem > info->screen_size)
+	{
+		printk(KERN_ERR "fb%d: not enough framebuffer memory (%d kB requested , %d kB available)\n", info->node, mem >> 10, (unsigned int) (info->screen_size >> 10));
+		return -EINVAL;
+	}
+
+	rv = svga_check_timings (&s3_timing_regs, var, info->node);
+	if (rv < 0)
+	{
+		printk(KERN_ERR "fb%d: invalid timings requested\n", info->node);
+		return rv;
+	}
+
+	return 0;
+}
+
+/* Set video mode from par */
+
+static int s3fb_set_par(struct fb_info *info)
+{
+	u32 value, mode, hmul, offset_value, screen_size;
+	u32 bpp = info->var.bits_per_pixel;
+
+	if (bpp != 0) {
+		info->fix.ypanstep = 1;
+		info->fix.line_length = (info->var.xres_virtual * bpp) / 8;
+
+		info->flags &= ~FBINFO_MISC_TILEBLITTING; 
+		info->tileops = NULL;
+
+		offset_value = (info->var.xres_virtual * bpp) / 64;
+		screen_size = info->var.yres_virtual * info->fix.line_length;
+	} else {
+		info->fix.ypanstep = 16;
+		info->fix.line_length = 0;
+
+		info->flags |= FBINFO_MISC_TILEBLITTING; 
+		info->tileops = &s3fb_tile_ops;
+
+		offset_value = info->var.xres_virtual / 16;
+		screen_size = (info->var.xres_virtual * info->var.yres_virtual) / 64;
+		// FIXME test screen_size
+	}
+
+	info->var.xoffset = 0;
+	info->var.yoffset = 0;
+	info->var.activate = FB_ACTIVATE_NOW;
+
+	/* Unlock registers */
+	vga_wcrt(NULL, 0x38, 0x48);
+	vga_wcrt(NULL, 0x39, 0xA5);
+	vga_wseq(NULL, 0x08, 0x06);
+	svga_wcrt_mask(0x11, 0x00, 0x80);
+
+/*
+	printk(KERN_INFO "SEQ[0x10] = %x, SEQ[0x11] = %x\n",
+		vga_rseq(NULL, 0x10), vga_rseq(NULL, 0x11));
+
+{
+	int mclk_freq;
+	u8 regval = vga_rseq(NULL, 0x10);
+	mclk_freq = ((vga_rseq(NULL, 0x11) + 2) * 14318) / ((regval & 0x1F)  + 2);
+	mclk_freq = mclk_freq >> (regval >> 5);
+	printk(KERN_INFO "fb%d: %d MHz MCLK\n", info->node, (mclk_freq + 500) / 1000);
+}
+*/
+		
+	/* Blank screen and turn off sync */
+	svga_wseq_mask(0x01, 0x20, 0x20); 
+	svga_wcrt_mask(0x17, 0x00, 0x80);
+
+	/* Set default values */	
+	svga_set_default_gfx_regs();
+	svga_set_default_atc_regs();
+	svga_set_default_seq_regs();
+	svga_set_default_crt_regs();
+	svga_wcrt_multi(s3_line_compare_regs, 0xFFFFFFFF);
+	svga_wcrt_multi(s3_start_address_regs, 0);
+	
+	/* S3 specific initialization */
+	svga_wcrt_mask(0x58, 0x10, 0x10); /* enable linear framebuffer */
+	svga_wcrt_mask(0x31, 0x08, 0x08); /* enable sequencer access to framebuffer above 256 kB */
+
+	svga_wcrt_mask(0x33, 0x08, 0x08); // DDR ?
+	svga_wcrt_mask(0x43, 0x01, 0x01); // DDR ?
+
+
+//	svga_wcrt_mask(0x58, 0x03, 0x03); /* XXX */
+	
+//	svga_wcrt_mask(0x53, 0x12, 0x13); /* enable MMIO */
+//	svga_wcrt_mask(0x40, 0x08, 0x08); /* enable write buffer */
+
+//	pr_debug "fb%d: MCLK reg values %x %x\n", info->node,
+//			  vga_rseq(NULL, 0x10), vga_rseq(NULL, 0x11));
+
+
+
+	/* Set the offset register */
+	pr_debug("fb%d: offset register       : %d\n", info->node, offset_value);
+	svga_wcrt_multi(s3_offset_regs, offset_value);
+
+		/* Set the fetch count register */
+/*		value = (info->var.xres / 8) + 8;
+		pr_debug( "viafb: fetch count register  : %d\n", value);
+		via_wseq_multi(via_fetch_count_regs, value);
+*/
+
+
+//	if (info->var.nonstd != 0) vga_wcrt(NULL, 0x67, info->var.nonstd);
+//	vga_wcrt(NULL, 0x54, info->var.nonstd << 3); // M parameter 0x18
+	vga_wcrt(NULL, 0x54, 0x18); // M parameter 0x18
+	vga_wcrt(NULL, 0x60, 0xff); // N parameter
+	vga_wcrt(NULL, 0x61, 0xff); // L parameter
+	vga_wcrt(NULL, 0x62, 0xff); // L parameter
+
+	vga_wcrt(NULL, 0x3A, 0x35); 
+	svga_wattr(0x33, 0x00);
+	
+	if (info->var.vmode & FB_VMODE_DOUBLE)
+		svga_wcrt_mask(0x09, 0x80, 0x80);
+	else
+		svga_wcrt_mask(0x09, 0x00, 0x80);
+
+	if (info->var.vmode & FB_VMODE_INTERLACED)
+		svga_wcrt_mask(0x42, 0x20, 0x20);
+	else
+		svga_wcrt_mask(0x42, 0x00, 0x20);
+
+	/* Set mode-specific register values */
+	mode = svga_match_format(s3fb_formats, &(info->var), &(info->fix));
+	switch (mode) {
+		case 0:
+			pr_debug("fb%d: 8 bit pseudocolor\n", info->node);
+			svga_wcrt_mask(0x50, 0x00, 0x30);	
+			svga_wcrt_mask(0x67, 0x00, 0xF0);
+			hmul = 1;
+		break;
+		case 1: 
+			pr_debug("fb%d: 5/5/5 truecolor\n", info->node);
+			svga_wcrt_mask(0x50, 0x10, 0x30);
+			svga_wcrt_mask(0x67, 0x30, 0xF0);
+			hmul = 2;
+		break;
+		case 2: 
+			pr_debug("fb%d: 5/6/5 truecolor\n", info->node);
+			svga_wcrt_mask(0x50, 0x10, 0x30);
+			svga_wcrt_mask(0x67, 0x50, 0xF0);
+			hmul = 2;
+		break;
+		case 3:
+			pr_debug("fb%d: 8/8/8 truecolor X\n", info->node);
+//			svga_wcrt_mask(0x50, 0x10, 0x30);
+			svga_wcrt_mask(0x50, 0x30, 0x30);
+			svga_wcrt_mask(0x67, 0x70, 0xF0);
+			hmul = 2;
+		break;
+		case 4:
+			pr_debug("fb%d: 8/8/8 truecolor\n", info->node);
+			svga_wcrt_mask(0x50, 0x30, 0x30);
+			svga_wcrt_mask(0x67, 0xD0, 0xF0);
+			hmul = 1;
+		break;
+		case 5:
+			pr_debug("fb%d: text mode\n", info->node);
+			svga_set_textmode_vga_regs();
+			/* Set additional registers like in 8-bit mode */
+			svga_wcrt_mask(0x50, 0x00, 0x30);	
+			svga_wcrt_mask(0x67, 0x00, 0xF0);
+			/* Disable enhanced mode */
+			svga_wcrt_mask(0x3A, 0x00, 0x30);	
+			hmul = 1;
+		break;
+		default:
+			printk(KERN_ERR "fb%d: unsupported mode - bug\n", info->node);
+			return -EINVAL;
+	}
+
+	s3_set_pixclock(info, info->var.pixclock);
+	svga_set_timings(&s3_timing_regs, &(info->var), hmul, 1,
+			 (info->var.vmode & FB_VMODE_DOUBLE)     ? 2 : 1,
+			 (info->var.vmode & FB_VMODE_INTERLACED) ? 2 : 1,
+			 info->node);
+
+	/* Set interlaced mode start/end register */
+	value = info->var.xres + info->var.left_margin + info->var.right_margin + info->var.hsync_len;
+	value = ((value * hmul) / 8) - 5;
+	vga_wcrt(NULL, 0x3C, (value + 1) / 2);
+			 
+	memset((u8*)info->screen_base, 0x00, screen_size);
+
+	/* Device and screen back on */
+	svga_wcrt_mask(0x17, 0x80, 0x80);
+	svga_wseq_mask(0x01, 0x00, 0x20); 
+
+	return 0;
+}
+
+/* Set a colour register */
+
+static int s3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+	u_int transp, struct fb_info *fb) {
+
+	switch (fb->var.bits_per_pixel) {
+		case 8:
+			if (regno >= 256) return -EINVAL;
+			outb(0xFF, VGA_PEL_MSK);
+			outb(regno, VGA_PEL_IW);
+			outb(red >> 10, VGA_PEL_D);
+			outb(green >> 10, VGA_PEL_D);
+			outb(blue >> 10, VGA_PEL_D);
+			if (regno < 16)
+				((u32*)fb->pseudo_palette)[regno] = ((blue & 0xFF00) >> 8) | 
+					(green & 0xFF00) | ((red & 0xFF00) << 8);
+		break;
+		case 16:
+			if (regno >= 16) return -EINVAL;
+			if (fb->var.green.length == 5)
+				((u32*)fb->pseudo_palette)[regno] = ((red & 0xF800) >> 1) | ((green & 0xF800) >> 6) | ((blue & 0xF800) >> 11);
+			else if (fb->var.green.length == 6)
+				((u32*)fb->pseudo_palette)[regno] = (red & 0xF800) | ((green & 0xFC00) >> 5) | ((blue & 0xF800) >> 11);
+			else return -EINVAL;
+		break;
+
+		case 24:
+		case 32:
+			if (regno >= 16) return -EINVAL;
+			((u32*)fb->pseudo_palette)[regno] = ((transp & 0xFF00) >> 16 ) | ((blue & 0xFF00) >> 8) | 
+				(green & 0xFF00) | ((red & 0xFF00) << 8);
+		break;
+		default:
+			return -EINVAL;
+	}
+	
+	return 0;
+}
+
+/* Set the display blanking state */
+
+static int s3fb_blank(int blank_mode, struct fb_info *info)
+{
+	switch (blank_mode) {
+		case FB_BLANK_UNBLANK:
+			svga_wcrt_mask(0x17, 0x80, 0x80);
+			svga_wseq_mask(0x01, 0x00, 0x20);
+
+		break;
+		case FB_BLANK_HSYNC_SUSPEND:
+			printk(KERN_INFO "fb%d: DPMS standby not implemented, blanking used instead\n", info->node);
+		case FB_BLANK_NORMAL:
+			svga_wseq_mask(0x01, 0x20, 0x20); 
+
+		break;
+		case FB_BLANK_VSYNC_SUSPEND:
+			printk(KERN_INFO "fb%d: DPMS suspend not implemented, DPMS off used instead\n", info->node);
+		case FB_BLANK_POWERDOWN:
+			svga_wseq_mask(0x01, 0x20, 0x20); 
+			svga_wcrt_mask(0x17, 0x00, 0x80);
+		break;
+	}
+
+	return 0;
+}
+
+/* Pan the display */
+
+static int s3fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) {
+
+	unsigned int offset;
+
+	/* Validate the offsets - At the moment, only the Y offset can be changed */
+	if ((var->xoffset + var->xres) > var->xres_virtual) return -EINVAL;
+	if ((var->yoffset + var->yres) > var->yres_virtual) return -EINVAL;
+
+	/* Calculate the offset */
+	if (var->bits_per_pixel == 0) {
+		offset = (var->yoffset / 16) * (var->xres_virtual / 2) + (var->xoffset / 2);
+		offset = offset >> 2;
+	} else {
+		offset = (var->yoffset * info->fix.line_length) +
+			 (var->xoffset * var->bits_per_pixel / 8);
+		offset = offset >> 2;
+	}
+
+	/* Set the offset */
+	svga_wcrt_multi(s3_start_address_regs, offset);
+	
+	return 0;
+}
+
+
+
+/* ------------------------------------------------------------------------- */
+
+/* Frame buffer operations */
+
+static struct fb_ops s3fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_open	= s3fb_open,
+	.fb_release	= s3fb_release,
+	.fb_check_var	= s3fb_check_var,
+	.fb_set_par	= s3fb_set_par,
+	.fb_setcolreg	= s3fb_setcolreg,
+	.fb_blank	= s3fb_blank,
+	.fb_pan_display	= s3fb_pan_display,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+};
+
+/* ------------------------------------------------------------------------- */
+
+static int __devinit s3_identification(int chip)
+{
+	if (chip == CHIP_XXX_TRIO) {
+		u8 cr30 = vga_rcrt(NULL, 0x30);
+		u8 cr2e = vga_rcrt(NULL, 0x2e);
+		u8 cr2f = vga_rcrt(NULL, 0x2f);
+
+		if ((cr30 == 0xE0) || (cr30 == 0xE1)) {
+			if (cr2e == 0x10)
+				return CHIP_732_TRIO32;
+			if (cr2e == 0x11) {
+				if (! (cr2f & 0x40))
+					return CHIP_764_TRIO64;
+				else
+					return CHIP_765_TRIO64VP;
+			}
+		}
+	}
+
+	if (chip == CHIP_XXX_TRIO64V2) {
+		u8 cr6f = vga_rcrt(NULL, 0x6f);
+
+		if (! (cr6f & 0x01))
+			return CHIP_775_TRIO64V2_DX;
+		else
+			return CHIP_785_TRIO64V2_GX;
+	}
+
+	return CHIP_UNKNOWN;
+}
+
+/* PCI probe */
+
+static int __devinit s3_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct fb_info *info;
+	struct s3fb_info *par;
+	int rc;
+	u8 regval, cr38, cr39;
+
+	/* Allocate and fill driver data structure */
+	
+	info = framebuffer_alloc(sizeof(struct s3fb_info), NULL);
+	if (!info) return -ENOMEM;
+	par = (struct s3fb_info*) info;
+
+	info->flags = FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_YPAN;
+	info->fbops = &s3fb_ops;
+
+	/* Prepare PCI device */
+
+	rc = pci_enable_device(dev);
+	if (rc < 0) {
+		printk(KERN_ERR "s3fb: cannot enable PCI device\n");
+		goto err_enable_device;
+	}
+
+	rc = pci_request_regions(dev, "s3fb");
+	if (rc < 0) {
+		printk(KERN_ERR "s3fb: cannot reserve framebuffer region\n");
+		goto err_request_regions;
+	}
+
+	/* Map physical IO memory address into kernel space */
+
+	info->fix.smem_start = pci_resource_start(dev, 0);
+	info->fix.smem_len = pci_resource_len(dev, 0);
+
+	info->screen_base = ioremap_nocache(info->fix.smem_start, info->fix.smem_len);
+	if (! info->screen_base) {
+		rc = -ENOMEM;
+		printk(KERN_ERR "s3fb: ioremap for framebuffer failed\n");
+		goto err_ioremap;
+	}
+
+	/* Unlock regs */
+	cr38 = vga_rcrt(NULL, 0x38);
+	cr39 = vga_rcrt(NULL, 0x39);
+	vga_wseq(NULL, 0x08, 0x06);
+	vga_wcrt(NULL, 0x38, 0x48);
+	vga_wcrt(NULL, 0x39, 0xA5);
+
+	/* Find how many physical memory there is on card */
+	/* 0x36 register is accessible even if other registers are locked */
+	regval = vga_rcrt(NULL, 0x36);
+	info->screen_size = s3_memsizes[regval >> 5] << 10;
+	info->fix.smem_len = info->screen_size;
+
+	par->chip = id->driver_data & CHIP_MASK;
+	par->rev = vga_rcrt(NULL, 0x2f);
+	if (par->chip & CHIP_UNDECIDED_FLAG)
+		par->chip = s3_identification(par->chip);
+
+//	par->chip = (par->chip & 0x07);
+
+	/* Find MCLK frequency */
+	regval = vga_rseq(NULL, 0x10);
+	par->mclk_freq = ((vga_rseq(NULL, 0x11) + 2) * 14318) / ((regval & 0x1F)  + 2);
+	par->mclk_freq = par->mclk_freq >> (regval >> 5);
+
+	/* Restore locks */ 
+	vga_wcrt(NULL, 0x38, cr38);
+	vga_wcrt(NULL, 0x39, cr39);
+
+	strcpy(info->fix.id, s3_names [par->chip]);
+	info->fix.mmio_start = 0;
+	info->fix.mmio_len = 0;
+	info->fix.type = FB_TYPE_PACKED_PIXELS;
+	info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+	info->fix.ypanstep = 0;
+	info->fix.accel = FB_ACCEL_NONE;
+	info->pseudo_palette = (void*) (par->pseudo_palette);
+
+	/* Prepare startup mode */
+
+	rc = fb_find_mode(&(info->var), info, mode, NULL, 0, NULL, 8);
+	if (! ((rc == 1) || (rc == 2))) {
+		rc = -EINVAL;
+		printk(KERN_ERR "s3fb: mode %s not found\n", mode);
+		goto err_find_mode;
+	}
+
+	rc = fb_alloc_cmap(&info->cmap, 256, 0);
+	if (rc < 0) goto err_alloc_cmap;
+
+	rc = register_framebuffer(info);
+	if (rc < 0) goto err_reg_fb;
+
+	printk(KERN_INFO "fb%d: %s on %s, %d MB RAM, %d MHz MCLK\n", info->node, info->fix.id,
+		 pci_name(dev), info->fix.smem_len >> 20, (par->mclk_freq + 500) / 1000);
+
+//	if (par->chip == CHIP_UNKNOWN)
+		printk(KERN_INFO "fb%d: unknown chip, CR2D=%x, CR2E=%x, CRT2F=%x, CRT30=%x\n",
+			info->node, vga_rcrt(NULL, 0x2d), vga_rcrt(NULL, 0x2e),
+			vga_rcrt(NULL, 0x2f), vga_rcrt(NULL, 0x30));
+
+	/* Record a reference to the driver data */
+	pci_set_drvdata(dev, info);
+
+	return 0;
+
+	/* Error handling */
+err_reg_fb:
+	fb_dealloc_cmap(&info->cmap);
+err_alloc_cmap:
+err_find_mode:
+	iounmap(info->screen_base);
+err_ioremap:
+	pci_release_regions(dev);
+err_request_regions:
+/*	pci_disable_device(dev); */
+err_enable_device:
+	framebuffer_release(info);
+	return rc;
+}
+
+/* PCI remove */
+
+static void __devexit s3_pci_remove(struct pci_dev *dev)
+{
+	struct fb_info *info = pci_get_drvdata(dev);
+
+	if (info) {
+		unregister_framebuffer(info);
+		fb_dealloc_cmap(&info->cmap);
+
+		iounmap(info->screen_base);
+		pci_release_regions(dev);
+/*		pci_disable_device(dev); */
+
+		pci_set_drvdata(dev, NULL);
+		framebuffer_release(info);
+	}
+}
+
+/* PCI suspend */
+
+/*
+static int s3_pci_suspend (struct pci_dev* dev, pm_message_t state)
+{
+	struct fb_info *info = pci_get_drvdata(dev);
+	unsigned int count = atomic_read(&(((struct s3_info*)info)->par.ref_count));
+
+	printk(KERN_INFO "fb%d: suspend\n");
+
+	if ((state.event == PM_EVENT_FREEZE) || (!count)) {
+		return 0;
+	}
+
+	acquire_console_sem();
+	fb_set_suspend(info, 1);
+
+	pci_save_state(dev);
+	pci_disable_device(dev);
+	pci_set_power_state(dev, pci_choose_state(dev, state));
+	release_console_sem();
+
+	return 0;
+}
+*/
+
+/* PCI resume */
+/*
+static int s3_pci_resume (struct pci_dev* dev)
+{
+	struct fb_info *info = pci_get_drvdata(dev);
+	unsigned int count = atomic_read(&(((struct s3_info*)info)->par.ref_count));
+
+	printk(KERN_INFO "fb%d: resume\n");
+
+	if (!count) {
+		return 0;
+	}
+
+	acquire_console_sem();
+	pci_set_power_state(dev, PCI_D0);
+	pci_restore_state(dev);
+	pci_enable_device(dev);
+	pci_set_master(dev);
+
+	s3fb_set_par (info);
+	fb_set_suspend (info, 0);
+	release_console_sem();
+
+	return 0;
+}
+*/
+
+/* List of boards that we are trying to support */
+
+static struct pci_device_id s3_devices[] __devinitdata = {
+	{PCI_VENDOR_ID_S3,	0x8810,		PCI_ANY_ID,PCI_ANY_ID,0,0, CHIP_XXX_TRIO},
+	{PCI_VENDOR_ID_S3,	0x8811,		PCI_ANY_ID,PCI_ANY_ID,0,0, CHIP_XXX_TRIO},
+	{PCI_VENDOR_ID_S3,	0x8812,		PCI_ANY_ID,PCI_ANY_ID,0,0, CHIP_M65_AURORA64VP},
+	{PCI_VENDOR_ID_S3,	0x8814,		PCI_ANY_ID,PCI_ANY_ID,0,0, CHIP_767_TRIO64UVP},
+	{PCI_VENDOR_ID_S3,	0x8901,		PCI_ANY_ID,PCI_ANY_ID,0,0, CHIP_XXX_TRIO64V2},
+	{PCI_VENDOR_ID_S3,	0x8902,		PCI_ANY_ID,PCI_ANY_ID,0,0, CHIP_551_PLATO_PX},
+	{0, 0, 0, 0, 0, 0, 0}
+};
+
+
+MODULE_DEVICE_TABLE(pci, s3_devices);
+
+static struct pci_driver s3fb_pci_driver = {
+	name:"s3fb",
+	id_table:s3_devices,
+	probe:s3_pci_probe,
+	remove:__devexit_p(s3_pci_remove),
+//	suspend:s3_pci_suspend,
+//	resume:s3_pci_resume,
+};
+
+/* Parse user speficied options */
+
+#ifndef MODULE
+static int  __init s3fb_setup(char *options)
+{
+	char *this_opt;
+
+	if (!options || !*options)
+		return 0;
+
+	while ((this_opt = strsep(&options, ",")) != NULL) {
+		if (!*this_opt)
+			continue;
+		mode = this_opt;
+	}
+
+	return 0;
+}
+#endif
+
+/* Cleanup */
+
+static void __exit s3fb_cleanup(void)
+{
+
+	pr_debug("s3fb: cleaning up\n");
+	pci_unregister_driver(&s3fb_pci_driver);
+}
+
+/* Driver Initialisation */
+
+int __init s3fb_init(void)
+{
+
+#ifndef MODULE
+        char *option = NULL;
+
+        if (fb_get_options("s3fb", &option))
+                return -ENODEV;
+        s3fb_setup(option);
+#endif  
+
+	pr_debug("s3fb: initializing\n");
+	return pci_register_driver(&s3fb_pci_driver);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Modularization */
+
+module_init(s3fb_init);
+module_exit(s3fb_cleanup);
diff -uprN -X linux-2.6.18/Documentation/dontdiff linux-2.6.18/drivers/video/svgalib.c linux-2.6.18-feanor/drivers/video/svgalib.c
--- linux-2.6.18/drivers/video/svgalib.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18-feanor/drivers/video/svgalib.c	2006-11-23 23:32:03.000000000 +0100
@@ -0,0 +1,453 @@
+/*
+ *  Common utility functions for VGA-based graphics cards.
+ *
+ *  Copyright (c) 2006 Ondrej Zajicek <santiago@crfreenet.org>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ *
+ *  Some parts are based on David Boucher's viafb (http://davesdomain.org.uk/viafb/)                                            
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/svga.h>
+#include <linux/slab.h>
+#include <asm/types.h>
+#include <asm/io.h>
+
+
+/* Write a CRT register value spread across multiple registers */
+
+void svga_wcrt_multi(struct vga_regset *regset, u32 value) {
+
+	u8 regval, bitval, bitnum;
+
+	while (regset->regnum != VGA_REGSET_END_VAL) {
+		regval = vga_rcrt(NULL, regset->regnum);
+		bitnum = regset->lowbit;
+		while (bitnum <= regset->highbit) {
+			bitval = 1 << bitnum;
+			regval = regval & ~bitval;
+			if (value & 1) regval = regval | bitval;
+			bitnum ++;
+			value = value >> 1;
+		}
+		vga_wcrt(NULL, regset->regnum, regval);
+		regset ++;
+	}
+}
+
+/* Write a sequence register value spread across multiple registers */
+
+void svga_wseq_multi(struct vga_regset *regset, u32 value) {
+
+	u8 regval, bitval, bitnum;
+
+	while (regset->regnum != VGA_REGSET_END_VAL) {
+		regval = vga_rseq(NULL, regset->regnum);
+		bitnum = regset->lowbit;
+		while (bitnum <= regset->highbit) {
+			bitval = 1 << bitnum;
+			regval = regval & ~bitval;
+			if (value & 1) regval = regval | bitval;
+			bitnum ++;
+			value = value >> 1;
+		}
+		vga_wseq(NULL, regset->regnum, regval);
+		regset ++;
+	}
+}
+
+unsigned int svga_regset_size(struct vga_regset *regset)
+{
+	u8 count = 0;
+
+	while (regset->regnum != VGA_REGSET_END_VAL) {
+		count += regset->highbit - regset->lowbit + 1;
+		regset ++;
+	}
+	return 1 << count;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/* Set graphics controller registers to sane values */
+void svga_set_default_gfx_regs(void)
+{
+	/* All standard GFX registers (GR00 - GR08) */
+	vga_wgfx(NULL, VGA_GFX_SR_VALUE, 0x00);
+	vga_wgfx(NULL, VGA_GFX_SR_ENABLE, 0x00);
+	vga_wgfx(NULL, VGA_GFX_COMPARE_VALUE, 0x00);
+	vga_wgfx(NULL, VGA_GFX_DATA_ROTATE, 0x00);
+	vga_wgfx(NULL, VGA_GFX_PLANE_READ, 0x00);
+	vga_wgfx(NULL, VGA_GFX_MODE, 0x00);
+//	vga_wgfx(NULL, VGA_GFX_MODE, 0x40);
+	vga_wgfx(NULL, VGA_GFX_MISC, 0x05);
+//	vga_wgfx(NULL, VGA_GFX_MISC, 0x01);
+	vga_wgfx(NULL, VGA_GFX_COMPARE_MASK, 0x0F);
+	vga_wgfx(NULL, VGA_GFX_BIT_MASK, 0xFF);
+}
+
+/* Set attribute controller registers to sane values */
+void svga_set_default_atc_regs(void)
+{
+	/* All standard ATC registers (AR00 - AR14) */
+	u8 count;
+	for (count = 0; count <= 0xF; count ++)
+		svga_wattr(count, count);
+
+	svga_wattr(VGA_ATC_MODE, 0x01);
+//	svga_wattr(VGA_ATC_MODE, 0x41);
+	svga_wattr(VGA_ATC_OVERSCAN, 0x00);
+	svga_wattr(VGA_ATC_PLANE_ENABLE, 0x0F);
+	svga_wattr(VGA_ATC_PEL, 0x00);
+	svga_wattr(VGA_ATC_COLOR_PAGE, 0x00);
+}
+
+/* Set sequencer registers to sane values */
+void svga_set_default_seq_regs(void)
+{
+	/* Standard sequencer registers (SR01 - SR04), SR00 is not set */
+	vga_wseq(NULL, VGA_SEQ_CLOCK_MODE, VGA_SR01_CHAR_CLK_8DOTS);
+	vga_wseq(NULL, VGA_SEQ_PLANE_WRITE, VGA_SR02_ALL_PLANES);
+	vga_wseq(NULL, VGA_SEQ_CHARACTER_MAP, 0x00);
+	vga_wseq(NULL, VGA_SEQ_MEMORY_MODE, VGA_SR04_EXT_MEM | VGA_SR04_SEQ_MODE | VGA_SR04_CHN_4M);
+//	vga_wseq(NULL, VGA_SEQ_MEMORY_MODE, VGA_SR04_EXT_MEM);
+}
+
+/* Set CRTC registers to sane values */
+void svga_set_default_crt_regs(void)
+{
+	/* Standard CRT registers CR03 CR08 CR09 CR14 CR17 */
+	svga_wcrt_mask(0x03, 0x80, 0x80);	/* Enable vertical retrace EVRA */
+	vga_wcrt(NULL, VGA_CRTC_PRESET_ROW, 0);
+	svga_wcrt_mask(VGA_CRTC_MAX_SCAN, 0, 0x1F); 
+	vga_wcrt(NULL, VGA_CRTC_UNDERLINE, 0);
+	vga_wcrt(NULL, VGA_CRTC_MODE, 0xE3);
+}
+
+void svga_set_textmode_vga_regs(void)
+{
+	/* svga_wseq_mask(0x1, 0x00, 0x01); */   /* Switch 8/9 pixel per char */       
+	vga_wseq(NULL, VGA_SEQ_MEMORY_MODE,	VGA_SR04_EXT_MEM);
+	vga_wseq(NULL, VGA_SEQ_PLANE_WRITE,	0x03);
+
+	vga_wcrt(NULL, VGA_CRTC_MAX_SCAN,	0x0f); /* 0x4f */
+	vga_wcrt(NULL, VGA_CRTC_UNDERLINE,	0x1f);
+	svga_wcrt_mask(VGA_CRTC_MODE,		0x23, 0x7f);
+
+	vga_wcrt(NULL, VGA_CRTC_CURSOR_START,	0x0d);
+	vga_wcrt(NULL, VGA_CRTC_CURSOR_END,	0x0e);
+	vga_wcrt(NULL, VGA_CRTC_CURSOR_HI,	0x00);
+	vga_wcrt(NULL, VGA_CRTC_CURSOR_LO,	0x00);
+
+	vga_wgfx(NULL, VGA_GFX_MODE,		0x10); /* Odd/even memory mode */
+	vga_wgfx(NULL, VGA_GFX_MISC,		0x0E); /* Misc graphics register - text mode enable */
+	vga_wgfx(NULL, VGA_GFX_COMPARE_MASK,	0x00);
+
+	vga_r(NULL, 0x3DA);
+	vga_w(NULL, VGA_ATT_W, 0x00);
+
+	svga_wattr(0x10, 0x0C);			/* Attribute Mode Control Register - text mode, blinking and line graphics */
+	svga_wattr(0x13, 0x08);			/* Horizontal Pixel Panning Register  */
+
+	vga_r(NULL, 0x3DA);
+	vga_w(NULL, VGA_ATT_W, 0x20);
+}
+
+void svga_dump_var(struct fb_var_screeninfo *var, int node)
+{
+	pr_debug("fb%d: var.vmode         : 0x%X\n", node, var->vmode);
+	pr_debug("fb%d: var.xres          : %d\n", node, var->xres);
+	pr_debug("fb%d: var.yres          : %d\n", node, var->yres);
+	pr_debug("fb%d: var.bits_per_pixel: %d\n", node, var->bits_per_pixel);
+	pr_debug("fb%d: var.xres_virtual  : %d\n", node, var->xres_virtual);
+	pr_debug("fb%d: var.yres_virtual  : %d\n", node, var->yres_virtual);
+	pr_debug("fb%d: var.left_margin   : %d\n", node, var->left_margin);
+	pr_debug("fb%d: var.right_margin  : %d\n", node, var->right_margin);
+	pr_debug("fb%d: var.upper_margin  : %d\n", node, var->upper_margin);
+	pr_debug("fb%d: var.lower_margin  : %d\n", node, var->lower_margin);
+	pr_debug("fb%d: var.hsync_len     : %d\n", node, var->hsync_len);
+	pr_debug("fb%d: var.vsync_len     : %d\n", node, var->vsync_len);
+	pr_debug("fb%d: var.sync          : 0x%X\n", node, var->sync);
+	pr_debug("fb%d: var.pixclock      : %d\n\n", node, var->pixclock);
+}
+
+/* ------------------------------------------------------------------------- */
+
+
+/*
+   Compute PLL settings (M, N, R)
+   F_VCO = (F_BASE * M) / N
+   F_OUT = F_VCO / (2^R)
+*/
+
+static inline u32 abs_diff(u32 a, u32 b) {return (a > b) ? (a - b) : (b - a);}
+
+int svga_compute_pll(struct svga_pll *pll, u32 f_wanted, u16 *m, u16 *n, u16 *r, int node)
+{
+	u16 am, an, ar;
+	u32 f_vco, f_current, delta_current, delta_best;
+
+	pr_debug("fb%d: ideal frequency: %d kHz\n", node, (unsigned int) f_wanted);
+
+	ar = pll->r_max;
+	f_vco = f_wanted << ar;
+
+	/* overflow check */
+	if ((f_vco >> ar) != f_wanted)
+		return -EINVAL;
+
+	/* It is usually better to have greater VCO clock
+	   because of better frequency stability.
+	   So first try r_max, then r smaller. */
+	while ((ar > pll->r_min) && (f_vco > pll->f_vco_max)) {
+		ar--;
+		f_vco = f_vco >> 1;
+	}
+
+	/* VCO bounds check */
+	if ((f_vco < pll->f_vco_min) || (f_vco > pll->f_vco_max))
+		return -EINVAL;
+
+	delta_best = 0xFFFFFFFF;
+	*m = 0;
+	*n = 0;
+	*r = ar;
+	
+	am = pll->m_min;
+	an = pll->n_min;
+	
+	while ((am <= pll->m_max) && (an <= pll->n_max)) {
+		f_current = (pll->f_base * am) / an;
+		delta_current = abs_diff (f_current, f_vco);
+
+		if (delta_current < delta_best) {
+			delta_best = delta_current;
+			*m = am;
+			*n = an;
+		}
+
+		if (f_current <= f_vco) {
+			am ++;
+		} else {
+			an ++;
+		}
+	}
+
+	f_current = (pll->f_base * *m) / *n;
+	pr_debug("fb%d: found frequency: %d kHz (VCO %d kHz)\n", node, (int) (f_current >> ar), (int) f_current);
+	pr_debug("fb%d: m = %d n = %d r = %d\n", node, (unsigned int) *m, (unsigned int) *n, (unsigned int) *r);
+	return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+/* Check CRT timing values */
+int svga_check_timings(struct svga_timing_regs *tm, struct fb_var_screeninfo *var, int node)
+{
+	u32 value;
+	
+	var->xres         = (var->xres+7)&~7; 
+	var->left_margin  = (var->left_margin+7)&~7; 
+	var->right_margin = (var->right_margin+7)&~7; 
+	var->hsync_len    = (var->hsync_len+7)&~7; 
+	
+	// Check horizontal total
+	value = var->xres + var->left_margin + var->right_margin + var->hsync_len;
+	if (((value / 8) - 5) >= svga_regset_size (tm->h_total_regs)) return -EINVAL;
+	
+	// Check horizontal display and blank start
+	value = var->xres;
+	if (((value / 8) - 1) >= svga_regset_size (tm->h_display_regs)) return -EINVAL;
+	if (((value / 8) - 1) >= svga_regset_size (tm->h_blank_start_regs)) return -EINVAL;
+
+	// Check horizontal sync start
+	value = var->xres + var->right_margin;
+	if (((value / 8) - 1) >= svga_regset_size (tm->h_sync_start_regs)) return -EINVAL;
+
+	// Check horizontal blank end (or length)
+	value = var->left_margin + var->right_margin + var->hsync_len;
+	if ((value == 0) || ((value / 8) >= svga_regset_size (tm->h_blank_end_regs))) return -EINVAL;
+	
+	// Check horizontal sync end (or length)
+	value = var->hsync_len;
+	if ((value == 0) || ((value / 8) >= svga_regset_size (tm->h_sync_end_regs))) return -EINVAL;
+	
+	// Check vertical total
+	value = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
+	if ((value - 1) >= svga_regset_size(tm->v_total_regs)) return -EINVAL;
+	
+	// Check vertical display and blank start
+	value = var->yres;
+	if ((value - 1) >= svga_regset_size(tm->v_display_regs)) return -EINVAL;
+	if ((value - 1) >= svga_regset_size(tm->v_blank_start_regs)) return -EINVAL;
+
+	// Check vertical sync start
+	value = var->yres + var->lower_margin;
+	if ((value - 1) >= svga_regset_size(tm->v_sync_start_regs)) return -EINVAL;
+	
+	// Check vertical blank end (or length)
+	value = var->upper_margin + var->lower_margin + var->vsync_len;
+	if ((value == 0) || (value >= svga_regset_size (tm->v_blank_end_regs))) return -EINVAL;
+	
+	// Check vertical sync end  (or length)
+	value = var->vsync_len;
+	if ((value == 0) || (value >= svga_regset_size (tm->v_sync_end_regs))) return -EINVAL;
+
+	return 0;
+}
+
+/* Set CRT timing registers */
+void svga_set_timings(struct svga_timing_regs *tm, struct fb_var_screeninfo *var,
+			u32 hmul, u32 hdiv, u32 vmul, u32 vdiv, int node)
+{
+	u8 regval;
+	u32 value;
+
+	value = var->xres + var->left_margin + var->right_margin + var->hsync_len;
+	value = (value * hmul) / hdiv;
+	pr_debug("fb%d: horizontal total      : %d\n", node, value);
+	svga_wcrt_multi(tm->h_total_regs, (value / 8));
+	vga_wcrt(NULL, 0x3B, (value / 8) - 5);
+	
+	value = var->xres;
+	value = (value * hmul) / hdiv;
+	pr_debug("fb%d: horizontal display    : %d\n", node, value);
+	svga_wcrt_multi(tm->h_display_regs, (value / 8) - 1);
+	
+	value = var->xres;
+	value = (value * hmul) / hdiv;
+	pr_debug("fb%d: horizontal blank start: %d\n", node, value);
+	svga_wcrt_multi(tm->h_blank_start_regs, (value / 8));
+	
+	value = var->xres + var->left_margin + var->right_margin + var->hsync_len;
+	value = (value * hmul) / hdiv;
+	pr_debug("fb%d: horizontal blank end  : %d\n", node, value);
+	svga_wcrt_multi(tm->h_blank_end_regs, (value / 8) - 2); /* really -2 ? */
+	
+	value = var->xres + var->right_margin;
+	value = (value * hmul) / hdiv;
+	pr_debug("fb%d: horizontal sync start : %d\n", node, value);
+	svga_wcrt_multi(tm->h_sync_start_regs, (value / 8) + 2); /* why not -1 ? */
+	
+	value = var->xres + var->right_margin + var->hsync_len;
+	value = (value * hmul) / hdiv;
+	pr_debug("fb%d: horizontal sync end   : %d\n", node, value);
+	svga_wcrt_multi(tm->h_sync_end_regs, (value / 8) + 1); /* why not -1 ? */
+	
+	value = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
+	value = (value * vmul) / vdiv;
+	pr_debug("fb%d: vertical total        : %d\n", node, value);
+	svga_wcrt_multi(tm->v_total_regs, value - 2);
+	
+	value = var->yres;
+	value = (value * vmul) / vdiv;
+	pr_debug("fb%d: vertical display      : %d\n", node, value);
+	svga_wcrt_multi(tm->v_display_regs, value - 1);
+	
+	value = var->yres;
+	value = (value * vmul) / vdiv;
+	pr_debug("fb%d: vertical blank start  : %d\n", node, value);
+	svga_wcrt_multi(tm->v_blank_start_regs, value);
+	
+	value = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
+	value = (value * vmul) / vdiv;
+	pr_debug("fb%d: vertical blank end    : %d\n", node, value);
+	svga_wcrt_multi(tm->v_blank_end_regs, value - 2); /* really -2 ? */
+	
+	value = var->yres + var->lower_margin;
+	value = (value * vmul) / vdiv;
+	pr_debug("fb%d: vertical sync start   : %d\n", node, value);
+	svga_wcrt_multi(tm->v_sync_start_regs, value - 1);
+	
+	value = var->yres + var->lower_margin + var->vsync_len;
+	value = (value * vmul) / vdiv;
+	pr_debug("fb%d: vertical sync end     : %d\n", node, value);
+	svga_wcrt_multi(tm->v_sync_end_regs, value - 1);
+
+	/* Set horizontal and vertical sync pulse polarity in misc register */
+
+	regval = vga_r(NULL, VGA_MIS_R); 
+	if (var->sync & FB_SYNC_HOR_HIGH_ACT) {
+		pr_debug("fb%d: positive horizontal sync\n", node);
+		regval = regval & ~0x80;
+	} else {
+		pr_debug("fb%d: negative horizontal sync\n", node);
+		regval = regval | 0x80;
+	}
+	if (var->sync & FB_SYNC_VERT_HIGH_ACT) {
+		pr_debug("fb%d: positive vertical sync\n", node);
+		regval = regval & ~0x40;
+	} else {
+		pr_debug("fb%d: negative vertical sync\n\n", node);
+		regval = regval | 0x40;
+	}
+	vga_w(NULL, VGA_MIS_W, regval);                                                                                 
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+
+int svga_match_format(struct svga_fb_format *frm, struct fb_var_screeninfo *var, struct fb_fix_screeninfo *fix)
+{
+	int i = 0;
+	
+	while (frm->bits_per_pixel != SVGA_FORMAT_END_VAL)
+	{
+		if ((var->bits_per_pixel == frm->bits_per_pixel) &&
+		    (var->red.length     <= frm->red.length)     &&
+		    (var->green.length   <= frm->green.length)   &&
+		    (var->blue.length    <= frm->blue.length)    &&
+		    (var->transp.length  <= frm->transp.length)) {
+		    	var->bits_per_pixel = frm->bits_per_pixel;
+			var->red            = frm->red;
+			var->green          = frm->green;
+			var->blue           = frm->blue;
+			var->transp         = frm->transp;
+			var->nonstd         = frm->nonstd;
+			if (fix != NULL) {
+				fix->type      = frm->type;
+				fix->type_aux  = frm->type_aux;
+				fix->visual    = frm->visual;
+				fix->xpanstep  = frm->xpanstep;
+			}
+			return i;
+		}
+		i++;
+		frm++;
+	}
+	return -EINVAL;
+}
+
+
+EXPORT_SYMBOL(svga_wseq_multi);
+EXPORT_SYMBOL(svga_wcrt_multi);
+EXPORT_SYMBOL(svga_regset_size);
+
+EXPORT_SYMBOL(svga_set_default_gfx_regs);
+EXPORT_SYMBOL(svga_set_default_atc_regs);
+EXPORT_SYMBOL(svga_set_default_seq_regs);
+EXPORT_SYMBOL(svga_set_default_crt_regs);
+EXPORT_SYMBOL(svga_set_textmode_vga_regs);
+
+EXPORT_SYMBOL(svga_dump_var);
+
+EXPORT_SYMBOL(svga_compute_pll);
+EXPORT_SYMBOL(svga_check_timings);
+EXPORT_SYMBOL(svga_set_timings);
+EXPORT_SYMBOL(svga_match_format);
+
+MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>");
+MODULE_DESCRIPTION("Common utility functions for VGA-based graphics cards");
+MODULE_LICENSE("GPL");
diff -uprN -X linux-2.6.18/Documentation/dontdiff linux-2.6.18/include/linux/svga.h linux-2.6.18-feanor/include/linux/svga.h
--- linux-2.6.18/include/linux/svga.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.18-feanor/include/linux/svga.h	2006-11-23 11:41:50.000000000 +0100
@@ -0,0 +1,113 @@
+#ifndef _LINUX_SVGA_H
+#define _LINUX_SVGA_H
+
+#ifdef __KERNEL__
+
+#include <video/vga.h>
+
+/* Terminator for register set */
+
+#define VGA_REGSET_END_VAL	0xFF
+#define VGA_REGSET_END		{VGA_REGSET_END_VAL, 0, 0}
+
+struct vga_regset {
+	__u8 regnum;
+	__u8 lowbit;
+	__u8 highbit;
+};
+
+/* ------------------------------------------------------------------------- */
+
+#define SVGA_FORMAT_END_VAL	0xFFFF
+#define SVGA_FORMAT_END		{SVGA_FORMAT_END_VAL, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, 0, 0, 0, 0, 0, 0}
+
+struct svga_fb_format {
+	/* var part */
+	u32 bits_per_pixel;
+	struct fb_bitfield red;
+	struct fb_bitfield green;
+	struct fb_bitfield blue;
+	struct fb_bitfield transp;
+	u32 nonstd;
+	/* fix part */
+	u32 type;
+	u32 type_aux;
+	u32 visual;
+	u32 xpanstep;
+	u32 xresstep;
+};
+
+struct svga_timing_regs {
+	struct vga_regset *h_total_regs;
+	struct vga_regset *h_display_regs;
+	struct vga_regset *h_blank_start_regs;
+	struct vga_regset *h_blank_end_regs;
+	struct vga_regset *h_sync_start_regs;
+	struct vga_regset *h_sync_end_regs;
+
+	struct vga_regset *v_total_regs;
+	struct vga_regset *v_display_regs;
+	struct vga_regset *v_blank_start_regs;
+	struct vga_regset *v_blank_end_regs;
+	struct vga_regset *v_sync_start_regs;
+	struct vga_regset *v_sync_end_regs;
+};
+
+struct svga_pll {
+	u16 m_min;
+	u16 m_max;
+	u16 n_min;
+	u16 n_max;
+	u16 r_min;
+	u16 r_max;  /* r_max < 32 */
+	u32 f_vco_min;
+	u32 f_vco_max;
+	u32 f_base;
+};
+
+
+/* Write a value to the attribute register */
+
+static inline void svga_wattr(u8 index, u8 data) {
+
+	inb(0x3DA);
+	outb(index, 0x3C0);
+	outb(data, 0x3C0);
+}
+
+/* Write a value to a sequence register with a mask */
+
+static inline void svga_wseq_mask(u8 index, u8 data, u8 mask) {
+
+	vga_wseq(NULL, index, (data & mask) | (vga_rseq(NULL, index) & ~mask));
+}
+
+/* Write a value to a CRT register with a mask */
+
+static inline void svga_wcrt_mask(u8 index, u8 data, u8 mask) {
+
+	vga_wcrt(NULL, index, (data & mask) | (vga_rcrt(NULL, index) & ~mask));
+}
+
+
+void svga_wcrt_multi(struct vga_regset *regset, u32 value);
+void svga_wseq_multi(struct vga_regset *regset, u32 value);
+unsigned int svga_regset_size(struct vga_regset *regset);
+
+void svga_set_default_gfx_regs(void);
+void svga_set_default_atc_regs(void);
+void svga_set_default_seq_regs(void);
+void svga_set_default_crt_regs(void);
+void svga_set_textmode_vga_regs(void);
+
+void svga_dump_var(struct fb_var_screeninfo *var, int node);
+
+int svga_compute_pll(struct svga_pll *pll, u32 f_wanted, u16 *m, u16 *n, u16 *r, int node);
+int svga_check_timings(struct svga_timing_regs *tm, struct fb_var_screeninfo *var, int node);
+void svga_set_timings(struct svga_timing_regs *tm, struct fb_var_screeninfo *var, u32 hmul, u32 hdiv, u32 vmul, u32 vdiv, int node);
+
+int svga_match_format(struct svga_fb_format *frm, struct fb_var_screeninfo *var, struct fb_fix_screeninfo *fix);
+
+#endif /* __KERNEL__  */
+#endif /* _LINUX_SVGA_H */
+


-- 
Elen sila lumenn' omentielvo

Ondrej 'SanTiago' Zajicek (email: santiago@mail.cz, jabber: santiago@njs.netlab.cz)
OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net)
"To err is human -- to blame it on a computer is even more so."

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2006-11-23 23:41 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-11-23 23:41 [PATCH] driver for S3 Trio Ondrej Zajicek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).