Date: Mon, 9 Mar 1998 10:50:20 +0100 (MET)
From: Juergen Orschiedt <orschied@kassandra.zx.basf-ag.de>
Subject: L68K: Framebuffer for Tseng chipsets
To: linux-m68k@lists.linux-m68k.org
Sender: owner-linux-m68k@phil.uni-sb.de
Reply-To: juergen.orschiedt@zx.basf-ag.de

The following patch contains:
  o changes to arch/m68k/atari/time.c
    instead of inb/outb we use readb/writeb

  o framebuffer driver for tseng chipsets 
    ET4000, ET4000W32i, ET4000W32p, ET6000
    + hwscroll for all chipsets (nearly the complete video-
      memory is used for scrollbuffer) 
    + accelerated functions for W32x, ET6000
    + no need for mode= (autodetect current mode)
 
Commandline-syntax:
    video=tseng:mem=0xXXXXX,io=0xXXXXX

    Additional options:
    	ET4000: force ET4000 chip
    	ET6000: force ET6000 chip
    	noaccel: disable accelerated functions
    	flag:   debug-support (read the code)

There are some known flaws using tsengfb together with atafb...
but in principle you can use it for debugging:

	video=tseng:mem=0xfec00000,io=0xfed00000 video=atafb: video=map:10

vc1 on atafb, rest on tsengfb

atafb:external and tsengfb doesn't work with the same card!

Juergen

P.S. Geerts patches required...

------------- snap -------------
*** l68k/linux-2.1.85/include/asm-m68k/io.h	Thu Feb  5 15:10:45 1998
--- linux/include/asm-m68k/io.h	Sun Mar  8 23:02:02 1998
***************
*** 57,62 ****
--- 57,67 ----
  	return (void *) mm_ptov(address);
  }
  
+ /* some fakes... */
+ #ifdef CONFIG_ATARI
+ #include <asm/atari_isahw.h>
+ #endif
+ 
  /*
   * IO bus memory addresses are 1:1 with the physical address,
   * except on the PCI bus of the Hades.
*** l68k/linux-2.1.85/drivers/video/Config.in	Sun Mar  1 14:52:02 1998
--- linux/drivers/video/Config.in	Thu Feb 19 07:39:17 1998
***************
*** 26,31 ****
--- 26,32 ----
    if [ "$CONFIG_ATARI" = "y" ]; then
      bool 'Atari native chipset support' CONFIG_FB_ATARI
  #    tristate 'Mach64 Frame Buffer support' CONFIG_FB_MACH64
+     tristate 'Tseng Frame Buffer support' CONFIG_FB_TSENG
    fi
    if [ "$CONFIG_CHRP" = "y" -o "$CONFIG_PMAC" = "y" ]; then
      bool 'Open Firmware frame buffer device support' CONFIG_FB_OPEN_FIRMWARE
*** l68k/linux-2.1.85/include/linux/fb.h	Sun Mar  1 14:52:51 1998
--- linux/include/linux/fb.h	Thu Feb 26 20:46:21 1998
***************
*** 87,92 ****
--- 87,93 ----
  #define FB_ACCEL_MACH64		6	/* ATI Mach 64			*/
  #define FB_ACCEL_TGA		7	/* DEC 21030 TGA		*/
  #define FB_ACCEL_ATY		8	/* atyfb (ATI Mach64)		*/
+ #define FB_ACCEL_TSENG      9   /* Tseng chipsets: ET4000, ET4000W[ip], ET6000 */
  
  #define FB_SYNC_HOR_HIGH_ACT	1	/* horizontal sync high active	*/
  #define FB_SYNC_VERT_HIGH_ACT	2	/* vertical sync high active	*/
*** /dev/null	Sun Jun 16 21:20:44 1996
--- linux/include/asm-m68k/atari_isahw.h	Sat Feb 28 20:20:43 1998
***************
*** 0 ****
--- 1,132 ----
+ /*
+  * include/asm/atari_isahw.h			$Revision: 1.1 $
+  *
+  * this file contains definitions for the various
+  * ISA-based boards for Atari machines
+  *
+  *
+  * __addr_region:	descripes an ISA-region
+  *					phys: phys address in CPU-space
+  *					virt: virt address
+  *					size: size of decoded region
+  *
+  * __isa_descriptor:	mainly used for VGA boards, but also usable for
+  *					true ISA extenders like Panther, NOVA
+  *
+  *					IOBase:	ISA IO address range
+  *					IOBaseOdd:	same, but for access to odd addresses
+  *					BaseMem:	ISA Membase, the first MB of ISA space
+  *					ExtMem:		extendend Mem, if adapter support this
+  *
+  * detected_isa:	points to a __isa_descriptor and descripes the
+  *					ISA adapters characteristics
+  *					at the moment only one adaptor possible!
+  *
+  * Parameter used in macros
+  *   ISA_IOBase	 base-address to reach IO-port 0x0000
+  *   ISA_BaseMem base-address to reach the first Meg of ISA-Memory (usually
+  *		 the standard VGA-memory, starting at 0xA0000 in ISA-memspace)
+  *
+  *   (maybe you'll have to redefine these parameters if you've more than one board,
+  *    f.e. #define ISA_IOBase locIOBase[x]
+  *    whereby x is the instance of the driver)
+  *
+  * ! To Do:
+  *   We should use the __isa_descriptor (use ISA_IOBaseOdd if available)
+  */
+ 
+ #ifndef ATARI_ISAHW_INCLUDE
+ #define ATARI_ISAHW_INCLUDE
+ 
+ /*
+  * structures for regions and fingerprints
+  */
+ typedef struct {
+ 	caddr_t	phys,
+ 			virt;
+ 	__u32	size;
+ } __addr_region;		/* previous __vga_region */
+ 
+ typedef struct {
+ 	__addr_region IOBase;
+ 	__addr_region IOBaseOdd;
+ 	__addr_region BaseMem;
+ 	__addr_region ExtMem;
+ } __isa_descriptor;		/* previous __vga_fingerprint */
+ 
+ extern __isa_descriptor *detected_isa;
+ 
+ /*
+  * definitions to reuse iNtel code
+  */
+  
+ #undef inb
+ #undef inb_p
+ #define inb(port) readb((ISA_IOBase)+port)
+ #define inb_p(port) readb((ISA_IOBase)+port)
+ 
+ #undef inw
+ #undef inw_p
+ #define inw(port) readw((ISA_IOBase)+port)
+ #define inw_p(port) readw((ISA_IOBase)+port)
+ 
+ #undef inl
+ #undef inl_p
+ #define inl(port) readl((ISA_IOBase)+port)
+ #define inl_p(port) readl((ISA_IOBase)+port)
+ 
+ #undef outb
+ #undef outb_p
+ #define outb(val, port)	((void)writeb(val, (ISA_IOBase)+port))
+ #define outb_p(val, port)	writeb(val, (ISA_IOBase)+port)
+ 
+ #undef outw
+ #undef outw_p
+ #define outw(val, port) ((void)writew(val, (ISA_IOBase)+port))
+ #define outw_p(val, port) writew(val, (ISA_IOBase)+port)
+ 
+ #undef outl
+ #undef outl_p
+ #define outl(val, port) ((void)writel(val, (ISA_IOBase)+port))
+ #define outl_p(val, port) writel(val, (ISA_IOBase)+port)
+  
+ #define insb(port, dst, cnt)		\
+ 	do {__u8 *__dst=(__u8 *)dst;	\
+ 		int __cnt=cnt;				\
+ 		while (__cnt--)				\
+ 			*__dst++=inb((port));	\
+ 	} while (0)
+ 
+ #define insw(port, dst, cnt)		\
+ 	do {__u16 *__dst=(__u16 *)dst;	\
+ 		int __cnt=cnt;				\
+ 		while (__cnt--)				\
+ 			*__dst++=inw((port));	\
+ 	} while (0)
+ 
+ #define outsb(port, src, cnt)		\
+ 	do { __u8 *__src=(__u8 *)src;	\
+ 		int __cnt=cnt;				\
+ 		while (__cnt--)				\
+ 			outb(*__src++, (port));	\
+ 	} while (0)
+ 
+ #define outsw(port, src, cnt)		\
+ 	do { __u16 *__src=(__u16 *)src;	\
+ 		int __cnt=cnt;				\
+ 		while (__cnt--)				\
+ 			outb(*__src++, (port));	\
+ 	} while (0)
+ 
+ #define IGNORE_CONST_1(var)						\
+ 	({ typedef __base_t = (var);				\
+ 	   __base_t *__p = (__base_t *)&(var);		\
+ 	   __p; })
+ 
+ #define IGNORE_CONST(var) (*IGNORE_CONST_1(var))
+ 
+ 
+ /* a (hopefully) save definition for ISA_IOBase */
+ #define ISA_IOBase (detected_isa->IOBase.virt)
+ 
+ #endif
*** /dev/null	Sun Jun 16 21:20:44 1996
--- linux/drivers/video/tsengfb.c	Mon Mar  9 09:01:15 1998
***************
*** 0 ****
--- 1,2201 ----
+ /*
+  * Linux/drivers/video/tseng.c -- Tseng frame buffer device
+  *										$Revision: 2.1.85.3 $
+  *
+  *    Copyright (C) 1997 Juergen Orschiedt
+  *						 Wout Klaren
+  *
+  * 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.
+  *
+  * - features:
+  *   o panning with all chipsets
+  *   o acceleration with ET4000W32, ET6000
+  *   o no "mode=" on startup is treatened as "keep current"
+  *	 o like NVDI we ignore the EGA and MGA modes
+  *   o autodetect (1bpp/8bpp)
+  *   o only 8byN fonts
+  *
+  * - bugs:
+  *   o colour table too dim in mfb-mode
+  *   o 1bpp doesn't work at the moment...
+  * - todo:
+  *   o testing for ET6000
+  *   o cleanup necessary - duplicate code
+  *   o selectable blanking during large transfers
+  *	 o mode-setting
+  *   o clock-setting (dependency from card/RamDAC)
+  *   o interface to svgadetect
+  *	 o measurement for video-clock (userlevel?)
+  *   o setting from svgalib/nvdi parameters (ioctrl, userlevel)
+  *   o converter from svgalib/nvdi to fbset
+  * - missing:
+  *   o 'true' VGA textmode
+  *
+  * naming conventions:
+  *	 tsengfb_xxx:	fb-related functions, hardware independend
+  *   tseng_xxx:		hardware-layer, all tseng-chips
+  *   w32_xxx:		hardware-layer, all accelerated chips
+  *	 et4000_xxx,
+  *	 et6000_xxx,
+  *   w32[ip]_xxx:	hardware-layer, chip-dependend
+  *
+  * $Log: tsengfb.c,v $
+  * Revision 2.1.85.3  1998/03/09 08:00:12  orschied
+  * - checked against 2.1.85 - works
+  *   Wout's test is outstanding (should work on W32P)
+  *   cleanup necessary - common layout for W32P/W32I
+  *
+  * Revision 2.1.85.2  1998/03/08 23:04:59  orschied
+  * - added Wout's W32P putc/putcs handling
+  * - cleaned up after an accident with emacs...
+  *
+  * Revision 2.1.85.1  1998/02/10 22:39:53  orschied
+  * - added NEWARCHSTRUCT to distinguish between current sourcetree (without m68k/atari)
+  *   and upcoming changes
+  * - diffs from Wout not included due to some open questions
+  *
+  */
+ 
+ 
+ #define TSENGFB_DEBUG
+ 
+ #include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.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/init.h>
+ #include <asm/uaccess.h>
+ #include <asm/system.h>
+ #include <asm/irq.h>
+ #include <asm/pgtable.h>
+ #include <asm/io.h>
+ 
+ #ifdef NEWARCHSTRUCT
+ #include <asm/atari/hardware.h>
+ #include <asm/atari/isahardware.h>
+ #else
+ #include <asm/atarihw.h>
+ #include <asm/atari_isahw.h>
+ #endif
+ 
+ #include "videohw.h"			/* svgadetect ... */
+ #include "vgafb.h"			/* macros and register definitions */
+ #include "tsengfb.h"			/* chipset dependend definitions */
+ 
+ #include "fbcon.h"
+ #include "fbcon-mfb.h"
+ #include "fbcon-cfb8.h"
+ #include "fbcon-cfb16.h"
+ 
+ /* different debug-settings */
+ #define DUMP_TSENG_REGS	(1 << 15)
+ #define TSENG_CARD		((tseng_flag & 0x00ff))
+ #define TSENG_RAMDAC	((tseng_flag & 0xff00) >> 8)
+ 
+ /* disable/enable private extensions */
+ #define	ET4000W32_SHOW_LOGO		0
+ #define ET4000W32_HW_CURSOR		0
+ 
+ /* backward compatibility */
+ #ifdef FB_ACCEL_ET4000W32
+ #define FB_ACCEL_TSENG FB_ACCEL_ET4000W32
+ #endif
+ 
+ #ifdef TSENGFB_DEBUG
+ #  define DPRINTK(fmt, args...)	printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+ #else
+ #  define DPRINTK(fmt, args...)
+ #endif
+ 
+ #define arraysize(x)		(sizeof(x)/sizeof(*(x)))
+ 
+ /*
+  * Type definitions - stolen from retz3fb.c
+  */
+ 
+ struct tsengfb_par
+ {
+   __u32 screen_base;
+   int xres;
+   int yres;
+   int xres_virtual;
+   int yres_virtual;
+   int bpp;
+   int accel;
+ 
+   struct fb_bitfield red;
+   struct fb_bitfield green;
+   struct fb_bitfield blue;
+   struct fb_bitfield transp;
+ 
+   int	sync;
+   int pixclock;
+   int left_margin;	/* time from sync to picture	*/
+   int right_margin;	/* time from picture to sync	*/
+   int upper_margin;	/* time from sync to picture	*/
+   int lower_margin;
+   int hsync_len;	/* length of horizontal sync	*/
+   int vsync_len;        /* length of vertical sync	*/
+   int vmode;
+ };
+ 
+ struct display_data {
+   long h_total;		/* Horizontal Total */
+   long h_sstart;	/* Horizontal Sync Start */
+   long h_sstop;		/* Horizontal Sync Stop */
+   long h_bstart;	/* Horizontal Blank Start */
+   long h_bstop;		/* Horizontal Blank Stop */
+   long h_dispend;	/* Horizontal Display End */
+   long v_total;		/* Vertical Total */
+   long v_sstart;	/* Vertical Sync Start */
+   long v_sstop;		/* Vertical Sync Stop */
+   long v_bstart;	/* Vertical Blank Start */
+   long v_bstop;		/* Vertical Blank Stop */
+   long v_dispend;	/* Horizontal Display End */
+ };
+ 
+ struct tseng_regs {
+   __u8 miscout,
+     inpstat0,
+     inpstat1,
+     featurectl,
+     videosysena,
+     segsel1,
+     segsel2,
+     atc[24],
+     crtc[64],
+     tseq[8],
+     gdc[9],
+     crtcb[16],
+     imareg[8];
+ };
+ 
+ /* private definitions of framebuffer functions */
+ struct fb_hwswitch
+ {
+   int (*init)(void);
+   int (*encode_fix)(struct fb_fix_screeninfo *fix, struct tsengfb_par *par);
+   int (*decode_var)(struct fb_var_screeninfo *var, struct tsengfb_par *par);
+   int (*encode_var)(struct fb_var_screeninfo *var, struct tsengfb_par *par);
+   int (*getcolreg)(unsigned int regno, unsigned int *red, unsigned int *green,
+ 		   unsigned int *blue, unsigned int *transp, struct fb_info *info);
+   int (*setcolreg)(unsigned int regno, unsigned int red, unsigned int green,
+ 		   unsigned int blue, unsigned int transp, struct fb_info *info);
+   void (*blank)(int blank, struct fb_info *info);
+ };
+ 
+ /*
+  * Variable declarations.
+  */
+ 
+ static struct fb_hwswitch *fbhw;
+ static struct tsengfb_par current_par;
+ static struct fb_var_screeninfo tsengfb_default;
+ static struct display disp;
+ static struct fb_info fb_info;
+ 
+ static char tsengfb_name[16];
+ static unsigned char tseng_clut[256][4];
+ 
+ static int current_par_valid = 0;
+ static int currcon = 0;
+ 
+ static int tseng_enable_accel = 1;
+ static int tseng_flag = 0;
+ static int tseng_inverse = 0;
+ static __u8 *const tseng_iobase;	/* allow optimization */
+ static int tseng_mode __initdata = -1;	/* nothing assigned */
+ static int et6000_iobase;		/* IO base of ET6000 registers */
+ 
+ static struct fb_videomode tsengfb_predefined[] __initdata =
+ {
+   {
+     "640x480", {		/* 640x480, 8bpp */
+       640, 480, 640, 480, 0, 0, 8, 0,
+       { 0, 8, 0 }, { 0, 8, 0 }, { 0, 8, 0 }, { 0, 0, 0 },
+       0, 0, -1, -1, FB_ACCEL_TSENG, 0, 0, 0, 0, 0, 0, 0,
+       FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+     }
+   },
+   {
+     "800x600", {		/* 800x600, 8bpp */
+       800, 600, 800, 600, 0, 0, 8, 0,
+       { 0, 8, 0 }, { 0, 8, 0 }, { 0, 8, 0 }, { 0, 0, 0 },
+       0, 0, -1, -1, FB_ACCEL_TSENG, 0, 0, 0, 0, 0, 0, 0,
+       FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+     }
+   },
+   {
+     "1024x768", {		/* 1024x768, 8bpp */
+       1024, 768, 1024, 768, 0, 0, 8, 0,
+       { 0, 8, 0 }, { 0, 8, 0 }, { 0, 8, 0 }, { 0, 0, 0 },
+       0, 0, -1, -1, FB_ACCEL_TSENG, 0, 0, 0, 0, 0, 0, 0,
+       FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+     }
+   },
+   {
+     "1280x960", {		/* 1280x960, 8bpp */
+       1280, 960, 1280, 960, 0, 0, 8, 0,
+       { 0, 8, 0 }, { 0, 8, 0 }, { 0, 8, 0 }, { 0, 0, 0 },
+       0, 0, -1, -1, FB_ACCEL_TSENG, 0, 0, 0, 0, 0, 0, 0,
+       FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+     }
+   },
+ };
+ 
+ #define NUM_TOTAL_MODES		(arraysize(tsengfb_predefined))
+ #define NUM_PREDEF_MODES	NUM_TOTAL_MODES
+ 
+ #undef ISA_IOBase
+ #define ISA_IOBase (tseng_iobase)    /* linkage to inb/outb macros */
+ 
+ /* ----------------------------------------------
+  * pointers to the GDC memory apertures - roadmap
+  *
+  * Startaddress and aperturesize depends on the address model:
+  *
+  *	w32_aperturesize:	  8k	|   128k	|   1024k
+  *	w32_aperturebase:	0xb8000	| 0x80000	| 0x000000
+  * 
+  * The PCI-cards (W32P, ET6000) uses the large aperture size
+  * For ISA-Adapters with 1MB address space we use the small aperture.
+  *
+  * Aperture 0 is used for accelerated CPU access (Pixel Amplification (TM))
+  * Aperture 1 is an unaccelerated CPU window (rev_char)
+  * Aperture 2 is an unaccelerated CPU window (bitmaps, sprite)
+  *
+  * w32_accelmap:	pointer to Aperture 0 in CPU address space, constant
+  *			purpose: accelerated CPU-access to video-memory
+  * w32_aperture1:	pointer to Aperture 1 in CPU address space, constant
+  *			purpose: non-accelerated CPU access to video-memory
+  * w32_patternbuf:	pointer to Aperture 2 in CPU address space, constant
+  *			purpose: FGcol, BGcol, Spritedata for hw-cursor
+  * w32_registermap:	pointer to ACL register map, constant
+  *
+  * w32_blitting:	hint that ACL is active (set during w32_setplanar)
+  */
+ 
+ static void     *w32_registermap;
+ static int	 w32_blitting = 0;
+ static __u8     *w32_accelmap,
+ 	        *w32_aperture1;
+ static __u32	 w32_aperturesize;
+ static __u32	 w32_aperturebase;
+ static W32_PatternBuf	 *w32_ACLpattern,
+ 			 *w32_patternbuf;
+ 
+ /*
+  * chipset dependend settings.
+  * should be taken from svgadetect-structure...
+  */
+ 
+ #if !defined(CONFIG_FB_SVGADETECT)
+ /* work around 'til svgadetect is integrated */
+ __vga_descriptor detected_vga[1] = {
+   {
+     {
+       { 0, 0, 65536 },	/* IOBase - phys, virt, size */
+       { 0, 0, 0 },	/* IOBase for odd byte read */
+       { 0, 0, 0 },	/* BaseMem */
+       { 0, 0, 0 }       /* Extended Mem */
+     },
+     PC_ISA, STDVGA, DAC_STANDARD,
+     0, 0, "Generic"
+   }
+ };
+ 
+ __isa_descriptor *detected_isa = &detected_vga[0].isa;
+ __vga_descriptor *tseng = detected_vga;	   
+ 
+ #endif
+ 
+ 
+ 
+ /*
+  *  Prototypes
+  */
+ 	
+ static void tseng_set_screen_base(__u32 s_base);
+ static void tseng_get_video(struct fb_var_screeninfo *screeninfo);
+ static void tseng_get_gdcregs(struct tseng_regs *);
+ static void tseng_set_gdcregs(struct tseng_regs *);
+ 
+ static int tseng_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
+ 			   unsigned int blue, unsigned int transp, struct fb_info *info);
+ 
+ 
+ /*
+  * text acceleration - only for accelerated chipsets ;-)
+  */
+ static struct display_switch fbcon_w32;
+ static void w32_setplanar(int mode);
+ 
+ void fbcon_w32_init(struct display *p);
+ static void fbcon_w32_bmove(struct display *p, int sy, int sx, int dy, int dx,
+ 			    int height, int width);
+ static void fbcon_w32_clear(struct vc_data *conp, struct display *p, int sy,
+ 			    int sx, int height, int width);
+ static void fbcon_w32_putc(struct vc_data *conp, struct display *p, int c, 
+ 			   int yy, int xx);
+ static void fbcon_w32_putcs(struct vc_data *conp, struct display *p,
+ 			    const char *s, int count, int yy, int xx);
+ void fbcon_w32_revc(struct display *p, int xx, int yy);
+ 
+ /****************************************
+  *				       	*
+  * Chip set specific functions.	       	*
+  *				        *
+  ***************************************/
+ 
+ static inline void enable_tseng_ext(void)
+ {
+   outb(3, 0x3bf);			/* Enable ET4000 extensions */
+   outb(0xa0, 0x3d8);
+ }
+ 
+ static inline void disable_tseng_ext(void)
+ {
+   outb(0x29, 0x3d8);
+   outb(1, 0x3bf);			/* Disable ET4000 extensions */
+ }
+ 
+ static int compute_clock_tolerance(int requested, int current)
+ {
+   int diff = current - requested;
+   return (diff * 1000000) / requested;
+ }
+ 
+ #ifdef WITH_CLOCK_SELECTION
+ /*  set the clock-frequency
+  *  this function highly relies on the the detected card/ramdac
+  *  We have at least 3 different basic types of clock generators:
+  *    a) Crazy Dots: read only register located at ISA_IOBase
+  *    b) Spectrum:   Clock generator with serial interface
+  *    c) ISA Clockgenerators: wide field...
+  */
+ 
+ static int set_clock_frequency(int requested)
+ {
+ #define MAX_CLOCK	16
+   static int cd_clk[MAX_CLOCK] = {0};
+   static int nova_clk[MAX_CLOCK] = {0};
+   static int spectrum_clk[MAX_CLOCK] = {0};
+   static int isa_clk[MAX_CLOCK] = {0};
+ 
+   int *clk_table;
+   int nearest, count, offset, mode;
+   int tolerance = (1 << (sizeof(int) - 1)) - 1;
+ 
+   switch (tseng->Card) {
+   case CRAZYDOTS:
+     clk_table = cd_clk;
+     break;
+   case NOVA:
+   case NOVAPLUS:
+     clk_table = nova_clk;
+     break;
+     
+   case SPECTRUM:
+     clk_table = spectrum_clk;
+     break;
+   default:
+     clk_table = isa_clk;
+   }
+   for (count = 0; count < MAX_CLOCK; count++) {
+     int current;
+     current = compute_clock_tolerance(clk_table[count], requested);
+     if (current < tolerance) {
+       tolerance = current;
+       offset = count;
+     }
+   }
+ }
+ #endif		/* WITH_CLOCK_SELECTION */
+ 
+ /*
+  * int tseng_init(void)
+  *
+  * Initialization.
+  */
+ 
+ __initfunc(static int tseng_init(void))
+ {
+   unsigned char tmp;
+   int i;
+   
+   enable_tseng_ext();
+   
+   /* Initiate color table */
+    
+   for (i = 0; i < 256; i++)
+     {
+       tseng_clut[i][0] = (unsigned char) i;
+       tseng_clut[i][1] = (unsigned char) i;
+       tseng_clut[i][2] = (unsigned char) i;
+       tseng_clut[i][3] = 0;
+     }
+ 
+   w32_ACLpattern = (W32_PatternBuf *) (tseng->isa.BaseMem.size - sizeof(W32_PatternBuf));
+ 
+   if (tseng_enable_accel)
+     {
+       if (tseng->Chip != ET6000)
+ 	{
+ 	  if (tseng->Chip == ET4000W32P)
+ 	    {
+ 	      tmp = crt_r(0x36);
+ 	      crt_wb(tmp | 0x28, 0x36);
+ 	    }
+ 	}
+       else
+ 	{
+ 	  et6000_iobase = ((int) crt_r(0x21) << 8) +
+ 	    ((int) crt_r(0x22) << 16) +
+ 	    ((int) crt_r(0x23) << 24);
+ 	  tmp = inb(et6000_iobase + 0x40);
+ 	  outb(tmp | 0x02, et6000_iobase + 0x40);
+ 	}
+     }
+ 
+   /*
+    * Print some settings.
+    */
+ 
+   printk("Linear memory base: 0x%8.8x, video RAM: %d Kb.\n",
+ 	 (unsigned int) tseng->isa.BaseMem.virt, tseng->isa.BaseMem.size / 1024);
+ 
+   if (!tseng_enable_accel)
+     printk("Acceleration disabled.\n");
+ 
+   tseng_setcolreg(255, 255, 255, 255, 0, &fb_info);
+   tseng_setcolreg(254, 0, 0, 0, 0, &fb_info);
+   
+   disable_tseng_ext();
+ 
+   return 0;
+ }
+ 
+ 
+ /* static int tseng_encode_fix(struct fb_fix_screeninfo *fix, 
+  *                             struct tsengfb_par *par)
+  *
+  * Fill in the 'fix' structure based on the values in the 'par' structure.
+  * Note that 4MB of video memory is reported. This makes it possible to access
+  * the accelerator registers from user space.
+  * Changes: size reflects the maximum ramsize. Lateron, we'll possibly change this
+  *          setting depending on the decoded address range (hint: svgadetect).
+  * To access the accelerator registers, we can also switch to planar mode.
+  * This has the advantage, that the user space program can work with _all_ W32's.
+  */
+ 
+ static int tseng_encode_fix(struct fb_fix_screeninfo *fix, 
+ 			    struct tsengfb_par *par)
+ {
+   memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ 
+   strcpy(fix->id, tsengfb_name);
+   fix->smem_start = tseng->isa.BaseMem.virt;
+   fix->smem_len = tseng->isa.BaseMem.size;
+   fix->mmio_start = tseng->isa.IOBase.virt;
+   fix->mmio_len = tseng->isa.IOBase.size;
+   
+   fix->type = FB_TYPE_PACKED_PIXELS;
+   fix->type_aux = 0;
+   
+   switch (par->bpp) {
+   case 1:
+     fix->visual = FB_VISUAL_MONO10;
+     fix->xpanstep = 8;
+     break;
+   case 4:
+     fix->visual = FB_VISUAL_PSEUDOCOLOR;
+     fix->xpanstep = 2;
+     break;
+   case 8:
+     fix->visual = FB_VISUAL_PSEUDOCOLOR;
+     fix->xpanstep = 1;
+     break;
+   case 15:
+   case 16:
+   case 24:
+     fix->visual = FB_VISUAL_DIRECTCOLOR;
+     fix->xpanstep = 1;
+     break;
+ 
+   }
+ 
+   fix->ypanstep = 1;
+   fix->ywrapstep = 0;
+ 
+   /* should we fill in line_length? */
+   fix->line_length = 0;
+ 
+   return 0;
+ }
+ 
+ /*
+  * int tseng_decode_var(struct fb_var_screeninfo *var, struct tsengfb_par *par)
+  *
+  * Get the video parameters out of 'var'. If a value does not fit, round
+  * it up. If it is to big, return -EINVAL.
+  */
+ 
+ static int tseng_decode_var(struct fb_var_screeninfo *var, 
+ 			    struct tsengfb_par *par)
+ {
+   par->screen_base = (var->yoffset * var->xres_virtual + var->xoffset)
+     * var->bits_per_pixel >> 3;
+   if (var->bits_per_pixel < 8)
+     par->screen_base *= 4;
+ 
+   par->xres = var->xres;
+   par->yres = var->yres;
+   par->xres_virtual = var->xres_virtual;
+   par->yres_virtual = var->yres_virtual;
+   par->bpp = var->bits_per_pixel;
+   
+   par->accel = 0;
+   if (par->bpp == 8 && tseng_enable_accel)
+     par->accel = FB_ACCEL_TSENG;
+ 
+   par->pixclock = var->pixclock;
+   par->sync = var->sync;
+   par->left_margin = var->left_margin;
+   par->right_margin = var->right_margin;
+   par->upper_margin = var->upper_margin;
+   par->lower_margin = var->lower_margin;
+   par->hsync_len = var->hsync_len;
+   par->vsync_len = var->vsync_len;
+ 
+   return 0;
+ }
+ 
+ /*
+  * int tseng_encode_var(struct fb_var_screeninfo *var, struct tsengfb_par *par)
+  *
+  * Fill the 'var' structure based on the values in 'par' and maybe other
+  * values read out of the hardware.
+  */
+ 
+ static int tseng_encode_var(struct fb_var_screeninfo *var, 
+ 			    struct tsengfb_par *par)
+ {
+   int i;
+ 
+   var->xres = par->xres;
+   var->yres = par->yres;
+   var->xres_virtual = par->xres_virtual;
+   var->yres_virtual = par->yres_virtual;
+ 
+   var->bits_per_pixel = par->bpp;
+   var->grayscale = 0;
+ 
+   if (par->bpp == 8)	{
+     var->red.offset = 0;
+     var->red.length = 8;
+     var->red.msb_right = 0;
+     var->blue = var->green = var->red;
+   } else {
+     var->red.offset = 11;
+     var->red.length = 5;
+     var->red.msb_right = 0;
+     var->green.offset = 5;
+     var->green.length = 6;
+     var->green.msb_right = 0;
+     var->blue.offset = 0;
+     var->blue.length = 5;
+     var->blue.msb_right = 0;
+   }
+ 
+   var->transp.offset = 0;
+   var->transp.length = 0;
+   var->transp.msb_right = 0;
+ 
+   var->nonstd = 0;
+   var->activate = 0;
+ 
+   var->height = -1;
+   var->width = -1;
+   var->accel = (tseng_enable_accel) ? FB_ACCEL_TSENG : FB_ACCEL_NONE;
+   var->vmode = FB_VMODE_NONINTERLACED;
+ 
+   var->pixclock = par->pixclock;
+   var->sync = par->sync;
+   var->left_margin = par->left_margin;
+   var->right_margin = par->right_margin;
+   var->upper_margin = par->upper_margin;
+   var->lower_margin = par->lower_margin;
+   var->hsync_len = par->hsync_len;
+   var->vsync_len = par->vsync_len;
+ 
+   for (i = 0; i < arraysize(var->reserved); i++)
+     var->reserved[i] = 0;
+ 
+   return 0;
+ }
+ 
+ 
+ /* int tseng_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
+  *		     unsigned int blue, unsigned int transp, struct fb_info *info)
+  *
+  * Set a single color register. The values supplied are already
+  * rounded to the hardware's capabilities (according to the entries
+  * in the var structure). Return != 0 for invalid regno.
+  */
+ 
+ static int tseng_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
+ 			   unsigned int blue, unsigned int transp, struct fb_info *info)
+ {
+   __u32 flags;
+ 
+   if (regno > 255)
+     return 1;
+ 
+   tseng_clut[regno][0] = red & 0xFF;
+   tseng_clut[regno][1] = green & 0xFF;
+   tseng_clut[regno][2] = blue & 0xFF;
+   tseng_clut[regno][3] = transp;
+ 
+   save_flags(flags);
+   cli();
+   outb(regno, 0x3c8);
+   outb((red & 0xff) >> 2, 0x3c9);
+   outb((green & 0xff) >> 2, 0x3c9);
+   outb((blue & 0xff) >> 2, 0x3c9);
+   restore_flags(flags);
+ 
+   return 0;
+ }
+ 
+ /* * int tseng_getcolreg(unsigned int regno, unsigned int *red, unsigned int *green,
+  *		     unsigned int *blue, unsigned int *transp, struct fb_info *info)
+  *
+  * Read a single color register and split it into colors/transparent.
+  * Return != 0 for invalid regno.
+  */
+ 
+ static int tseng_getcolreg(unsigned int regno, unsigned int *red, unsigned int *green,
+ 			   unsigned int *blue, unsigned int *transp, struct fb_info *info)
+ {
+   if (regno > 255)
+     return 1;
+ 
+   *red = tseng_clut[regno][0];
+   *green = tseng_clut[regno][1];
+   *blue = tseng_clut[regno][2];
+   *transp = tseng_clut[regno][3];
+ 
+   return 0;
+ }
+ 
+ /*
+  * void tseng_blank(int blank, struct fb_info *info)
+  *
+  *  Blank the screen if blank_mode != 0, else unblank. If blank == NULL
+  *  then the caller blanks by setting the CLUT (Color Look Up Table) to all
+  *  black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
+  *  to e.g. a video mode which doesn't support it. Implements VESA suspend
+  *  and powerdown modes on hardware that supports disabling hsync/vsync:
+  *    blank_mode == 2: suspend vsync
+  *    blank_mode == 3: suspend hsync
+  *    blank_mode == 4: powerdown
+  */    
+ 
+ static void tseng_blank(int blank, struct fb_info *info)
+ {
+   __u8 tsmode;
+ 	
+   switch (blank) {
+   case 4:	/* TODO: powerdown */
+   case 3:       /* TODO: suspend hsync */
+   case 2:       /* TODO: suspend vsync */
+   case 1:
+     tsmode = seq_r(0x01);
+     seq_wb((tsmode | 0x20), 0x01);
+     break;
+   default:
+     tsmode = seq_r(0x01);
+     seq_wb((tsmode & ~0x20), 0x01);
+     break;
+   }
+   return;
+ }
+ 
+ static struct fb_hwswitch tseng_switch =
+ {
+   tseng_init, tseng_encode_fix, tseng_decode_var, tseng_encode_var,
+   tseng_getcolreg, tseng_setcolreg, tseng_blank
+ };
+ 
+ /*
+  * read out the gdc register data
+  */
+ 
+ void tseng_get_gdcregs(struct tseng_regs *regs)
+ {
+   __u8 reg;
+ 
+   for (reg=0; reg <= 0x18; reg++)
+     regs->crtc[reg] = crt_r(reg);
+   for (reg=0x30; reg < 0x38; reg++)
+     regs->crtc[reg] = crt_r(reg);
+   reg=0x3f; regs->crtc[reg] = crt_r(reg);
+   
+   for (reg=0x00; reg <= 0x17; reg++) {
+     if (reg == 0x15)
+       continue;
+     (void) inb(GREG_STATUS1_R);		/* set ATC to IDX */
+       outb((reg | 0x20), ACT_IDX);	/* keep write-access to palette RAM disabled */
+       regs->atc[reg] = inb(ACT_IDX+1);
+   }
+ 
+   for (reg=0; reg <= 0x08; reg++)
+     regs->gdc[reg] = gfx_r(reg);
+ 
+   for (reg=0; reg <= 0x07; reg++) {
+     if (reg == 0x05)
+       continue;
+     regs->tseq[reg] = seq_r(reg);
+   }
+ 
+   regs->miscout = inb(0x3cc);
+   regs->segsel1 = inb(0x3c3);
+   regs->segsel2 = inb(0x3cd);
+ 
+   if (tseng_flag & DUMP_TSENG_REGS) {
+     printk("crt: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ 	   regs->crtc[0x00], regs->crtc[0x01], regs->crtc[0x02], regs->crtc[0x03],
+ 	   regs->crtc[0x04], regs->crtc[0x05], regs->crtc[0x06], regs->crtc[0x07],
+ 	   regs->crtc[0x08], regs->crtc[0x09], regs->crtc[0x0a], regs->crtc[0x0b],
+ 	   regs->crtc[0x0c], regs->crtc[0x0d], regs->crtc[0x0e], regs->crtc[0x0f]);
+ 
+     printk("crt: %02x %02x %02x %02x %02x %02x %02x %02x %02x -- -- -- -- -- -- --\n",
+ 	   regs->crtc[0x10], regs->crtc[0x11], regs->crtc[0x12], regs->crtc[0x13],
+ 	   regs->crtc[0x14], regs->crtc[0x15], regs->crtc[0x16], regs->crtc[0x17],
+ 	   regs->crtc[0x18]);
+ 
+     printk("crt: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --\n");
+     printk("crt: %02x %02x %02x %02x %02x %02x %02x %02x -- -- -- -- -- -- -- %02x\n",
+ 	   regs->crtc[0x30], regs->crtc[0x31], regs->crtc[0x32], regs->crtc[0x33],
+ 	   regs->crtc[0x34], regs->crtc[0x35], regs->crtc[0x36], regs->crtc[0x37],
+ 	   regs->crtc[0x3f]);
+ 
+     printk("atc: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ 	   regs->atc[0x00], regs->atc[0x01], regs->atc[0x02], regs->atc[0x03],
+ 	   regs->atc[0x04], regs->atc[0x05], regs->atc[0x06], regs->atc[0x07],
+ 	   regs->atc[0x08], regs->atc[0x09], regs->atc[0x0a], regs->atc[0x0b],
+ 	   regs->atc[0x0c], regs->atc[0x0d], regs->atc[0x0e], regs->atc[0x0f]);
+     
+     printk("atc: %02x %02x %02x %02x %02x -- %02x %02x\n",
+ 	   regs->atc[0x10], regs->atc[0x11], regs->atc[0x12], regs->atc[0x13],
+ 	   regs->atc[0x14], regs->atc[0x16], regs->atc[0x17]);
+     
+     printk("gdc: %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ 	   regs->gdc[0x00], regs->gdc[0x01], regs->gdc[0x02], regs->gdc[0x03],
+ 	   regs->gdc[0x04], regs->gdc[0x05], regs->gdc[0x06], regs->gdc[0x07],
+ 	   regs->gdc[0x08]);
+ 
+     printk("seq: %02x %02x %02x %02x %02x -- %02x %02x\n",
+ 	   regs->tseq[0x00], regs->tseq[0x01], regs->tseq[0x02], regs->tseq[0x03],
+ 	   regs->tseq[0x04], regs->tseq[0x06], regs->tseq[0x07]);
+   }
+ }
+ 
+ /* 
+  * still missing: set the GDC register set to the given values
+  */
+ void tseng_set_gdcregs(struct tseng_regs *regset)
+ {
+ 	return;
+ }
+ 
+ /*
+  * set fb-parameters from hardware
+  */
+ 
+ __initfunc(static void tseng_get_video(struct fb_var_screeninfo *screeninfo))
+ {
+   struct display_data data;
+   struct tseng_regs gdc;
+ 
+   long rowOffset;
+   int  bpp = 8;		/* should be readout from hardware... */
+ 
+   /* Timing sequencer contains additional info
+    * about EGA/VGA-Mode, MCLOCK divider
+    */
+ 
+   tseng_get_gdcregs(&gdc);
+ 
+   /* raw CRTC data */
+   data.h_total	= gdc.crtc[0x00];
+   data.h_bstart	= gdc.crtc[0x02];
+   data.h_bstop	= gdc.crtc[0x03] & 0x1f;
+   data.h_sstart	= gdc.crtc[0x04];
+   data.h_sstop	= gdc.crtc[0x05] & 0x1f;
+   data.h_dispend	= gdc.crtc[0x01];
+   data.v_total	= gdc.crtc[0x06];
+   data.v_sstart	= gdc.crtc[0x10];
+   data.v_sstop	= gdc.crtc[0x11] & 0x0f;
+   data.v_dispend	= gdc.crtc[0x12];
+   rowOffset		= gdc.crtc[0x13];
+   data.v_bstart	= gdc.crtc[0x15];
+   data.v_bstop	= gdc.crtc[0x16];
+ 
+   if (gdc.crtc[0x05] & (1 << 7)) data.h_bstop		|= (1 << 5);
+ 
+   if (gdc.crtc[0x07] & (1 << 3)) data.v_bstart	|= (1 << 8);
+   if (gdc.crtc[0x07] & (1 << 2)) data.v_sstart	|= (1 << 8);
+   if (gdc.crtc[0x07] & (1 << 1)) data.v_dispend	|= (1 << 8);
+   if (gdc.crtc[0x07] & (1 << 0)) data.v_total     |= (1 << 8);
+ 
+   if (gdc.crtc[0x07] & (1 << 7)) data.v_sstart	|= (1 << 9);
+   if (gdc.crtc[0x07] & (1 << 6)) data.v_dispend	|= (1 << 9);
+   if (gdc.crtc[0x07] & (1 << 5)) data.v_total		|= (1 << 9);
+   
+   if (gdc.crtc[0x09] & (1 << 5)) data.v_bstart	|= (1 << 9);
+ 
+   if (gdc.crtc[0x35] & (1 << 3)) data.v_sstart	|= (1 << 10);
+   if (gdc.crtc[0x35] & (1 << 2)) data.v_dispend	|= (1 << 10);
+   if (gdc.crtc[0x35] & (1 << 1)) data.v_total		|= (1 << 10);
+   if (gdc.crtc[0x35] & (1 << 0)) data.v_bstart	|= (1 << 10);
+ 
+   if (gdc.crtc[0x3f] & (1 << 7)) rowOffset		|= (1 << 8);
+   if (gdc.crtc[0x3f] & (1 << 4)) data.h_sstart	|= (1 << 8);
+   if (gdc.crtc[0x3f] & (1 << 2)) data.h_bstart	|= (1 << 8);
+   if (gdc.crtc[0x3f] & (1 << 0)) data.h_total	|= (1 << 8);
+ 
+     /* --- adjust the raw controller values --- */
+   data.h_dispend	+= 1;
+   data.h_bstart   += 1;
+   data.h_total	+= 5;
+   data.v_total	+= 2;
+ 
+   data.h_bstop	|= ((data.h_bstart + 63) & ~63);
+   data.h_sstop	|= ((data.h_sstart + 31) & ~31);
+   data.v_bstop	|= ((data.v_bstart + 255) & ~255);
+   data.v_sstop	|= ((data.v_sstart + 15) & ~15);
+ 	
+   if (data.h_bstop > data.h_total)
+     data.h_bstop = data.h_total;
+ 
+   if (data.h_sstop > data.h_total)
+     data.h_sstop = data.h_total;
+ 
+   if (data.v_bstop > data.v_total)
+     data.v_bstop = data.v_total;
+ 
+   if (data.v_sstop > data.v_total)
+     data.v_sstop = data.v_total;
+ 
+   data.h_total	*= bpp;		/* Horizontal Total */
+   data.h_sstart	*= bpp;		/* Horizontal Sync Start */
+   data.h_sstop	*= bpp;		/* Horizontal Sync Stop */
+   data.h_bstart	*= bpp;		/* Horizontal Blank Start */
+   data.h_bstop	*= bpp;		/* Horizontal Blank Stop */
+   data.h_dispend  *= bpp;
+   rowOffset		*= bpp;
+ 
+   if (!(gdc.gdc[5] & 0x40))
+     rowOffset *= 2;
+   
+   {	/* calculate settings */
+     int mclock, dotclk, csclk;
+ 
+     /* clock timings (in ps) from svgalib:cardex.w32
+      * should be replaced by measurement - later */
+ 		
+ 
+     long vclk[8] = {
+       19860, 17654, 15315, 13819,	/* 50.350, 56.644, 65.293, 72.364, */
+       12433, 11092, 15797, 13266	/* 80.429, 90.155, 63.300, 75.379 */
+     };
+ 
+     dotclk	= (gdc.tseq[0x01] & (1 << 3)) ? 2 : 1;
+     mclock	= (gdc.tseq[0x07] & (1 << 0)) ? 4 :
+                                          (gdc.tseq[0x07] & (1 << 6)) ? 2 : 1;
+     csclk   = (gdc.miscout >> 2) & 0x03;
+     if (gdc.crtc[0x31] & (1 << 1))
+       csclk |= (1 << 2);
+     /*
+      * some video-cards use the CS3,4 for DAC controlling,
+      * so we don't look at it (at the moment...)
+      */
+ 
+     screeninfo->xres		= data.h_dispend;
+     screeninfo->yres		= data.v_dispend + 1;
+     screeninfo->xres_virtual	= rowOffset;
+     screeninfo->yres_virtual	= (tseng->isa.BaseMem.size - sizeof(W32_PatternBuf)) / rowOffset;
+     
+     current_par.bpp = 
+       screeninfo->bits_per_pixel= gdc.gdc[5] & 0x40 ? 8 : 1;
+     
+     screeninfo->grayscale       = 0;	/* should really depend on mono/color mode */
+     screeninfo->accel		= tseng_enable_accel;
+     screeninfo->pixclock	= vclk[csclk] / mclock;
+     screeninfo->left_margin	= data.h_total - data.h_sstop;
+     screeninfo->right_margin	= data.h_sstart - data.h_dispend;
+     screeninfo->upper_margin	= data.v_total - data.v_sstop;
+     screeninfo->lower_margin	= data.v_sstart - data.v_dispend;
+     screeninfo->hsync_len	= data.h_sstop - data.h_sstart;
+     screeninfo->vsync_len	= data.v_sstop - data.v_sstart;
+   }
+ }
+ 
+ static void tseng_set_screen_base(__u32 s_base)
+ {
+   __u32   cph,            /* Cursor Position High */
+           lsa,            /* Linear Starting Address */
+           mask;
+   __u32 flags;
+ 
+   mask = (tseng->Chip < ET4000W32) ? 0x03 : 0x0f;
+   save_flags(flags);			/* seems saver... */
+   cli();
+ 
+   cph = (crt_r(0x33) & ~mask) << 16;
+   lsa = cph | (s_base >> 2);
+ 
+   crt_wb((lsa & 0x0000ff), 0x0d);
+   crt_wb((lsa & 0x00ff00) >> 8, 0x0c);
+   crt_wb((lsa >> 16) & mask, 0x33);
+   restore_flags(flags);
+ }
+ 
+ static int tsengfb_pan_display ( struct fb_var_screeninfo *var,
+                         int con, struct fb_info *info)
+ {
+   int off;
+ 
+   off =  (var->xoffset + var->yoffset * var->xres_virtual)
+     * var->bits_per_pixel >> 3;
+ 
+   if (var->bits_per_pixel < 8)
+     off *= 4;	
+   current_par.screen_base = off;
+   tseng_set_screen_base (off);
+   return 0;
+ }
+ 
+ /******************************************
+  *	       	                          *
+  * Frame buffer functions.		  *
+  *					  *
+  *****************************************/
+ 
+ static void w32fb_get_par(struct tsengfb_par *par)
+ {
+   if (current_par_valid)
+     *par = current_par;
+   else
+     fbhw->decode_var(&tsengfb_default, par);
+ }
+ 
+ static void w32fb_set_par(struct tsengfb_par *par)
+ {
+   __u8 reg;
+   current_par = *par;
+   current_par_valid = 1;
+ 
+   inb(GREG_STATUS1_R);			/* select IDX */
+   for (reg=1; reg < 0x10; reg++) {
+     if (current_par.bpp == 1)
+       attr_wb(0xff, reg);	/* monochrom translations */
+     else
+       attr_wb(reg, reg);		/* one-to-one */
+   }
+   outb(0x20, ACT_IDX);			/* enable clut */
+   (void) inb(ACT_IDX);
+ 
+   tseng_set_screen_base(par->screen_base);
+ }
+ 
+ static int tseng_set_var(struct fb_var_screeninfo *var, int isactive)
+ {
+   int error, activate;
+   struct tsengfb_par par;
+ 
+   if ((error = fbhw->decode_var(var, &par)))
+     return error;
+ 
+   activate = var->activate;
+   if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive)
+     w32fb_set_par(&par);
+   fbhw->encode_var(var, &par);
+   var->activate = activate;
+ 
+   return 0;
+ }
+ 
+ static void tseng_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, &fb_display[con].var, 1,
+ 		fbhw->setcolreg, info);
+   else
+     fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+ 		&fb_display[con].var, 1, fbhw->setcolreg, info);
+ }
+ 
+ /*
+  * int tsengfb_open(struct fb_info *info)
+  *
+  * Open the frame buffer device.
+  */
+ 
+ static int tsengfb_open(struct fb_info *info)
+ {
+   MOD_INC_USE_COUNT;
+   return 0;
+ }
+ 
+ /*
+  * int tsengfb_release(struct fb_info *info)
+  *
+  * Release the frame buffer device.
+  */
+ 
+ static int tsengfb_release(struct fb_info *info)
+ {
+   MOD_DEC_USE_COUNT;
+   return 0;
+ }
+ 
+ 
+ /* int tsengfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
+  *
+  * Get the fixed part of the display.
+  */
+ 
+ static int tsengfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
+ {
+ 	struct tsengfb_par par;
+ 	int error = 0;
+ 
+ 	if (con == -1)
+ 		w32fb_get_par(&par);
+ 	else
+ 		error = fbhw->decode_var(&fb_display[con].var, &par);
+ 
+ 	return (error) ? error : fbhw->encode_fix(fix, &par);
+ }
+ 
+ /* int tsengfb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+  *
+  * Get the user defined part of the display.
+  */
+ 
+ static int tsengfb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+ {
+   struct tsengfb_par par;
+   int error = 0;
+ 
+   if (con == -1)
+     {
+       w32fb_get_par(&par);
+       error = fbhw->encode_var(var, &par);
+     }
+   else
+     *var = fb_display[con].var;
+   
+   return error;
+ }
+ 
+ static void w32fb_set_disp(int con, struct fb_info *info)
+ {
+   struct fb_fix_screeninfo fix;
+   struct display *display;
+ 
+   if (con >= 0)
+     display = &fb_display[con];
+   else
+     display = &disp;	/* Used during initialization. */
+ 
+   tsengfb_get_fix(&fix, con, info);
+   if (con == -1)
+     con = 0;
+   display->screen_base = fix.smem_start;			/* start at top of VMEM */
+   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->can_soft_blank = 1;
+   display->inverse = tseng_inverse;
+   switch (tsengfb_default.bits_per_pixel) { /* ++Geert: FIXME par */
+   case 1:
+     display->dispsw = &fbcon_mfb;
+     break;
+   case 8:
+     if (tseng_enable_accel && tseng->Chip >= ET4000W32)
+       display->dispsw = &fbcon_w32;
+     else
+       display->dispsw = &fbcon_cfb8;
+     break;
+   case 15:
+   case 16:
+     display->dispsw = &fbcon_cfb16;
+     break;
+   default:
+     display->dispsw = NULL;
+     break;
+   }
+ }
+ 
+ /* int tsengfb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+  *
+  * Set the user defined part of the display.
+  */
+ 
+ static int tsengfb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+ {
+   int error, oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+   struct display *display;
+ 
+   if (con >= 0)
+     display = &fb_display[con];
+   else
+     display = &disp;
+ 
+   if ((error = tseng_set_var(var, con == currcon)))
+     return error;
+   
+   if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ 		oldxres = display->var.xres;
+ 		oldyres = display->var.yres;
+ 		oldvxres = display->var.xres_virtual;
+ 		oldvyres = display->var.yres_virtual;
+ 		oldbpp = display->var.bits_per_pixel;
+ 
+ 		display->var = *var;
+ 
+ 		if (oldxres != var->xres || oldyres != var->yres ||
+ 		    oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
+ 		    oldbpp != var->bits_per_pixel)
+ 		{
+ 			struct fb_fix_screeninfo fix;
+ 
+ 			tsengfb_get_fix(&fix, con, info);
+ 
+ 			display->screen_base = fix.smem_start;
+ 			display->var.xoffset = 0;
+ 			display->var.yoffset = 0;
+ 			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->can_soft_blank = 1;
+ 			display->inverse = tseng_inverse;
+ 
+ 			if (fb_info.changevar)
+ 				(*fb_info.changevar)(con);
+ 		}
+ 
+ 		if (oldbpp != var->bits_per_pixel)
+ 		{
+ 			if ((error = fb_alloc_cmap(&display->cmap, 0, 0)))
+ 				return error;
+ 			tseng_install_cmap(con, info);
+ 		}
+ 	}
+ 
+ 	var->activate = 0;
+ 
+ 	return 0;
+ }
+ 
+ /*
+  * int tsengfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+  *
+  * Get the color map.
+  */
+ 
+ static int tsengfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+ {
+   if (con == currcon)		/* Current console? */
+     return (fb_get_cmap(cmap, &fb_display[con].var, kspc, fbhw->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(1<<fb_display[con].var.bits_per_pixel),
+ 		 cmap, kspc ? 0 : 2);
+   
+   return 0;
+ }
+ 
+ /* int tsengfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+  *
+  * Set the color map.
+  */
+ 
+ static int tsengfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+ {
+   int error;
+ 
+   if (!fb_display[con].cmap.len)    {    /* No colormap allocated? */ 
+     if ((error = fb_alloc_cmap(&fb_display[con].cmap,
+ 			       1 << fb_display[con].var.bits_per_pixel, 0)))
+       return error;
+   }
+   
+   if (con == currcon)		/* Current console? */
+     return(fb_set_cmap(cmap, &fb_display[con].var, kspc, fbhw->setcolreg, info));
+ 	else
+ 	  fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ 
+   return 0;
+ }
+ 
+ /*
+  * int tsengfb_ioctl(struct inode *inode, struct file *file,
+  *			unsigned int cmd, unsigned long arg, int con, struct fb_info *info)
+  *
+  * ET4000W32 specific ioctls.
+  */
+ 
+ static int tsengfb_ioctl(struct inode *inode, struct file *file,
+ 			 unsigned int cmd, unsigned long arg, int con, struct fb_info *info)
+ {
+   return -EINVAL;
+ }
+ 
+ static struct fb_ops tsengfb_ops =
+ {
+   tsengfb_open, tsengfb_release,
+   tsengfb_get_fix,
+   tsengfb_get_var, tsengfb_set_var,
+   tsengfb_get_cmap, tsengfb_set_cmap,
+   tsengfb_pan_display,
+   tsengfb_ioctl
+ };
+ 
+ /******************************************************************************
+  *									      *
+  * Generic functions.							      *
+  *									      *
+  ******************************************************************************/
+ 
+ static int w32fb_switch(int con, struct fb_info *info)
+ {
+   /*
+    * Do we have to save the colormap?
+    */
+ 
+   if (fb_display[currcon].cmap.len)
+     fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
+ 		fbhw->getcolreg, info);
+ 
+   tseng_set_var(&fb_display[con].var, 1);
+   currcon = con;
+   tseng_install_cmap(con, info);		/* Install new colormap. */
+ 
+   return 0;
+ }
+ 
+ /*
+  * int w32fb_update_var(int con)
+  *
+  * Update the 'var' structure (called by fbcon.c).
+  * Since it is called by a kernel driver, no range checking is done.
+  */
+ 
+ static int w32fb_updatevar(int con, struct fb_info *info)
+ {
+   struct fb_var_screeninfo * var = &fb_display[con].var;
+   int off;
+ 
+   off =  (var->xoffset + var->yoffset * var->xres_virtual)
+     * var->bits_per_pixel >> 3;
+   if (var->bits_per_pixel < 8)
+     off *= 4;
+ 
+   tseng_set_screen_base(off);
+   return 0;
+ }
+ 
+ /******************************************************************************
+  *									      *
+  * Initialization.							      *
+  *									      *
+  ******************************************************************************/
+ 
+ #if !defined(CONFIG_FB_SVGADETECT)
+ 
+ /*
+  * Utility functions -> will be replaced soon by svgadetect... 
+  */
+ 
+ static inline unsigned char read_reg(int address, unsigned char reg)
+ {
+   outb(reg, address);
+   return inb(address + 1);
+ }
+ 
+ static inline unsigned char read_reg_3C0(unsigned char reg, int vga_io_base)
+ {
+   unsigned char result;
+ 
+   (void) inb(0x3DA);
+   outb(reg & 0xDF, 0x3c0);
+   udelay(1);
+   result = inb(0x3C1);
+   (void) inb(0x3DA);
+   udelay(1);
+   outb(0x20, 0x3c0);
+   (void) inb(0x3DA);
+ 
+   return result;
+ }
+ 
+ static inline void write_reg(int address, unsigned char reg, unsigned char val)
+ {
+   outb(reg, address);
+   outb(val, address+1);
+ }
+ 
+ static inline void write_reg_3C0(unsigned char reg, unsigned char val,
+ 				 int vga_io_base)
+ {
+   (void) inb(0x3DA);
+   outb(reg & 0xDF, 0x3c0);
+   outb(val, 0x3c0);
+ 	(void) inb(0x3DA);
+ 	outb(0x20, 0x3c0);
+ 	(void) inb(0x3DA);
+ }
+ 
+ /*
+  * static int port_is_wr(int address, unsigned char mask)
+  *
+  * Test if the bits in 'mask' of the I/O port at 'address' are
+  * writeable.
+  */
+ 
+ __initfunc(static int port_is_wr(int address, unsigned char mask))
+ {
+   unsigned char old;
+   int result = 0;
+ 
+   old = inb(address);
+   outb(old | mask, address);
+   if ((inb(address) & mask) == mask) {
+     outb(old & ~mask, address);
+     if ((inb(address) & mask) == 0)
+       result = 1;
+   }
+   outb(old, address);
+   return result;
+ }
+ 
+ /*
+  * static int reg_is_wr(int address, unsigned char index, unsigned char mask)
+  *
+  * Test if the bits in 'mask' of the register at 'address' index
+  * 'reg' are writeable.
+  */
+ 
+ __initfunc(static int reg_is_wr(int address, unsigned char reg,
+ 				unsigned char mask))
+ {
+   unsigned char old;
+   int result = 0;
+ 
+   old = read_reg(address, reg);
+   write_reg(address, reg, old | mask);
+ 
+   if ((read_reg(address, reg) & mask) == mask) {
+     write_reg(address, reg, old & ~mask);
+ 
+     if ((read_reg(address, reg) & mask) == 0)
+       result = 1;
+   }
+ 
+ 	write_reg(address, reg, old);
+ 
+ 	return result;
+ }
+ 
+ /*
+  * static int detect_et4000w32(void)
+  *
+  * Check if a ET4000W32 card is present. This is a very simple
+  * test. It might fail if you have another card installed.
+  */
+ 
+ __initfunc(static int detect_et4000w32(void))
+ {
+ 	static const char *types[] =
+ 	{
+ 		"STDVGA",
+ 		"ET4000",
+ 		"ET4000W32",		/* 0 */
+ 		"ET4000W32i rev A",	/* 1 */
+ 		"ET4000W32p rev A",	/* 2 */
+ 		"ET4000W32i rev B",	/* 3 */
+ 		"",			/* 4 */
+ 		"ET4000W32p rev B",	/* 5 */
+ 		"ET4000W32p rev D",	/* 6 */
+ 		"ET4000W32p rev C",	/* 7 */
+ 		"",			/* 8 */
+ 		"",			/* 9 */
+ 		"",			/* 10 */
+ 		"ET4000W32i rev C",	/* 11 */
+ 		"",			/* 12 */
+ 		"",			/* 13 */
+ 		"",			/* 14 */
+ 		""			/* 15 */
+ 	};
+ 	static const int et4000_types[] =
+ 	{
+ 		STDVGA,
+ 		ET4000,
+ 		ET4000W32,		/* 0 */
+ 		ET4000W32I,		/* 1 */
+ 		ET4000W32P,		/* 2 */
+ 		ET4000W32I,		/* 3 */
+ 		STDVGA,			/* 4 */
+ 		ET4000W32P,		/* 5 */
+ 		ET4000W32P,		/* 6 */
+ 		ET4000W32P,		/* 7 */
+ 		STDVGA,			/* 8 */
+ 		STDVGA,			/* 9 */
+ 		STDVGA,			/* 10 */
+ 		ET4000W32I,		/* 11 */
+ 		STDVGA,			/* 12 */
+ 		STDVGA,			/* 13 */
+ 		STDVGA,			/* 14 */
+  		STDVGA			/* 15 */
+  	};
+ 
+ 	int type = 0;		/* STDVGA */
+ 
+ 	enable_tseng_ext();
+ 
+ 	if (port_is_wr(0x3CD, 0x3F))
+ 	{
+ 		if (reg_is_wr(0x3d4, 0x33, 0xF))
+ 		{
+ 			if (port_is_wr(0x3CB, 0x33))
+ 				type = 2 + (read_reg(0x217A, 0xEC) >> 4);
+ 			else
+ 			type = 1;		/* ET4000 */
+ 		}
+ 	}
+ 
+ 	printk("%s card detected.\n", types[type]);
+ 
+ 	if (et4000_types[type] != STDVGA) {
+ tseng->Card = TSENG_CARD;		/* defined via tseng_flag */
+ tseng->RamDac = TSENG_RAMDAC;	/* dito... */
+ 	}
+ 
+ 	disable_tseng_ext();
+ 	return et4000_types[type];
+ }
+ #endif			/* CONFIG_FB_SVGADETECT */
+ 
+ __initfunc(static int get_video_mode(const char *name))
+ {
+ 	int i;
+ 
+ 	for (i = 1; i < NUM_TOTAL_MODES; i++)
+ 	{
+ 		if (!strcmp(name, tsengfb_predefined[i].name))
+ 		{
+ 			tsengfb_default = tsengfb_predefined[i].var;
+ 			return i;
+ 		}
+ 	}
+ 	return -1;
+ }
+ 
+ __initfunc(void tsengfb_setup(char *options, char *ints))
+ {
+ 	char *this_opt;
+ 	int mode;
+ 
+ 	fb_info.fontname[0] = '\0';
+ 
+ 	if (!options || !*options)
+ 		return;
+ 	
+ 	for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ","))
+ 	{
+ 		if (!strcmp(this_opt, "inverse"))
+ 		{
+ 			tseng_inverse = 1;
+ 			fb_invert_cmaps();
+ 		}
+ 		else if (!strncmp(this_opt, "font:", 5))
+ 			strcpy(fb_info.fontname, this_opt+5);
+ 		else if (!strcmp(this_opt, "noaccel"))
+ 			tseng_enable_accel = 0;
+ 		else if (!strcmp(this_opt, "et4000"))
+ 			tseng->Chip= ET4000;
+ 		else if (!strcmp(this_opt, "et6000"))
+ 			tseng->Chip = ET6000;
+ 		else if (!strncmp(this_opt, "mem=", 4))
+ 			tseng->isa.BaseMem.phys = (__u8 *) simple_strtoul(this_opt+4, NULL, 0);
+ 		else if (!strncmp(this_opt, "io=", 3))
+ 			tseng->isa.IOBase.phys = (__u8 *) simple_strtoul(this_opt+3, NULL, 0);
+ 		else if (!strncmp(this_opt, "flag=", 5))
+ 			tseng_flag = simple_strtoul(this_opt+5, NULL, 0);
+ 		else if ((mode = get_video_mode(this_opt)) >= 0)
+ 			tseng_mode = mode;
+ 	}
+ }
+ 
+ /*
+  * basic initialization of fb-driver
+  */
+ 
+ __initfunc(int tsengfb_init(unsigned long memstart))
+ {
+   struct tsengfb_par par;
+   int err;
+ 
+   if (((long) tseng->isa.BaseMem.phys == 0) || ((long) tseng->isa.IOBase.phys == 0))
+     return memstart;
+ 
+   /*
+    * Map the video memory. On the Hades it is not necessary to do this
+    * if the video card is on the PCI bus, because the entire PCI
+    * memory area is mapped transparently.
+    */
+ 
+   if ((!MACH_IS_HADES) ||
+       ((unsigned long) tseng->isa.IOBase.phys < 0x80000000) ||
+       ((unsigned long) tseng->isa.IOBase.phys > 0x9FFFFFFF) ||
+       ((unsigned long) tseng->isa.IOBase.phys < 0xB0000000) ||
+       ((unsigned long) tseng->isa.IOBase.phys > 0xBFFFFFFF)) {
+     memstart = PAGE_ALIGN(memstart);
+     tseng->isa.IOBase.virt = (__u8 *) kernel_map((unsigned long) tseng->isa.IOBase.phys, tseng->isa.IOBase.size,
+ 						 KERNELMAP_NOCACHE_SER, &memstart);
+   } else {
+     tseng->isa.IOBase.virt = tseng->isa.IOBase.phys;
+     tseng->isa.BaseMem.virt = tseng->isa.BaseMem.phys;
+   }
+ 
+   /*
+    * If not given on the commandline (et6000) detect the type of
+    * ET4000 card.
+    */
+ 
+   IGNORE_CONST(tseng_iobase) = tseng->isa.IOBase.virt;		/* prep. IO */
+ 
+   if (tseng->Chip == STDVGA)	{
+     tseng->Chip = detect_et4000w32();
+     if (tseng->Chip == STDVGA)
+       return memstart;
+   }
+ 
+   if (tseng->Chip > ET4000)  {
+     __u8 tmp;
+     tmp = crt_r(0x37);
+     tseng->isa.BaseMem.size = ((tmp & 8) ? 256 : 1024) * ((tmp & 1) ? 4 : 2);
+     if (crt_r(0x32) & 0x80)
+       tseng->isa.BaseMem.size *= 2;
+   } else
+     tseng->isa.BaseMem.size = 1024;
+   tseng->isa.BaseMem.size *= 1024;
+ 
+   /* defered mapping of mem_base until size is known */
+   if (!tseng->isa.BaseMem.virt) {
+     tseng->isa.BaseMem.virt = (__u8 *) kernel_map((unsigned long)tseng->isa.BaseMem.phys,
+ 						  tseng->isa.BaseMem.size, KERNELMAP_NO_COPYBACK, &memstart);
+     memstart += PAGE_SIZE;
+   }
+ 
+   if (tseng->Chip < ET4000W32)
+     tseng_enable_accel = 0;		/* Disable accelerator. */
+ 
+   /* set aperture characteristics */
+   if (tseng->Chip < ET4000W32P) {
+     w32_aperturesize = 8 * 1024;
+     w32_aperturebase = 0xb8000;
+   } else {
+     w32_aperturesize = 1024 * 1024;
+     w32_aperturebase = 0;           /* align to basemem */
+   }
+ 
+   /*
+    * Initiate frame buffer.
+    */
+ 
+   fbhw = &tseng_switch;
+ 
+   fbhw->init();
+ 
+   switch (tseng->Chip) {
+   case ET4000:
+     strcpy(tsengfb_name, "ET4000");
+     break;
+   case ET4000W32:
+     strcpy(tsengfb_name, "ET4000/W32");
+     break;
+   case ET4000W32I:
+     strcpy(tsengfb_name, "ET4000/W32i");
+     break;
+   case ET4000W32P:
+     strcpy(tsengfb_name, "ET4000/W32p");
+     break;
+   case ET6000:
+     strcpy(tsengfb_name, "ET6000");
+     break;
+   }
+   strcpy(fb_info.modename, tsengfb_name);
+   fb_info.changevar = NULL;
+   fb_info.node = -1;
+   fb_info.fbops = &tsengfb_ops;
+   fb_info.disp = &disp;
+   fb_info.switch_con = &w32fb_switch;
+   fb_info.updatevar = &w32fb_updatevar;
+   fb_info.blank = &tseng_blank;
+   
+   err = register_framebuffer(&fb_info);
+   if (err < 0)
+     return memstart;
+   
+   if (tseng_mode == -1)
+     tseng_get_video(&tsengfb_default);
+   
+   fbhw->decode_var(&tsengfb_default, &par);
+   fbhw->encode_var(&tsengfb_default, &par);
+   tseng_set_var(&tsengfb_default, 0);
+   tsengfb_get_var(&disp.var, -1, &fb_info);
+   w32fb_set_disp(-1, &fb_info);
+   
+   tseng_install_cmap(0, &fb_info);
+   
+   /* TODO: This driver cannot be unloaded yet. */
+   MOD_INC_USE_COUNT;
+      
+   return memstart;
+ }
+      
+ 
+ /* ----------------------------------------------
+  *    Text console acceleration
+  * ----------------------------------------------
+  */
+ #define USE_BLANK_SCREEN	0		/* blank screen if moves > 1/2 screen */
+ #define BLANK_DURING_PUTCS	0		/* blank screen during putcs */
+ #define LARGE_APERTURE 0			/* large aperture with 1MB range */
+ 
+ #define INIT_w32reg(w32)   \
+     volatile W32MMReg *w32reg = (volatile W32MMReg *)(w32)
+ 
+      /* "Butterfly" translation table
+       *
+       * unfortunately, the W32i uses the "wrong" bitalignment in Pixel Amplification
+       * Mode. Therefore, we have to swap the bits (D0<->D7, D1<->D6, ...)...
+       */
+ 
+ static __u8 butterfly[256] = {
+   0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x70, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+   0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x78, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+   0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x74, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+   0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x7c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+   0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x72, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+   0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x7a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+   0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x76, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+   0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x7e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+   0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x71, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+   0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x79, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+   0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x77, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+   0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x7d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+   0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x73, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+   0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x7b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+   0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x77, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+   0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x7f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff};
+ 
+ /*
+  *  `switch' for the low level operations
+  */
+ 
+ static struct display_switch fbcon_w32 = {
+   fbcon_w32_init, fbcon_w32_bmove, fbcon_w32_clear, fbcon_w32_putc, fbcon_w32_putcs,
+   fbcon_w32_revc
+ };
+ 
+ /*
+  * expand pattern given in `col` to a unsigned long
+  */
+ 
+ static __inline__ __u32 expand_attribute(__u8 ch)
+ {
+   __u32 expanded;
+   __asm__ __volatile__ (
+ 			"moveb	%1, %0		\n\t"
+ 			"asl	#8, %0		\n\t"
+ 			"moveb	%1, %0		\n\t"
+ 			"movew	%0, %1		\n\t"
+ 			"swap	%0		\n\t"
+ 			"movew	%1, %0		\n\t"
+ 			: "=d" (expanded), "=d" (ch) : "1" (ch));
+   return expanded;
+ }
+ 
+ /*
+  * set grafic subsystem into planar mode (for W32i)
+  * set the hint-flag
+  */
+ static void w32_setplanar(int mode)
+ {
+   static __u8 vsconf1, gdcmap;
+   __u32 flags;
+ 
+   if (mode) {
+     if (w32_blitting++)
+       return;
+     /* set planar mode */
+     save_flags(flags);
+     cli();
+     outb(0x03, 0x3bf); outb(0xa0, 0x3d8);		/* unlock special regs */
+     if (tseng->Chip < ET4000W32P) {
+       gdcmap = gfx_r(GFX_MISC);
+       outb((gdcmap & 0xf3) | 0x04, GFX_DATA);
+       vsconf1 = crt_r(0x36);
+ #if LARGE_APERTURE
+       crt_wb((vsconf1 & 0xc7) | 0x38, 0x36);
+       gen_wb(0x01, 0xf7, 0x217a);
+ #else
+       crt_wb((vsconf1 & 0xc7) | 0x28, 0x36);
+ #endif
+     }
+     restore_flags(flags);
+   } else {
+     if (--w32_blitting)
+       return;
+     /* reset to previous mode */
+     save_flags(flags);
+     cli();
+     if (tseng->Chip < ET4000W32P) {
+ #if LARGE_APERTURE
+       gen_wb(0x00, 0xf7, 0x217a);
+ #endif
+       crt_wb(vsconf1 & 0xd7, 0x36);
+       gfx_wb(gdcmap, GFX_MISC);
+     }
+     outb(0x29, 0x3d8); outb(0x01, 0x3bf);		/* lock again */
+     restore_flags(flags);
+   }
+ }
+ 
+ static int fbcon_w32_init_done = 0;
+ 
+ void fbcon_w32_init(struct display *p)
+ {
+   register __u8 * realbase;
+ 
+   p->next_line=p->var.xres_virtual;
+ 
+   /* connect ACL address space to the world */
+   realbase = tseng->isa.BaseMem.virt + w32_aperturebase;
+ 
+   if (tseng->Chip < ET4000W32P) {
+     w32_accelmap   = realbase + 0 * w32_aperturesize;;
+     w32_patternbuf = (W32_PatternBuf *) (realbase + 2 * w32_aperturesize);
+   } else {
+     w32_accelmap   = realbase + 2 * w32_aperturesize;
+     w32_patternbuf = (W32_PatternBuf *) (realbase + (__u32) w32_ACLpattern);
+   }
+   w32_aperture1  = realbase + 1 * w32_aperturesize;
+   w32_registermap= realbase + 4 * w32_aperturesize - 256;
+ 
+   if (!fbcon_w32_init_done) {
+     INIT_w32reg(w32_registermap);
+ 
+     fbcon_w32_init_done = 1;
+     w32_setplanar(1);
+ 
+     /* reset accelerator to power-up condition */
+     w32reg->ACLctl.Suspend = W32ACL_TO;
+     while (w32reg->ACLctl.Status & W32ACL_RDST);
+     w32reg->ACLctl.Suspend = 0;
+ 
+     /* initialize MMU - all addresses are in ACL address space */
+     if (tseng->Chip < ET4000W32P) {
+       w32reg->MMUctl.BasePtr0= cpu_to_le32((__u32)&w32_ACLpattern->SpriteData);
+       w32reg->MMUctl.BasePtr1= cpu_to_le32((__u32)&w32_ACLpattern->SpriteData);
+       w32reg->MMUctl.BasePtr2= cpu_to_le32((__u32)w32_ACLpattern);	/*ACL space */
+       w32reg->MMUctl.Control = 0x71;	       /* aperture 0 is accelerated */
+     } else {
+       w32reg->MMUctl.BasePtr0= cpu_to_le32(0);
+       w32reg->MMUctl.BasePtr1= cpu_to_le32(0x100000);
+       w32reg->MMUctl.BasePtr2= cpu_to_le32((__u32) &w32_ACLpattern->SpriteData);
+       w32reg->MMUctl.Control = 0x74;	       /* aperture 2 is accelerated */
+     }
+     /* preset the accelerator */
+     if (tseng->Chip < ET6000)
+       w32reg->ACLctl.SyncEnable = 0;
+     else {
+       w32reg->ACLctl.Power = 1;
+       w32reg->ACLctl.ET6000Conf = 0;
+     }
+ 
+     w32reg->ACLctl.IrqMask = 0;
+ 
+     /* now, we preset the ACL with some save values */
+     w32reg->ACLreg.PatternAddress     = (void *)cpu_to_le32((__u32)&w32_ACLpattern->BGcol[0]);
+     w32reg->ACLreg.SourceAddress      = (void *)cpu_to_le32((__u32)&w32_ACLpattern->FGcol[0]);
+     w32reg->ACLreg.PatternYOffset     = cpu_to_le16(4-1);	/* mostly 4 by 2 maps */
+     w32reg->ACLreg.SourceYOffset      = cpu_to_le16(4-1);	/* mostly 4 by 2 maps */
+     w32reg->ACLreg.DestinationYOffset = cpu_to_le16(p->var.xres_virtual - 1);
+     w32reg->ACLreg.XYDirection	      = 0;				/* TopDown, Left2Right */
+     w32reg->ACLreg.SourceWrap	= cpu_to_le16(W32Wrap4by2);
+     w32reg->ACLreg.PatternWrap	= cpu_to_le16(W32Wrap4by2);
+     *(__u32 *) &w32reg->ACLreg.XCount		= 0;
+     w32reg->ACLreg.BackgroundROP = W32BLTpatcopy;
+     w32reg->ACLreg.ForegroundROP = W32BLTsrccopy;
+ 
+     if (tseng->Chip < ET6000) {
+       w32reg->ACLreg.RoutingControl = W32RouteNoAddrNoData;
+       w32reg->ACLreg.ReloadControl  = W32ReloadNone;
+     } else {
+       w32reg->ACLreg.SteppingInhibit = 0;
+       w32reg->ACLreg.MixControl      = 0x33;
+     }
+ 
+     if (tseng->Chip < ET4000W32P) {
+       *(__u32 *) &w32reg->ACLreg.XPosition    = 0;
+       w32reg->ACLreg.VirtualBusSize = 0;
+     } else {
+       w32reg->W32pACLreg.NQXPosition =
+ 	w32reg->W32pACLreg.NQYPosition = cpu_to_le16(0);
+       w32reg->ACLreg.PixelDepth  = (p->var.bits_per_pixel == 16) ? 1 : 0;
+       w32reg->ACLctl.OpState = 0x10;
+       w32reg->ACLreg.MixAddress = 0;
+       w32reg->ACLreg.MixYOffset = cpu_to_le16(64 - 1);	/* ???? */
+     }
+     
+     w32_setplanar(0);
+   }
+ }
+ 
+ void fbcon_w32_bmove(struct display *p, int sy, int sx, int dy, int dx,
+ 		     int height, int width)
+ {
+ #if USE_BLANK_SCREEN
+   static int screen_blanked = 0;
+ #endif
+ 
+   int	  cntx, cnty,
+           xres_virtual = p->var.xres_virtual,
+ 	  dir = 0;
+ 
+   __u32 src, dst;
+     
+   INIT_w32reg(w32_registermap);
+ 
+   {	/* subblock for (hopefully) better register optimization */
+     int srcx, srcy,
+       dstx, dsty,
+       fwidth = 8;
+ 
+     srcx = fwidth * sx;
+     dstx = fwidth * dx;
+     cntx = fwidth * width;
+ 
+     srcy = sy * p->fontheight;
+     dsty = dy * p->fontheight;
+     cnty = height * p->fontheight;
+ 
+ 
+     src = srcx + srcy * xres_virtual;
+     dst = dstx + dsty * xres_virtual;
+ 
+     if (dstx > srcx) {
+       dir |= 0x01;
+       src += cntx - fwidth;
+       dst += cntx - fwidth;
+     }
+ 
+     if (dsty > srcy) {
+       dir |= 0x02;
+       src += (cnty - 1) * xres_virtual;
+       dst += (cnty - 1) * xres_virtual;
+     }
+   }
+ 
+   /* - do the BLITMOVE - */
+   w32_setplanar(1);
+ 
+   if (tseng->Chip < ET4000W32P)
+     *(__u32 *) &w32reg->ACLreg.XPosition = 0;
+   w32reg->ACLreg.SourceAddress      = (void *)cpu_to_le32(src);
+   w32reg->ACLreg.SourceYOffset      = cpu_to_le16(xres_virtual-1);
+   w32reg->ACLreg.DestinationYOffset = cpu_to_le16(xres_virtual-1);
+   w32reg->ACLreg.XYDirection	      = dir;
+   w32reg->ACLreg.SourceWrap	      = cpu_to_le16(W32WrapNone);
+   w32reg->ACLreg.XCount	      = cpu_to_le16(cntx-1);
+   w32reg->ACLreg.YCount	      = cpu_to_le16(cnty-1);
+ 
+ #if USE_BLANK_SCREEN
+   if (height > (p->conp->vc_rows >> 2)) {
+     register __u8 TSmode;
+ 
+     screen_blanked = TSmode = seq_r(0x01);
+     seq_wb(TSmode | 0x20, 0x01);
+   }
+ #endif
+ 
+   w32reg->ACLreg.DestinationAddress = (void *)cpu_to_le32(dst);
+   if (tseng->Chip < ET4000W32P)
+     w32reg->ACLctl.OpState = W32ACL_StartBlit;
+   W32_WaitBlit(w32reg);
+ 
+ #if USE_BLANK_SCREEN
+   if (screen_blanked) {
+     seq_wb(screen_blanked, screen_blanked);
+     screen_blanked= 0;
+   }
+ #endif
+ 
+   w32reg->ACLreg.SourceAddress	  = (void *)cpu_to_le32((__u32)&w32_ACLpattern->FGcol[0]);
+   w32reg->ACLreg.SourceYOffset	  = cpu_to_le16(4-1);	/* mostly 4 by 2 maps */
+   w32reg->ACLreg.SourceWrap		  = cpu_to_le16(W32Wrap4by2);
+   w32reg->ACLreg.XYDirection = 0;		/* restore default value */
+   w32_setplanar(0);
+ }
+ 
+ void fbcon_w32_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+ 		       int height, int width)
+ {
+   int	  cntx, cnty,
+           xres_virtual = p->var.xres_virtual;
+ 
+   __u32 dst;
+ 
+   INIT_w32reg(w32_registermap);
+ 
+   w32_setplanar(1);
+   w32_patternbuf->FGcol[0] =
+     w32_patternbuf->FGcol[1] = expand_attribute(attr_bgcol_ec(p,conp));
+ 
+   {	
+     register int srcx, srcy,
+                  fwidth = 8;
+ 
+     srcx = sx * fwidth; srcy = sy * p->fontheight;
+     cntx = width * fwidth; cnty = height * p->fontheight;
+     dst  = srcx + srcy * xres_virtual;
+   }
+ 
+   if (tseng->Chip < ET4000W32P) {
+     *(__u32 *) &w32reg->ACLreg.XPosition = 0;
+     w32reg->ACLreg.DestinationYOffset = cpu_to_le16(xres_virtual - 1);
+     w32reg->ACLreg.XCount			  = cpu_to_le16(cntx-1);
+     w32reg->ACLreg.YCount			  = cpu_to_le16(cnty-1);
+     w32reg->ACLreg.DestinationAddress = (void *)cpu_to_le32(dst);
+     w32reg->ACLctl.OpState			  = W32ACL_StartBlit;		/* start ... */
+   } else {
+     w32reg->ACLreg.DestinationYOffset = cpu_to_le16(xres_virtual - 1);
+     w32reg->ACLreg.XCount			  = cpu_to_le16(cntx-1);
+     w32reg->ACLreg.YCount			  = cpu_to_le16(cnty-1);
+     w32reg->ACLreg.DestinationAddress = (void *)cpu_to_le32(dst);
+   }
+   W32_WaitBlit(w32reg);
+   w32_setplanar(0);
+ }
+ 
+ void fbcon_w32_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
+ {
+   int	  xres_virtual = p->next_line,
+        	  cntx = p->fontwidth,
+        	  cnty = p->fontheight;
+ 
+   INIT_w32reg(w32_registermap);
+ 
+   w32_setplanar(1);
+ 
+   w32_patternbuf->FGcol[0] =
+   w32_patternbuf->FGcol[1] = expand_attribute(attr_fgcol(p, conp));
+   w32_patternbuf->BGcol[0] =
+   w32_patternbuf->BGcol[1] = expand_attribute(attr_bgcol(p, conp));
+ 	
+ 
+   if (tseng->Chip < ET4000W32P)  {
+     *(__u32 *) &w32reg->ACLreg.XPosition = 0;
+     w32reg->MMUctl.BasePtr0= cpu_to_le32(yy * cnty * xres_virtual);
+   } else
+     w32reg->MMUctl.BasePtr2= cpu_to_le32(yy * cnty * xres_virtual);
+ 
+   w32reg->ACLreg.DestinationYOffset = cpu_to_le16(xres_virtual - 1);
+   w32reg->ACLreg.XCount		    = cpu_to_le16(cntx - 1);
+   w32reg->ACLreg.YCount		    = cpu_to_le16(cnty - 1);
+ 
+   if (tseng->Chip == ET6000) {
+     w32reg->ACLreg.MixControl	    = 0x32;
+   } else {
+     w32reg->ACLreg.RoutingControl   = W32RouteNoAddrMixData;
+     w32reg->ACLctl.SyncEnable	    = W32ACL_QueueWait;
+   }
+ 
+   w32reg->ACLreg.DestinationAddress = (void *)cpu_to_le32(yy * cnty * xres_virtual + xx * cntx);
+ 
+   do {
+     register int col= (tseng->Chip < ET4000W32P) ? xx : 0;
+ 
+     register int rows=cnty;
+     register __u8 ch = (__u8)c,
+ 	        *acl = w32_accelmap,
+        		*cdat = p->fontdata + ch * rows;
+ 
+ 		/* here goes the mixmode-magic */
+     for (; rows-- ;)
+       acl[col] = butterfly[*cdat++];
+   } while (0);
+ 
+   if (tseng->Chip == ET6000) {
+     w32reg->ACLreg.MixControl   = 0x33;
+   } else {
+     w32reg->ACLreg.RoutingControl = W32RouteNoAddrNoData;
+     w32reg->ACLctl.SyncEnable	= 0x00;		/* disable waitstates */
+   }
+   w32_setplanar(0);				/* set planar mode */
+ }
+ 
+ void fbcon_w32_putcs(struct vc_data *conp, struct display *p, const char *s,
+ 		       int count, int yy, int xx)
+ {
+   int xres_virtual = p->next_line,
+ 	      cntx = p->fontwidth,
+ 	      cnty = p->fontheight;
+ 
+   INIT_w32reg(w32_registermap);
+ 
+   w32_setplanar(1);
+ 
+   w32_patternbuf->FGcol[0] =
+     w32_patternbuf->FGcol[1] = expand_attribute(attr_fgcol(p, conp));
+   w32_patternbuf->BGcol[0] =
+     w32_patternbuf->BGcol[1] = expand_attribute(attr_bgcol(p, conp));
+ 
+   if (tseng->Chip < ET4000W32P) {
+     w32reg->MMUctl.BasePtr0= cpu_to_le32(yy * cnty * xres_virtual);
+     *(__u32 *) &w32reg->ACLreg.XPosition = 0;
+   }  
+   w32reg->ACLreg.DestinationYOffset = cpu_to_le16(xres_virtual - 1);
+   w32reg->ACLreg.XCount		  = cpu_to_le16(cntx - 1);
+   w32reg->ACLreg.YCount		  = cpu_to_le16(cnty - 1);
+ 
+   if (tseng->Chip == ET6000) {
+     w32reg->ACLreg.MixControl		  = 0x32;
+   } else {
+     w32reg->ACLreg.RoutingControl	  = W32RouteNoAddrMixData;
+     w32reg->ACLctl.SyncEnable      	  = W32ACL_QueueWait;
+   }
+ 
+   do  {
+     register __u8 *fontdata=p->fontdata,
+                   *acl=w32_accelmap;
+ 
+     if (tseng->Chip < ET4000W32P) {
+       while (count--) {
+ 	register int rows = cnty;
+ 	register __u8 *cdat = fontdata + ((__u8)*s++) * rows;
+ 
+ 	for ( ; rows-- ;)
+ 	  acl[xx] = butterfly[*cdat++];
+ 	xx +=1;
+       }
+     } else {
+       while (count--) {
+ 	register int rows = cnty;
+ 	register __u8 *cdat = fontdata + ((__u8)*s++) * rows;
+ 
+ 	w32reg->ACLreg.DestinationAddress = (void *)cpu_to_le32(yy * cnty * xres_virtual + xx * cntx);
+ 	for ( ; rows-- ;)
+ 	  acl[0] = butterfly[*cdat++];
+ 	xx +=1;
+       }
+     }
+   } while (0);
+ 
+   if (tseng->Chip == ET6000) {
+     w32reg->ACLreg.MixControl		  = 0x33;
+   } else {
+     w32reg->ACLreg.RoutingControl = W32RouteNoAddrNoData;
+     w32reg->ACLctl.SyncEnable     = 0x00;	/* disable waitstates */
+   }
+   w32_setplanar(0);
+ }
+ 
+ void fbcon_w32_revc(struct display *p, int xx, int yy)
+ {
+   __u32 dest;
+   int xres_virtual=p->next_line, rows;
+ 
+   INIT_w32reg(w32_registermap);
+   if (!w32_blitting) {
+     w32_setplanar(1);
+ 
+     dest = yy * p->fontheight * xres_virtual + xx * 8;
+ 
+     for (rows = p->fontheight ; rows-- ; dest += xres_virtual) {
+       w32reg->MMUctl.BasePtr1 = cpu_to_le32(dest);
+       ((__u32 *)w32_aperture1)[0] ^= 0x0f0f0f0f;
+       ((__u32 *)w32_aperture1)[1] ^= 0x0f0f0f0f;
+     }
+     w32_setplanar(0);
+   }
+ }
+ 
+ #if ET4000W32_SHOW_LOGO
+ /*
+  * paint the linux-logo
+  * we use our own painting-routine, so we can set p->screen_base
+  * to the address in GDC-space, not CPU-space.
+  *
+  * Gadgets:
+  *   the logo is painted into CRTCB
+  *   after the first clear-screen, CRTCB is set to the right border
+  *   CRTCB is switched off after the first console switch
+  */
+ __initfunc(void et4000w32_show_logo(void))
+ {
+   return;
+ }
+ #endif	/* ET4000W32_SHOW_LOGO */
+ 
+ #ifdef MODULE
+ int init_module(void)
+ {
+   return tsengfb_init((unsigned long) NULL);
+ }
+ 
+ void cleanup_module(void)
+ {
+   /*
+    * Not reached, because the usecount will never
+    * be decremented to zero.
+    */
+ 
+   unregister_framebuffer(&fb_info);
+ }
+ #endif
+ 
+ #if 0
+ EXPORT_SYMBOL(w32_bitblt);
+ EXPORT_SYMBOL(w32_mem_base);
+ #endif
+ 
+ 
+ 
+ 
*** /dev/null	Sun Jun 16 21:20:44 1996
--- linux/drivers/video/tsengfb.h	Tue Feb  3 00:45:45 1998
***************
*** 0 ****
--- 1,244 ----
+ /*
+  *	linux/drivers/video/tsengfb.h		support for Tseng accelerators
+  *										$Revision: 2.1.77.2 $
+  *
+  *	J. Orschiedt	06/97
+  *  W. Klaren		12/97
+  *
+  * $Log: tsengfb.h,v $
+  * Revision 2.1.77.2  1998/02/01 20:31:14  orschied
+  * *** empty log message ***
+  *
+  * Revision 2.1.77.1  1998/01/14 07:24:09  orschied
+  * - increased rev-nr
+  *
+  * Revision 2.1  1998/01/14 07:23:10  orschied
+  * - forced rev-nr increase
+  *
+  * Revision 2.0  1998/01/14 07:20:58  orschied
+  * - increased revison number
+  *
+  */
+ 
+ /* the MMU Control Structure is located at Offset 0 in Memory Map */
+ typedef struct {
+     __u32	BasePtr0,	/* used for CPU-access */
+ 		BasePtr1,	/* used for ACL-access */
+ 		BasePtr2,	/* used for Forground/Background */
+ 		res_long;
+     __u8	res_byte[3],
+ 		Control;
+ } MMU_Control;
+ 
+ /* the ACL control registers (non-queued) are located at Offset 0x30 in Memory Map */
+ typedef struct {
+     volatile __u8 Suspend,
+ 		  OpState,
+ 		  SyncEnable,
+ #define ET6000Conf  SyncEnable
+ 		  res_byte,
+ 		  IrqMask,
+ 		  IrqStatus,
+ 		  Status,
+ 		  Power;		/* ET6000 */
+ } ACL_Control;
+ 
+ typedef struct {
+     __u16   NQXPosition,
+         NQYPosition;
+ } W32p_ACL_Registers;
+ 
+ /* the ACL registers (queued) are located at Offset 0x80 in Memory Map */
+ typedef struct {
+ 	__u8	*PatternAddress,
+ 		*SourceAddress;
+ 	__u16	PatternYOffset,
+ 		SourceYOffset,
+ 		DestinationYOffset;
+ 	__u8	VirtualBusSize,
+ #define PixelDepth  VirtualBusSize
+ 		XYDirection;
+ 	__u16	PatternWrap,
+ 		SourceWrap,
+ 		XPosition,
+ 		YPosition,
+ 		XCount,
+ 		YCount;
+ 	__u8	RoutingControl,
+ #define MixControl  RoutingControl
+ 		ReloadControl,
+ #define SteppingInhibit ReloadControl
+ 		BackgroundROP,
+ 		ForegroundROP,
+ 		*DestinationAddress,
+ 		/* --- W32p extensions --- */
+ 		*MixAddress;
+ 	__u16 MixYOffset,
+ 		ErrorTerm,
+ 		DeltaMinor,
+ 		DeltaMajor;
+ } ACL_Registers;
+ 
+ /*
+  * the W32 memory map has the following structure:
+  *
+  *   0x00	MMU Control Registers
+  *   0x30	ACL Control Registers
+  *   0x80	ACL Operation Registers
+  *
+  * GNU-C (at least 2.7.2) isn't able to align to arbitrary boundaries,
+  * therefore 'aargh' is used to fill up the alignment.
+  * 
+  */
+ 
+ typedef struct {
+ 	MMU_Control	MMUctl;
+ 	__u32		aargh  __attribute__ ((aligned (0x10)));
+ 	ACL_Control	ACLctl __attribute__ ((aligned (0x10)));  
+     W32p_ACL_Registers  W32pACLreg;
+ 	ACL_Registers	ACLreg __attribute__ ((aligned (0x80)));  
+ } W32MMReg;
+ 
+ #define W32_WaitQueue(x) 	while ((x)->ACLctl.Status & 0x01)
+ #define W32_WaitBlit(x)		while ((x)->ACLctl.Status & 0x02)
+ 
+ /*
+  * definitions for offset into Patternbuffer
+  */
+ 
+ typedef struct {
+ 	/* the Erase, Foreground and Background-patterns may be up to 8x4 */
+ 	__u32	FGcol[2];			/* ForeGround color */
+ 	__u32	BGcol[2];			/* BackGround color */
+ 	__u32	SpriteData[256];	/* Sprite Data Buffer */
+ } W32_PatternBuf;
+ 
+ /*
+  * ET4000W32 Accelerator related definitions
+  */
+ 
+ /*
+  * ACL Suspend/Terminate Register
+  */
+ #define W32ACL_TO			(1 << 4)	/* Terminate Accelerator Operation */
+ #define W32ACL_SO			(1 << 0)	/* Suspend Accelerator Operation */
+ 
+ /*
+  * ACL Operation State
+  */
+ #define W32ACL_RMO			(1 << 3)	/* Resume Accelerator Operation */
+ #define W32ACL_RSO			(1 << 0)	/* restore Accelerator State */
+ #define W32ACL_StartBlit	(W32ACL_RMO | W32ACL_RSO)
+ 
+ /*
+  * ACL Sync Enable Register
+  */
+ #define W32ACL_QueueWait	(1 << 0)	/* write to full queue will be "wait-stated" */
+ 
+ /*
+  * ACL Interrupt Mask Register
+  */
+ #define W32ACL_WfENA	(1 << 2)	/* enable IRQ when write to full Queue (EVENT) */
+ #define W32ACL_RdENA	(1 << 1)	/* enable IRQ on empty Queue (EVENT) and busy->idle trans. */
+ #define W32ACL_WrENA	(1 << 0)	/* enable IRQ while Queue not empty (STATE) */
+ 
+ /*
+  * ACL Interrupt Status Register
+  */
+ #define W32ACL_WfIRQ	(1 << 2)	/* interrupt caused by Write Fault */
+ #define W32ACL_RdIRQ	(1 << 1)	/* interrupt caused by Read */
+ #define W32ACL_WrIRQ	(1 << 0)	/* interrupt caused by Write */
+ 
+ /*
+  * ACL Accelerator Status Register
+  */
+ #define W32ACL_SSO	(1 << 3)	/* current op is screen-to-screen */
+ #define W32ACL_XYST	(1 << 2)	/* processing an X/Y block */
+ #define W32ACL_RDST	(1 << 1)	/* Accelerator busy or Queue not empty */
+ #define W32ACL_WRST (1 << 0)	/* Accelerator Queue is full */
+ 
+ /*
+  * Blitter operations
+  */
+ #define	W32BLTclear			0x00	/* 0 */
+ #define W32BLTnor			0x11	/* NOT src AND NOT dst */
+ #define W32BLTandInverted	0x22	/* NOT src AND dst */
+ #define W32BLTcopyInverted	0x33	/* NOT src */
+ #define W32BLTandReverse	0x44	/* src AND NOT dst */
+ #define W32BLTinvert		0x55	/* NOT dst */
+ #define W32BLTpatInvert		0x5a	/* dst XOR pat */
+ #define W32BLTxor			0x66	/* src XOR dst */
+ #define W32BLTnand			0x77	/* NOT src OR NOT dst */
+ #define W32BLTand			0x88	/* src AND dst */
+ #define W32BLTequiv			0x99	/* NOT src XOR dst */
+ #define	W32BLTnoop			0xaa	/* dst */
+ #define W32BLTorInverted	0xbb	/* NOT src OR dst */
+ #define W32BLTsrccopy		0xcc	/* src */
+ #define W32BLTorReverse		0xdd	/* src OR NOT dst */
+ #define W32BLTor			0xee	/* src OR dst */
+ #define W32BLTpatcopy		0xf0	/* pat */
+ #define W32BLTset			0xff	/* 1 */
+ 
+ /*
+  * x-by-y tile for pattern and source map wrapping
+  */
+ #define W32WrapNone	0x77
+ #define W32Wrap4by0	0x72
+ #define W32Wrap8by0	0x73
+ #define W32Wrap16by0	0x74
+ #define W32Wrap32by0	0x75
+ #define W32Wrap64by0	0x76
+ 
+ #define W32Wrap0by1	0x07
+ #define W32Wrap4by1	0x02
+ #define W32Wrap8by1	0x03
+ #define W32Wrap16by1	0x04
+ #define W32Wrap32by1	0x05
+ #define W32Wrap64by1	0x06
+ 
+ #define W32Wrap0by2	0x17
+ #define W32Wrap4by2	0x12
+ #define W32Wrap8by2	0x13
+ #define W32Wrap16by2	0x14
+ #define W32Wrap32by2	0x15
+ #define W32Wrap64by2	0x16
+ 
+ #define W32Wrap0by4	0x27
+ #define W32Wrap4by4	0x22
+ #define W32Wrap8by4	0x23
+ #define W32Wrap16by4	0x24
+ #define W32Wrap32by4	0x25
+ #define W32Wrap64by4	0x26
+ 
+ #define W32Wrap0by8	0x38
+ #define W32Wrap4by8	0x31
+ #define W32Wrap8by8	0x32
+ #define W32Wrap16by8	0x33
+ #define W32Wrap32by8	0x34
+ #define W32Wrap64by8	0x35
+ 
+ 
+ /*
+  * Routing Control
+  */
+ 
+ #define W32RouteNoAddrNoData	0x00
+ #define W32RouteNoAddrSrcData	0x01
+ #define W32RouteNoAddrMixData	0x02
+ #define W32RouteNoAddrXCount	0x04
+ #define W32RouteNoAddrYCount	0x05
+ 
+ #define W32RouteAddrNoData	0x00
+ #define W32RouteAddrSrcData	0x01
+ #define W32RouteAddrMixData	0x02
+ #define W32RouteAddrXCount	0x04
+ #define W32RouteAddrYCount	0x05
+ 
+ /*
+  * Reload Control
+  */
+ 
+ #define W32ReloadNone		0x00
+ #define W32ReloadSource		0x01
+ #define W32ReloadPattern	0x02
+ #define W32ReloadAll		0x03
*** /dev/null	Sun Jun 16 21:20:44 1996
--- linux/drivers/video/vgafb.h	Fri Feb 27 23:22:52 1998
***************
*** 0 ****
--- 1,218 ----
+ /* tab-width: 4 */
+ 
+ /*
+  * linux/drivers/video/vgafb.h		$Revision: 2.1.77.5 $
+  *
+  *   purpose: defines and macros for PC-compatible hardware
+  *			  possibly requires atari_isahw.h
+  *
+  *    Copyright (C) 1998 J. Orschiedt
+  *
+  *    based on retz3fb.h
+  *	 Copyright (C) 1997 Jes Sorensen
+  *
+  * Changes against retz3fb.h:
+  *   vgafb.h contains the subset for VGA-controller. So the card-specific
+  *         header-file only needs the specific extensions.
+  *
+  * 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.
+  */
+ 
+ /*
+  * Macros to access the indexed VGA-registers.
+  *   The IDX-register is at offset port, the DATA at port+1
+  *
+  *   we have four different sets of macros:
+  *
+  *  xxx_r	read the given index register
+  *
+  *  xxx_wb	access the registers in bytemode, means we need always
+  *		two writes to put the data to the indexed register
+  *  xxx_wm	access the registers in wordmode, motorola-fashion
+  *		IDX-register in upper byte, register-data in lower byte
+  *  xxx_wi	access the registers in wordmode, intel-fashion
+  *		IDX-register in lower byte, register-data in upper byte
+  */
+  
+ /*
+  * generic read/write registerpair
+  */
+ #define gen_r(sreg, sbase)	\
+     ({outb((sreg), (sbase)); inb((sbase)+1);})
+  
+ #define gen_wb(sdat,sreg,sbase)	\
+ 	do {	outb((sreg),(sbase));	\
+ 			outb((sdat),(sbase+1));\
+ 	} while (0)
+ 
+ #define gen_wm(sdat,sreg,sbase)	\
+     do { outw(((sreg)<< 8)|(sdat),(sbase);} while (0)
+ 
+ #define gen_wi(sdat,sreg,sbase)	\
+     do { outw((sreg)|((sdat)<<8),(sbase));} while (0)
+ 
+ /*
+  * Macro to access the timing-sequencer.
+  */
+ #define seq_r(sreg)	\
+ 	gen_r((sreg),SEQ_IDX)
+ 
+ #define seq_wb(sdat,sreg) \
+ 	gen_wb((sdat),(sreg),SEQ_IDX)
+ 
+ #define seq_wm(sdat,sreg) \
+ 	gen_wm((sdat),(sreg),SEQ_IDX)
+ 
+ #define seq_wi(sdat,sreg) \
+ 	gen_wi((sdat),(sreg),SEQ_IDX)
+ 
+ /*
+  * Macro to access the CRT controller.
+  */
+ #define crt_r(creg)	\
+ 	gen_r((creg),CRT_IDX)
+ 
+ #define crt_wb(cdat,creg) \
+ 	gen_wb((cdat),(creg),CRT_IDX)
+ 
+ #define crt_wm(cdat,creg) \
+ 	gen_wm((cdat),(creg),CRT_IDX)
+ 
+ #define crt_wi(cdat,creg) \
+ 	gen_wi((cdat),(creg),CRT_IDX)
+ 
+ /*
+  * Macro to access the graphics controller.
+  */
+ #define gfx_r(greg)	\
+ 	gen_r((greg),GFX_IDX)
+ 
+ #define gfx_wb(gdat,greg) \
+ 	gen_wb((gdat),(greg),GFX_IDX)
+ 
+ #define gfx_wm(gdat,greg) \
+ 	gen_wm((gdat),(greg),GFX_IDX)
+ 
+ #define gfx_wi(gdat,greg) \
+ 	gen_wi((gdat),(greg),GFX_IDX)
+ 
+ /*
+  * Macro to access the attribute controller.
+  * the attribute controller has special handling!!!
+  */
+ #define attr_r(areg)	\
+     ({outb((areg), ACT_IDX);	\
+ 	  inb(ACT_IDX+1);})
+ 
+ #define attr_wb(adat,areg) \
+ 	do {	outb((areg),(ACT_IDX));	\
+ 			outb((adat),(ACT_DATA));\
+ 	} while (0)
+ 	
+ /*
+  * General Registers
+  */
+ #define GREG_STATUS0_R		0x03c2
+ #define GREG_STATUS1_R		0x03da
+ #define GREG_MISC_OUTPUT_R	0x03cc
+ #define GREG_MISC_OUTPUT_W	0x03c2	
+ #define GREG_FEATURE_CONTROL_R	0x03ca
+ #define GREG_FEATURE_CONTROL_W	0x03da
+ #define GREG_POS		0x0102
+ 
+ /*
+  * Video DAC addresses
+  */
+ #define VDAC_ADDRESS		0x03c8
+ #define VDAC_ADDRESS_W		0x03c8
+ #define VDAC_ADDRESS_R		0x03c7
+ #define VDAC_STATE		0x03c7
+ #define VDAC_DATA		0x03c9
+ #define VDAC_MASK		0x03c6
+ 
+ /*
+  * Timing Sequencer
+  */
+ #define SEQ_IDX			0x03c4	/* Sequencer Index */
+ #define SEQ_DATA		0x03c5
+ #define SEQ_RESET		0x00
+ #define SEQ_CLOCKING_MODE	0x01
+ #define SEQ_MAP_MASK		0x02
+ #define SEQ_CHAR_MAP_SELECT	0x03
+ #define SEQ_MEMORY_MODE		0x04
+ 
+ /*
+  * Graphics Controller
+  */
+ #define GFX_IDX			0x03ce
+ #define GFX_DATA		0x03cf
+ #define GFX_SET_RESET		0x00
+ #define GFX_ENABLE_SET_RESET	0x01
+ #define GFX_COLOR_COMPARE	0x02
+ #define GFX_DATA_ROTATE		0x03
+ #define GFX_READ_MAP_SELECT	0x04
+ #define GFX_GRAPHICS_MODE	0x05
+ #define GFX_MISC		0x06
+ #define GFX_COLOR_XCARE		0x07
+ #define GFX_BITMASK		0x08
+ 
+ /*
+  * CRT Controller
+  */
+ #define CRT_IDX			0x03d4
+ #define CRT_DATA		0x03d5
+ #define CRT_HOR_TOTAL		0x00
+ #define CRT_HOR_DISP_ENA_END	0x01
+ #define CRT_START_HOR_BLANK	0x02
+ #define CRT_END_HOR_BLANK	0x03
+ #define CRT_START_HOR_RETR	0x04
+ #define CRT_END_HOR_RETR	0x05
+ #define CRT_VER_TOTAL		0x06
+ #define CRT_OVERFLOW		0x07
+ #define CRT_PRESET_ROW_SCAN	0x08
+ #define CRT_MAX_SCAN_LINE	0x09
+ #define CRT_CURSOR_START	0x0a
+ #define CRT_CURSOR_END		0x0b
+ #define CRT_START_ADDR_HIGH	0x0c
+ #define CRT_START_ADDR_LOW	0x0d
+ #define CRT_CURSOR_LOC_HIGH	0x0e
+ #define CRT_CURSOR_LOC_LOW	0x0f
+ #define CRT_START_VER_RETR	0x10
+ #define CRT_END_VER_RETR	0x11
+ #define CRT_VER_DISP_ENA_END	0x12
+ #define CRT_OFFSET		0x13
+ #define CRT_UNDERLINE_LOC	0x14
+ #define CRT_START_VER_BLANK	0x15
+ #define CRT_END_VER_BLANK	0x16
+ #define CRT_MODE_CONTROL	0x17
+ #define CRT_LINE_COMPARE	0x18
+ 
+ /*
+  * Attribute Controller
+  */
+ #define ACT_IDX			0x03C0
+ #define ACT_ADDRESS_R		0x03C0
+ #define ACT_DATA		0x03C0
+ #define ACT_PALETTE0		0x00
+ #define ACT_PALETTE1		0x01
+ #define ACT_PALETTE2		0x02
+ #define ACT_PALETTE3		0x03
+ #define ACT_PALETTE4		0x04
+ #define ACT_PALETTE5		0x05
+ #define ACT_PALETTE6		0x06
+ #define ACT_PALETTE7		0x07
+ #define ACT_PALETTE8		0x08
+ #define ACT_PALETTE9		0x09
+ #define ACT_PALETTE10		0x0A
+ #define ACT_PALETTE11		0x0B
+ #define ACT_PALETTE12		0x0C
+ #define ACT_PALETTE13		0x0D
+ #define ACT_PALETTE14		0x0E
+ #define ACT_PALETTE15		0x0F
+ #define ACT_ATTR_MODE_CNTL	0x10
+ #define ACT_OVERSCAN_COLOR	0x11
+ #define ACT_COLOR_PLANE_ENA	0x12
+ #define ACT_HOR_PEL_PANNING	0x13
+ #define ACT_COLOR_SELECT	0x14
*** /dev/null	Sun Jun 16 21:20:44 1996
--- linux/drivers/video/videohw.h	Sat Feb 28 00:05:53 1998
***************
*** 0 ****
--- 1,94 ----
+ /* tab-width: 4 */
+ 
+ /*
+  * linux/drivers/video/videohw.h		$Revision: 1.1 $
+  *
+  *    Copyright (C) 1997 J. Orschiedt
+  *
+  * Definitions for Hardware detections
+  *
+  * changes:
+  *   definitions of __vga_region, __vga_fingerprint moved
+  *   to asm/atari_isahw.h
+  */
+ 
+ /* architectures */
+ enum {
+ 	CRAZYDOTS = 1,
+ 	MV300,
+ 	NOVA,
+ 	NOVAPLUS,
+ 	PANTHER64,
+ 	PC_ISA,
+ 	SPECTRUM,
+ 	PROMST,
+ 	__NUM_ARCHs};
+ 
+ /* graphic chips */
+ enum {
+ 	STDVGA = 1,
+ 	G300,
+ 	ET4000,
+ 	ET4000W32,
+ 	ET4000W32I,
+ 	ET4000W32P,
+ 	ET6000,
+ 	MACH32,
+ 	MACH64,
+ 	TI34061,
+ 	__NUM_CHIPs};
+ 
+ /* RAMDACs */
+ 
+ enum {
+ 	DAC_BLIND6 = 1,
+ 	DAC_STANDARD,
+ 	DAC_SS24, DAC_SIERRA15, DAC_SIERRA15_16, DAC_SIERRA24,
+ 	DAC_MUSIC4870, DAC_MUSIC4910,
+ 	DAC_ACUMOS,
+ 	DAC_CIRRUS24,
+ 	DAC_ATT490, DAC_ATT491, DAC_ATT492, DAC_ATT493,
+ 	DAC_EDSUN,
+ 	BLINDW32,
+ 	DAC_ET6000,
+ 	DAC_ICS5341, DAC_ICS5301,
+ 	DAC_ATI_INTERNAL,
+ 	DAC_ATI68830, DAC_ATI68860, DAC_ATI68875, DAC_ATIMISC24,
+ 	DAC_ATT498, DAC_ATT408,
+ 	DAC_CH8398,
+ 	DAC_IBMRGB525,
+ 	DAC_IMSG174,
+ 	DAC_MU9C1880,
+ 	DAC_STG1700, DAC_STG1702, DAC_STG1703,
+ 	DAC_TVP3025,
+ 	DAC_S3,
+ 	DAC_UNKNOWN,
+ 	__NUM_DACs};
+ 
+ typedef struct {
+ 	__isa_descriptor isa;		/* address ranges */
+ 	__u8	Card;		/* card descriptor */
+ 	__u8	Chip;		/* chiptype */
+ 	__u8	RamDac;		/* RAMDAC */
+ 	__u8	Flags;		/* ATARI_VME || MEDUSA_ISA || HADES_ISA ... */
+ 	int		ChipId;		/* detected chip revision */
+ 	char *	CardName;
+ } __vga_descriptor;
+ 
+ #define	DAC_6_8_PROGRAM	0x40
+ #define	DAC_8BIT		0x80
+ 
+ /* flags for machine-dependencies */
+ #define	ATARI_VME	0x01
+ #define MEDUSA_ISA	0x02
+ #define HADES_ISA	0x04
+ #define HADES_PCI	0x08
+ #define VAPOUR_WARE	0x80
+ 
+ /*
+  * Local Variables:
+  * tab-width: 4
+  * End:
+  * vi: ts=4 sw=4
+  * vim: si
+  */
*** l68k/linux-2.1.85/arch/m68k/atari/time.c	Tue Jan  6 15:50:57 1998
--- linux/arch/m68k/atari/time.c	Sun Mar  1 12:16:42 1998
***************
*** 84,97 ****
  
  #define	RTC_READ(reg)				\
      ({	unsigned char	__val;			\
! 		outb(reg,&tt_rtc.regsel);	\
  		__val = tt_rtc.data;		\
  		__val;				\
  	})
  
  #define	RTC_WRITE(reg,val)			\
      do {					\
! 		outb(reg,&tt_rtc.regsel);	\
  		tt_rtc.data = (val);		\
  	} while(0)
  
--- 84,97 ----
  
  #define	RTC_READ(reg)				\
      ({	unsigned char	__val;			\
! 		writeb(reg,&tt_rtc.regsel);	\
  		__val = tt_rtc.data;		\
  		__val;				\
  	})
  
  #define	RTC_WRITE(reg,val)			\
      do {					\
! 		writeb(reg,&tt_rtc.regsel);	\
  		tt_rtc.data = (val);		\
  	} while(0)
  
*** l68k/linux-2.1.85/drivers/video/Makefile	Tue Jan 13 17:37:53 1998
--- linux/drivers/video/Makefile	Tue Feb 10 22:57:37 1998
***************
*** 96,101 ****
--- 96,109 ----
  L_OBJS += tgafb.o
  endif
  
+ ifeq ($(CONFIG_FB_TSENG),y)
+ L_OBJS += tsengfb.o
+ else
+   ifeq ($(CONFIG_FB_TSENG),m)
+   M_OBJS += tsengfb.o
+   endif
+ endif
+ 
  ifeq ($(CONFIG_FB_VIRGE),y)
  L_OBJS += virgefb.o
  else


