patch-2.4.20 linux-2.4.20/drivers/video/sti/stifb.c

Next file: linux-2.4.20/drivers/video/sti-bmode.h
Previous file: linux-2.4.20/drivers/video/sti/sticore.h
Back to the patch index
Back to the overall index

diff -urN linux-2.4.19/drivers/video/sti/stifb.c linux-2.4.20/drivers/video/sti/stifb.c
@@ -0,0 +1,1507 @@
+/*
+ * linux/drivers/video/sti/stifb.c - 
+ * Frame buffer driver for HP workstations with STI (standard text interface) 
+ * video firmware.
+ *
+ * Copyright (C) 2001 Helge Deller <deller@gmx.de>
+ * Portions Copyright (C) 2001 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+ * 
+ * Based on:
+ * - linux/drivers/video/artistfb.c -- Artist frame buffer driver
+ *	Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ *   - based on skeletonfb, which was
+ *	Created 28 Dec 1997 by Geert Uytterhoeven
+ * - HP Xhp cfb-based X11 window driver for XFree86
+ *	(c)Copyright 1992 Hewlett-Packard Co.
+ *
+ * 
+ *  The following graphics display devices (NGLE family) are supported by this driver:
+ *
+ *  HPA4070A	known as "HCRX", a 1280x1024 color device with 8 planes
+ *  HPA4071A	known as "HCRX24", a 1280x1024 color device with 24 planes,
+ *		optionally available with a hardware accelerator as HPA4071A_Z
+ *  HPA1659A	known as "CRX", a 1280x1024 color device with 8 planes
+ *  HPA1439A	known as "CRX24", a 1280x1024 color device with 24 planes,
+ *		optionally available with a hardware accelerator.
+ *  HPA1924A	known as "GRX", a 1280x1024 grayscale device with 8 planes
+ *  HPA2269A	known as "Dual CRX", a 1280x1024 color device with 8 planes,
+ *		implements support for two displays on a single graphics card.
+ *  HP710C	internal graphics support optionally available on the HP9000s710 SPU,
+ *		supports 1280x1024 color displays with 8 planes.
+ *  HP710G	same as HP710C, 1280x1024 grayscale only
+ *  HP710L	same as HP710C, 1024x768 color only
+ *  HP712	internal graphics support on HP9000s712 SPU, supports 640x480, 
+ *		1024x768 or 1280x1024 color displays on 8 planes (Artist)
+ *
+ * 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.
+ */
+
+/* TODO:
+ *	- Artist gfx is the only supported chip atm,
+ *	- remove the static fb_info to support multiple cards
+ *	- remove the completely untested 1bpp mode
+ *	- add support for h/w acceleration
+ *	- add hardware cursor
+ *	-
+ */
+
+
+/* on supported graphic devices you may:
+ * #define FALLBACK_TO_1BPP to fall back to 1 bpp, or
+ * #undef  FALLBACK_TO_1BPP to reject support for unsupported cards */
+#undef FALLBACK_TO_1BPP
+
+
+#include <linux/config.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/init.h>
+#include <linux/selection.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb32.h>
+
+#include <asm/grfioctl.h>	/* for HP-UX compatibility */
+
+#include "sticore.h"
+
+extern struct display_switch fbcon_sti; /* fbcon-sti.c */
+
+#ifdef __LP64__
+/* return virtual address */
+#define REGION_BASE(fb_info, index) \
+	(fb_info->sti->glob_cfg->region_ptrs[index] | 0xffffffff00000000)
+#else
+/* return virtual address */
+#define REGION_BASE(fb_info, index) \
+	fb_info->sti->glob_cfg->region_ptrs[index]
+#endif
+
+#define NGLEDEVDEPROM_CRT_REGION 1
+
+typedef struct {
+	__s32	video_config_reg;
+	__s32	misc_video_start;
+	__s32	horiz_timing_fmt;
+	__s32	serr_timing_fmt;
+	__s32	vert_timing_fmt;
+	__s32	horiz_state;
+	__s32	vert_state;
+	__s32	vtg_state_elements;
+	__s32	pipeline_delay;
+	__s32	misc_video_end;
+} video_setup_t;
+
+typedef struct {                  
+	__s16	sizeof_ngle_data;
+	__s16	x_size_visible;	    /* visible screen dim in pixels  */
+	__s16	y_size_visible;
+	__s16	pad2[15];
+	__s16	cursor_pipeline_delay;
+	__s16	video_interleaves;
+	__s32	pad3[11];
+} ngle_rom_t;
+
+struct stifb_info {
+	struct fb_info info;
+        struct fb_var_screeninfo var;
+        struct fb_fix_screeninfo fix;
+	struct display disp;
+	struct sti_struct *sti;
+	unsigned int id, real_id;
+	int currcon;
+	int cmap_reload:1;   
+	int deviceSpecificConfig;
+	ngle_rom_t ngle_rom;
+	struct { u8 red, green, blue; } palette[256];
+#ifdef FBCON_HAS_CFB32	
+	union {
+		u32 cfb32[16];
+	} fbcon_cmap;
+#endif			
+};
+
+static int stifb_force_bpp[MAX_STI_ROMS] = {0, };
+
+/* ------------------- chipset specific functions -------------------------- */
+
+/* offsets to graphic-chip internal registers */
+
+#define REG_1		0x000118
+#define REG_2		0x000480
+#define REG_3		0x0004a0
+#define REG_4		0x000600
+#define REG_6		0x000800
+#define REG_8		0x000820
+#define REG_9		0x000a04
+#define REG_10		0x018000
+#define REG_11		0x018004
+#define REG_12		0x01800c
+#define REG_13		0x018018
+#define REG_14  	0x01801c
+#define REG_15		0x200000
+#define REG_15b0	0x200000
+#define REG_16b1	0x200005
+#define REG_16b3	0x200007
+#define REG_21		0x200218
+#define REG_22		0x0005a0
+#define REG_23		0x0005c0
+#define REG_26		0x200118
+#define REG_27		0x200308
+#define REG_32		0x21003c
+#define REG_33		0x210040
+#define REG_34		0x200008
+#define REG_35		0x018010
+#define REG_38		0x210020
+#define REG_39		0x210120
+#define REG_40		0x210130
+#define REG_42		0x210028
+#define REG_43		0x21002c
+#define REG_44		0x210030
+#define REG_45		0x210034
+
+#define READ_BYTE(fb,reg)		__raw_readb((fb)->fix.mmio_start + (reg))
+#define READ_WORD(fb,reg)		__raw_readl((fb)->fix.mmio_start + (reg))
+#define WRITE_BYTE(value,fb,reg)	__raw_writeb((value),(fb)->fix.mmio_start + (reg))
+#define WRITE_WORD(value,fb,reg)	__raw_writel((value),(fb)->fix.mmio_start + (reg))
+
+#define ENABLE	1	/* for enabling/disabling screen */	
+#define DISABLE 0
+
+#define NGLE_LOCK(fb_info)	do { } while (0) 
+#define NGLE_UNLOCK(fb_info)	do { } while (0)
+
+static void
+SETUP_HW(struct stifb_info *fb)
+{
+	char stat;
+
+	do {
+		stat = READ_BYTE(fb, REG_15b0);
+		if (!stat)
+	    		stat = READ_BYTE(fb, REG_15b0);
+	} while (stat);
+}
+
+
+static void
+SETUP_FB(struct stifb_info *fb)
+{	
+	unsigned int reg10_value = 0;
+	
+	SETUP_HW(fb);
+	switch (fb->id)
+	{
+		case CRT_ID_VISUALIZE_EG:
+		case S9000_ID_ARTIST:
+		case S9000_ID_A1659A:
+			reg10_value = 0x13601000;
+			break;
+		case S9000_ID_A1439A:
+			if (fb->var.bits_per_pixel == 32)						
+				reg10_value = 0xBBA0A000;
+			else 
+				reg10_value = 0x13601000;
+			break;
+		case S9000_ID_HCRX:
+			if (fb->var.bits_per_pixel == 32)
+				reg10_value = 0xBBA0A000;
+			else					
+				reg10_value = 0x13602000;
+			break;
+		case S9000_ID_TIMBER:
+		case CRX24_OVERLAY_PLANES:
+			reg10_value = 0x13602000;
+			break;
+	}
+	if (reg10_value)
+		WRITE_WORD(reg10_value, fb, REG_10);
+	WRITE_WORD(0x83000300, fb, REG_14);
+	SETUP_HW(fb);
+	WRITE_BYTE(1, fb, REG_16b1);
+}
+
+static void
+START_IMAGE_COLORMAP_ACCESS(struct stifb_info *fb)
+{
+	SETUP_HW(fb);
+	WRITE_WORD(0xBBE0F000, fb, REG_10);
+	WRITE_WORD(0x03000300, fb, REG_14);
+	WRITE_WORD(~0, fb, REG_13);
+}
+
+static void
+WRITE_IMAGE_COLOR(struct stifb_info *fb, int index, int color) 
+{
+	SETUP_HW(fb);
+	WRITE_WORD(((0x100+index)<<2), fb, REG_3);
+	WRITE_WORD(color, fb, REG_4);
+}
+
+static void
+FINISH_IMAGE_COLORMAP_ACCESS(struct stifb_info *fb) 
+{		
+	WRITE_WORD(0x400, fb, REG_2);
+	if (fb->var.bits_per_pixel == 32) {
+		WRITE_WORD(0x83000100, fb, REG_1);
+	} else {
+		if (fb->id == S9000_ID_ARTIST || fb->id == CRT_ID_VISUALIZE_EG)
+			WRITE_WORD(0x80000100, fb, REG_26);
+		else							
+			WRITE_WORD(0x80000100, fb, REG_1);
+	}
+	SETUP_FB(fb);
+}
+
+static void
+SETUP_RAMDAC(struct stifb_info *fb) 
+{
+	SETUP_HW(fb);
+	WRITE_WORD(0x04000000, fb, 0x1020);
+	WRITE_WORD(0xff000000, fb, 0x1028);
+}
+
+static void 
+CRX24_SETUP_RAMDAC(struct stifb_info *fb) 
+{
+	SETUP_HW(fb);
+	WRITE_WORD(0x04000000, fb, 0x1000);
+	WRITE_WORD(0x02000000, fb, 0x1004);
+	WRITE_WORD(0xff000000, fb, 0x1008);
+	WRITE_WORD(0x05000000, fb, 0x1000);
+	WRITE_WORD(0x02000000, fb, 0x1004);
+	WRITE_WORD(0x03000000, fb, 0x1008);
+}
+
+#if 0
+static void 
+HCRX_SETUP_RAMDAC(struct stifb_info *fb)
+{
+	WRITE_WORD(0xffffffff, fb, REG_32);
+}
+#endif
+
+static void 
+CRX24_SET_OVLY_MASK(struct stifb_info *fb)
+{
+	SETUP_HW(fb);
+	WRITE_WORD(0x13a02000, fb, REG_11);
+	WRITE_WORD(0x03000300, fb, REG_14);
+	WRITE_WORD(0x000017f0, fb, REG_3);
+	WRITE_WORD(0xffffffff, fb, REG_13);
+	WRITE_WORD(0xffffffff, fb, REG_22);
+	WRITE_WORD(0x00000000, fb, REG_23);
+}
+
+static void
+ENABLE_DISABLE_DISPLAY(struct stifb_info *fb, int enable)
+{
+	unsigned int value = enable ? 0x43000000 : 0x03000000;
+        SETUP_HW(fb);
+        WRITE_WORD(0x06000000,	fb, 0x1030);
+        WRITE_WORD(value, 	fb, 0x1038);
+}
+
+static void 
+CRX24_ENABLE_DISABLE_DISPLAY(struct stifb_info *fb, int enable)
+{
+	unsigned int value = enable ? 0x10000000 : 0x30000000;
+	SETUP_HW(fb);
+	WRITE_WORD(0x01000000,	fb, 0x1000);
+	WRITE_WORD(0x02000000,	fb, 0x1004);
+	WRITE_WORD(value,	fb, 0x1008);
+}
+
+static void
+ARTIST_ENABLE_DISABLE_DISPLAY(struct stifb_info *fb, int enable) 
+{
+	u32 DregsMiscVideo = REG_21;
+	u32 DregsMiscCtl = REG_27;
+	
+	SETUP_HW(fb);
+	if (enable) {
+	  WRITE_WORD(READ_WORD(fb, DregsMiscVideo) | 0x0A000000, fb, DregsMiscVideo);
+	  WRITE_WORD(READ_WORD(fb, DregsMiscCtl)   | 0x00800000, fb, DregsMiscCtl);
+	} else {
+	  WRITE_WORD(READ_WORD(fb, DregsMiscVideo) & ~0x0A000000, fb, DregsMiscVideo);
+	  WRITE_WORD(READ_WORD(fb, DregsMiscCtl)   & ~0x00800000, fb, DregsMiscCtl);
+	}
+}
+
+#define GET_ROMTABLE_INDEX(fb) \
+	(READ_BYTE(fb, REG_16b3) - 1)
+
+#define HYPER_CONFIG_PLANES_24 0x00000100
+	
+#define IS_24_DEVICE(fb) \
+	(fb->deviceSpecificConfig & HYPER_CONFIG_PLANES_24)
+
+#define IS_888_DEVICE(fb) \
+	(!(IS_24_DEVICE(fb)))
+
+#define GET_FIFO_SLOTS(fb, cnt, numslots)			\
+{	while (cnt < numslots) 					\
+		cnt = READ_WORD(fb, REG_34);	\
+	cnt -= numslots;					\
+}
+
+#define	    IndexedDcd	0	/* Pixel data is indexed (pseudo) color */
+#define	    Otc04	2	/* Pixels in each longword transfer (4) */
+#define	    Otc32	5	/* Pixels in each longword transfer (32) */
+#define	    Ots08	3	/* Each pixel is size (8)d transfer (1) */
+#define	    OtsIndirect	6	/* Each bit goes through FG/BG color(8) */
+#define	    AddrLong	5	/* FB address is Long aligned (pixel) */
+#define	    BINovly	0x2	/* 8 bit overlay */
+#define	    BINapp0I	0x0	/* Application Buffer 0, Indexed */
+#define	    BINapp1I	0x1	/* Application Buffer 1, Indexed */
+#define	    BINapp0F8	0xa	/* Application Buffer 0, Fractional 8-8-8 */
+#define	    BINattr	0xd	/* Attribute Bitmap */
+#define	    RopSrc 	0x3
+#define	    BitmapExtent08  3	/* Each write hits ( 8) bits in depth */
+#define	    BitmapExtent32  5	/* Each write hits (32) bits in depth */
+#define	    DataDynamic	    0	/* Data register reloaded by direct access */
+#define	    MaskDynamic	    1	/* Mask register reloaded by direct access */
+#define	    MaskOtc	    0	/* Mask contains Object Count valid bits */
+
+#define MaskAddrOffset(offset) (offset)
+#define StaticReg(en) (en)
+#define BGx(en) (en)
+#define FGx(en) (en)
+
+#define BAJustPoint(offset) (offset)
+#define BAIndexBase(base) (base)
+#define BA(F,C,S,A,J,B,I) \
+	(((F)<<31)|((C)<<27)|((S)<<24)|((A)<<21)|((J)<<16)|((B)<<12)|(I))
+
+#define IBOvals(R,M,X,S,D,L,B,F) \
+	(((R)<<8)|((M)<<16)|((X)<<24)|((S)<<29)|((D)<<28)|((L)<<31)|((B)<<1)|(F))
+
+#define NGLE_QUICK_SET_IMAGE_BITMAP_OP(fb, val) \
+	WRITE_WORD(val, fb, REG_14)
+
+#define NGLE_QUICK_SET_DST_BM_ACCESS(fb, val) \
+	WRITE_WORD(val, fb, REG_11)
+
+#define NGLE_QUICK_SET_CTL_PLN_REG(fb, val) \
+	WRITE_WORD(val, fb, REG_12)
+
+#define NGLE_REALLY_SET_IMAGE_PLANEMASK(fb, plnmsk32) \
+	WRITE_WORD(plnmsk32, fb, REG_13)
+
+#define NGLE_REALLY_SET_IMAGE_FG_COLOR(fb, fg32) \
+	WRITE_WORD(fg32, fb, REG_35)
+
+#define NGLE_SET_TRANSFERDATA(fb, val) \
+	WRITE_WORD(val, fb, REG_8)
+
+#define NGLE_SET_DSTXY(fb, val) \
+	WRITE_WORD(val, fb, REG_6)
+
+#define NGLE_LONG_FB_ADDRESS(fbaddrbase, x, y) (		\
+	(u32) (fbaddrbase) +				\
+	    (	(unsigned int)  ( (y) << 13      ) |		\
+		(unsigned int)  ( (x) << 2       )	)	\
+	)
+
+#define NGLE_BINC_SET_DSTADDR(fb, addr) \
+	WRITE_WORD(addr, fb, REG_3)
+
+#define NGLE_BINC_SET_SRCADDR(fb, addr) \
+	WRITE_WORD(addr, fb, REG_2)
+
+#define NGLE_BINC_SET_DSTMASK(fb, mask) \
+	WRITE_WORD(mask, fb, REG_22)
+
+#define NGLE_BINC_WRITE32(fb, data32) \
+	WRITE_WORD(data32, fb, REG_23)
+
+#define START_COLORMAPLOAD(fb, cmapBltCtlData32) \
+	WRITE_WORD((cmapBltCtlData32), fb, REG_38)
+
+#define SET_LENXY_START_RECFILL(fb, lenxy) \
+	WRITE_WORD(lenxy, fb, REG_9)
+
+static void
+HYPER_ENABLE_DISABLE_DISPLAY(struct stifb_info *fb, int enable)
+{
+	u32 DregsHypMiscVideo = REG_33;
+	unsigned int value;
+	SETUP_HW(fb);
+	value = READ_WORD(fb, DregsHypMiscVideo);
+	if (enable)
+		value |= 0x0A000000;
+	else
+		value &= ~0x0A000000;
+	WRITE_WORD(value, fb, DregsHypMiscVideo);
+}
+
+
+/* BufferNumbers used by SETUP_ATTR_ACCESS() */
+#define BUFF0_CMAP0	0x00001e02
+#define BUFF1_CMAP0	0x02001e02
+#define BUFF1_CMAP3	0x0c001e02
+#define ARTIST_CMAP0	0x00000102
+#define HYPER_CMAP8	0x00000100
+#define HYPER_CMAP24	0x00000800
+
+static void
+SETUP_ATTR_ACCESS(struct stifb_info *fb, unsigned BufferNumber)
+{
+	SETUP_HW(fb);
+	WRITE_WORD(0x2EA0D000, fb, REG_11);
+	WRITE_WORD(0x23000302, fb, REG_14);
+	WRITE_WORD(BufferNumber, fb, REG_12);
+	WRITE_WORD(0xffffffff, fb, REG_8);
+}
+
+static void
+SET_ATTR_SIZE(struct stifb_info *fb, int width, int height) 
+{
+	WRITE_WORD(0x00000000, fb, REG_6);
+	WRITE_WORD((width<<16) | height, fb, REG_9);
+	WRITE_WORD(0x05000000, fb, REG_6);
+	WRITE_WORD(0x00040001, fb, REG_9);
+}
+
+static void
+FINISH_ATTR_ACCESS(struct stifb_info *fb) 
+{
+	SETUP_HW(fb);
+	WRITE_WORD(0x00000000, fb, REG_12);
+}
+
+static void
+elkSetupPlanes(struct stifb_info *fb)
+{
+	SETUP_RAMDAC(fb);
+	SETUP_FB(fb);
+}
+
+static void 
+ngleSetupAttrPlanes(struct stifb_info *fb, int BufferNumber)
+{
+	SETUP_ATTR_ACCESS(fb, BufferNumber);
+	SET_ATTR_SIZE(fb, fb->var.xres, fb->var.yres);
+	FINISH_ATTR_ACCESS(fb);
+	SETUP_FB(fb);
+}
+
+
+static void
+rattlerSetupPlanes(struct stifb_info *fb)
+{
+	CRX24_SETUP_RAMDAC(fb);
+    
+	/* replacement for: SETUP_FB(fb, CRX24_OVERLAY_PLANES); */
+	WRITE_WORD(0x83000300, fb, REG_14);
+	SETUP_HW(fb);
+	WRITE_BYTE(1, fb, REG_16b1);
+
+	/* XXX: replace by fb_setmem(), smem_start or screen_base ? */
+	memset_io(fb->fix.smem_start, 0xff,
+		fb->var.yres*fb->fix.line_length);
+    
+	CRX24_SET_OVLY_MASK(fb);
+	SETUP_FB(fb);
+}
+
+
+#define HYPER_CMAP_TYPE				0
+#define NGLE_CMAP_INDEXED0_TYPE			0
+#define NGLE_CMAP_OVERLAY_TYPE			3
+
+/* typedef of LUT (Colormap) BLT Control Register */
+typedef union	/* Note assumption that fields are packed left-to-right */
+{	u32 all;
+	struct
+	{
+		unsigned enable              :  1;
+		unsigned waitBlank           :  1;
+		unsigned reserved1           :  4;
+		unsigned lutOffset           : 10;   /* Within destination LUT */
+		unsigned lutType             :  2;   /* Cursor, image, overlay */
+		unsigned reserved2           :  4;
+		unsigned length              : 10;
+	} fields;
+} NgleLutBltCtl;
+
+
+#if 0
+static NgleLutBltCtl
+setNgleLutBltCtl(struct stifb_info *fb, int offsetWithinLut, int length)
+{
+	NgleLutBltCtl lutBltCtl;
+
+	/* set enable, zero reserved fields */
+	lutBltCtl.all           = 0x80000000;
+	lutBltCtl.fields.length = length;
+
+	switch (fb->id) 
+	{
+	case S9000_ID_A1439A:		/* CRX24 */
+		if (fb->var.bits_per_pixel == 8) {
+			lutBltCtl.fields.lutType = NGLE_CMAP_OVERLAY_TYPE;
+			lutBltCtl.fields.lutOffset = 0;
+		} else {
+			lutBltCtl.fields.lutType = NGLE_CMAP_INDEXED0_TYPE;
+			lutBltCtl.fields.lutOffset = 0 * 256;
+		}
+		break;
+		
+	case S9000_ID_ARTIST:
+		lutBltCtl.fields.lutType = NGLE_CMAP_INDEXED0_TYPE;
+		lutBltCtl.fields.lutOffset = 0 * 256;
+		break;
+		
+	default:
+		lutBltCtl.fields.lutType = NGLE_CMAP_INDEXED0_TYPE;
+		lutBltCtl.fields.lutOffset = 0;
+		break;
+	}
+
+	/* Offset points to start of LUT.  Adjust for within LUT */
+	lutBltCtl.fields.lutOffset += offsetWithinLut;
+
+	return lutBltCtl;
+}
+#endif
+
+static NgleLutBltCtl
+setHyperLutBltCtl(struct stifb_info *fb, int offsetWithinLut, int length) 
+{
+	NgleLutBltCtl lutBltCtl;
+
+	/* set enable, zero reserved fields */
+	lutBltCtl.all = 0x80000000;
+
+	lutBltCtl.fields.length = length;
+	lutBltCtl.fields.lutType = HYPER_CMAP_TYPE;
+
+	/* Expect lutIndex to be 0 or 1 for image cmaps, 2 or 3 for overlay cmaps */
+	if (fb->var.bits_per_pixel == 8)
+		lutBltCtl.fields.lutOffset = 2 * 256;
+	else
+		lutBltCtl.fields.lutOffset = 0 * 256;
+
+	/* Offset points to start of LUT.  Adjust for within LUT */
+	lutBltCtl.fields.lutOffset += offsetWithinLut;
+
+	return lutBltCtl;
+}
+
+
+static void hyperUndoITE(struct stifb_info *fb)
+{
+	int nFreeFifoSlots = 0;
+	u32 fbAddr;
+
+	NGLE_LOCK(fb);
+
+	GET_FIFO_SLOTS(fb, nFreeFifoSlots, 1);
+	WRITE_WORD(0xffffffff, fb, REG_32);
+
+	/* Write overlay transparency mask so only entry 255 is transparent */
+
+	/* Hardware setup for full-depth write to "magic" location */
+	GET_FIFO_SLOTS(fb, nFreeFifoSlots, 7);
+	NGLE_QUICK_SET_DST_BM_ACCESS(fb, 
+		BA(IndexedDcd, Otc04, Ots08, AddrLong,
+		BAJustPoint(0), BINovly, BAIndexBase(0)));
+	NGLE_QUICK_SET_IMAGE_BITMAP_OP(fb,
+		IBOvals(RopSrc, MaskAddrOffset(0),
+		BitmapExtent08, StaticReg(0),
+		DataDynamic, MaskOtc, BGx(0), FGx(0)));
+
+	/* Now prepare to write to the "magic" location */
+	fbAddr = NGLE_LONG_FB_ADDRESS(0, 1532, 0);
+	NGLE_BINC_SET_DSTADDR(fb, fbAddr);
+	NGLE_REALLY_SET_IMAGE_PLANEMASK(fb, 0xffffff);
+	NGLE_BINC_SET_DSTMASK(fb, 0xffffffff);
+
+	/* Finally, write a zero to clear the mask */
+	NGLE_BINC_WRITE32(fb, 0);
+
+	NGLE_UNLOCK(fb);
+}
+
+static void 
+ngleDepth8_ClearImagePlanes(struct stifb_info *fb)
+{
+	/* FIXME! */
+}
+
+static void 
+ngleDepth24_ClearImagePlanes(struct stifb_info *fb)
+{
+	/* FIXME! */
+}
+
+static void
+ngleResetAttrPlanes(struct stifb_info *fb, unsigned int ctlPlaneReg)
+{
+	int nFreeFifoSlots = 0;
+	u32 packed_dst;
+	u32 packed_len;
+
+	NGLE_LOCK(fb);
+
+	GET_FIFO_SLOTS(fb, nFreeFifoSlots, 4);
+	NGLE_QUICK_SET_DST_BM_ACCESS(fb, 
+				     BA(IndexedDcd, Otc32, OtsIndirect,
+					AddrLong, BAJustPoint(0),
+					BINattr, BAIndexBase(0)));
+	NGLE_QUICK_SET_CTL_PLN_REG(fb, ctlPlaneReg);
+	NGLE_SET_TRANSFERDATA(fb, 0xffffffff);
+
+	NGLE_QUICK_SET_IMAGE_BITMAP_OP(fb,
+				       IBOvals(RopSrc, MaskAddrOffset(0),
+					       BitmapExtent08, StaticReg(1),
+					       DataDynamic, MaskOtc,
+					       BGx(0), FGx(0)));
+	packed_dst = 0;
+	packed_len = (fb->var.xres << 16) | fb->var.yres;
+	GET_FIFO_SLOTS(fb, nFreeFifoSlots, 2);
+	NGLE_SET_DSTXY(fb, packed_dst);
+	SET_LENXY_START_RECFILL(fb, packed_len);
+
+	/*
+	 * In order to work around an ELK hardware problem (Buffy doesn't
+	 * always flush it's buffers when writing to the attribute
+	 * planes), at least 4 pixels must be written to the attribute
+	 * planes starting at (X == 1280) and (Y != to the last Y written
+	 * by BIF):
+	 */
+
+	if (fb->id == S9000_ID_A1659A) {   /* ELK_DEVICE_ID */
+		/* It's safe to use scanline zero: */
+		packed_dst = (1280 << 16);
+		GET_FIFO_SLOTS(fb, nFreeFifoSlots, 2);
+		NGLE_SET_DSTXY(fb, packed_dst);
+		packed_len = (4 << 16) | 1;
+		SET_LENXY_START_RECFILL(fb, packed_len);
+	}   /* ELK Hardware Kludge */
+
+	/**** Finally, set the Control Plane Register back to zero: ****/
+	GET_FIFO_SLOTS(fb, nFreeFifoSlots, 1);
+	NGLE_QUICK_SET_CTL_PLN_REG(fb, 0);
+	
+	NGLE_UNLOCK(fb);
+}
+    
+static void
+ngleClearOverlayPlanes(struct stifb_info *fb, int mask, int data)
+{
+	int nFreeFifoSlots = 0;
+	u32 packed_dst;
+	u32 packed_len;
+    
+	NGLE_LOCK(fb);
+
+	/* Hardware setup */
+	GET_FIFO_SLOTS(fb, nFreeFifoSlots, 8);
+	NGLE_QUICK_SET_DST_BM_ACCESS(fb, 
+				     BA(IndexedDcd, Otc04, Ots08, AddrLong,
+					BAJustPoint(0), BINovly, BAIndexBase(0)));
+
+        NGLE_SET_TRANSFERDATA(fb, 0xffffffff);  /* Write foreground color */
+
+        NGLE_REALLY_SET_IMAGE_FG_COLOR(fb, data);
+        NGLE_REALLY_SET_IMAGE_PLANEMASK(fb, mask);
+    
+        packed_dst = 0;
+        packed_len = (fb->var.xres << 16) | fb->var.yres;
+        NGLE_SET_DSTXY(fb, packed_dst);
+    
+        /* Write zeroes to overlay planes */		       
+	NGLE_QUICK_SET_IMAGE_BITMAP_OP(fb,
+				       IBOvals(RopSrc, MaskAddrOffset(0),
+					       BitmapExtent08, StaticReg(0),
+					       DataDynamic, MaskOtc, BGx(0), FGx(0)));
+		       
+        SET_LENXY_START_RECFILL(fb, packed_len);
+
+	NGLE_UNLOCK(fb);
+}
+
+static void 
+hyperResetPlanes(struct stifb_info *fb, int enable)
+{
+	unsigned int controlPlaneReg;
+
+	NGLE_LOCK(fb);
+
+	if (IS_24_DEVICE(fb))
+		if (fb->var.bits_per_pixel == 32)
+			controlPlaneReg = 0x04000F00;
+		else
+			controlPlaneReg = 0x00000F00;   /* 0x00000800 should be enought, but lets clear all 4 bits */
+	else
+		controlPlaneReg = 0x00000F00; /* 0x00000100 should be enought, but lets clear all 4 bits */
+
+	switch (enable) {
+	case 1:		/* ENABLE */
+		/* clear screen */
+		if (IS_24_DEVICE(fb))
+			ngleDepth24_ClearImagePlanes(fb);
+		else
+			ngleDepth8_ClearImagePlanes(fb);
+
+		/* Paint attribute planes for default case.
+		 * On Hyperdrive, this means all windows using overlay cmap 0. */
+		ngleResetAttrPlanes(fb, controlPlaneReg);
+
+		/* clear overlay planes */
+	        ngleClearOverlayPlanes(fb, 0xff, 255);
+
+		/**************************************************
+		 ** Also need to counteract ITE settings 
+		 **************************************************/
+		hyperUndoITE(fb);
+		break;
+
+	case 0:		/* DISABLE */
+		/* clear screen */
+		if (IS_24_DEVICE(fb))
+			ngleDepth24_ClearImagePlanes(fb);
+		else
+			ngleDepth8_ClearImagePlanes(fb);
+		ngleResetAttrPlanes(fb, controlPlaneReg);
+		ngleClearOverlayPlanes(fb, 0xff, 0);
+		break;
+
+	case -1:	/* RESET */
+		hyperUndoITE(fb);
+		ngleResetAttrPlanes(fb, controlPlaneReg);
+		break;
+    	}
+	
+	NGLE_UNLOCK(fb);
+}
+
+/* Return pointer to in-memory structure holding ELK device-dependent ROM values. */
+
+static void 
+ngleGetDeviceRomData(struct stifb_info *fb)
+{
+#if 0
+XXX: FIXME: !!!
+	int	*pBytePerLongDevDepData;/* data byte == LSB */
+	int 	*pRomTable;
+	NgleDevRomData	*pPackedDevRomData;
+	int	sizePackedDevRomData = sizeof(*pPackedDevRomData);
+	char	*pCard8;
+	int	i;
+	char	*mapOrigin = NULL;
+    
+	int romTableIdx;
+
+	pPackedDevRomData = fb->ngle_rom;
+
+	SETUP_HW(fb);
+	if (fb->id == S9000_ID_ARTIST) {
+		pPackedDevRomData->cursor_pipeline_delay = 4;
+		pPackedDevRomData->video_interleaves     = 4;
+	} else {
+		/* Get pointer to unpacked byte/long data in ROM */
+		pBytePerLongDevDepData = fb->sti->regions[NGLEDEVDEPROM_CRT_REGION];
+
+		/* Tomcat supports several resolutions: 1280x1024, 1024x768, 640x480 */
+		if (fb->id == S9000_ID_TOMCAT)
+	{
+	    /*  jump to the correct ROM table  */
+	    GET_ROMTABLE_INDEX(romTableIdx);
+	    while  (romTableIdx > 0)
+	    {
+		pCard8 = (Card8 *) pPackedDevRomData;
+		pRomTable = pBytePerLongDevDepData;
+		/* Pack every fourth byte from ROM into structure */
+		for (i = 0; i < sizePackedDevRomData; i++)
+		{
+		    *pCard8++ = (Card8) (*pRomTable++);
+		}
+
+		pBytePerLongDevDepData = (Card32 *)
+			((Card8 *) pBytePerLongDevDepData +
+			       pPackedDevRomData->sizeof_ngle_data);
+
+		romTableIdx--;
+	    }
+	}
+
+	pCard8 = (Card8 *) pPackedDevRomData;
+
+	/* Pack every fourth byte from ROM into structure */
+	for (i = 0; i < sizePackedDevRomData; i++)
+	{
+	    *pCard8++ = (Card8) (*pBytePerLongDevDepData++);
+	}
+    }
+
+    SETUP_FB(fb);
+#endif
+}
+
+
+#define HYPERBOWL_MODE_FOR_8_OVER_88_LUT0_NO_TRANSPARENCIES	4
+#define HYPERBOWL_MODE01_8_24_LUT0_TRANSPARENT_LUT1_OPAQUE	8
+#define HYPERBOWL_MODE01_8_24_LUT0_OPAQUE_LUT1_OPAQUE		10
+#define HYPERBOWL_MODE2_8_24					15
+
+/* HCRX specific boot-time initialization */
+static void __init
+SETUP_HCRX(struct stifb_info *fb)
+{
+	int	hyperbowl;
+        int	nFreeFifoSlots = 0;
+
+	if (fb->id != S9000_ID_HCRX)
+		return;
+
+	/* Initialize Hyperbowl registers */
+	GET_FIFO_SLOTS(fb, nFreeFifoSlots, 7);
+	
+	if (IS_24_DEVICE(fb)) {
+		hyperbowl = (fb->var.bits_per_pixel == 32) ?
+			HYPERBOWL_MODE01_8_24_LUT0_TRANSPARENT_LUT1_OPAQUE :
+			HYPERBOWL_MODE01_8_24_LUT0_OPAQUE_LUT1_OPAQUE;
+
+		/* First write to Hyperbowl must happen twice (bug) */
+		WRITE_WORD(hyperbowl, fb, REG_40);
+		WRITE_WORD(hyperbowl, fb, REG_40);
+		
+		WRITE_WORD(HYPERBOWL_MODE2_8_24, fb, REG_39);
+		
+		WRITE_WORD(0x014c0148, fb, REG_42); /* Set lut 0 to be the direct color */
+		WRITE_WORD(0x404c4048, fb, REG_43);
+		WRITE_WORD(0x034c0348, fb, REG_44);
+		WRITE_WORD(0x444c4448, fb, REG_45);
+	} else {
+		hyperbowl = HYPERBOWL_MODE_FOR_8_OVER_88_LUT0_NO_TRANSPARENCIES;
+
+		/* First write to Hyperbowl must happen twice (bug) */
+		WRITE_WORD(hyperbowl, fb, REG_40);
+		WRITE_WORD(hyperbowl, fb, REG_40);
+
+		WRITE_WORD(0x00000000, fb, REG_42);
+		WRITE_WORD(0x00000000, fb, REG_43);
+		WRITE_WORD(0x00000000, fb, REG_44);
+		WRITE_WORD(0x444c4048, fb, REG_45);
+	}
+}
+
+
+/* ------------------- driver specific functions --------------------------- */
+
+static int
+stifb_getcolreg(u_int regno, u_int *red, u_int *green,
+	      u_int *blue, u_int *transp, struct fb_info *info)
+{
+	struct stifb_info *fb = (struct stifb_info *) info;
+
+	if (regno > 255)
+		return 1;
+	*red = (fb->palette[regno].red<<8) | fb->palette[regno].red;
+	*green = (fb->palette[regno].green<<8) | fb->palette[regno].green;
+	*blue = (fb->palette[regno].blue<<8) | fb->palette[regno].blue;
+	*transp = 0;
+
+	return 0;
+}
+
+static int
+stifb_setcolreg(u_int regno, u_int red, u_int green,
+	      u_int blue, u_int transp, struct fb_info *info)
+{
+	struct stifb_info *fb = (struct stifb_info *) info;
+
+	if (regno > 255)
+		return 1;
+
+	red >>= 8;
+	green >>= 8;
+	blue >>= 8;
+	
+	if ((fb->palette[regno].red != red) ||
+	    (fb->palette[regno].green != green) ||
+	    (fb->palette[regno].blue != blue))
+		fb->cmap_reload = 1;
+	    
+	fb->palette[regno].red = red;
+	fb->palette[regno].green = green;
+	fb->palette[regno].blue = blue;
+    
+#ifdef FBCON_HAS_CFB32
+	if (regno < 16 && fb->var.bits_per_pixel == 32) {
+		fb->fbcon_cmap.cfb32[regno] = ((red << 16) |
+					       (green << 8) |
+					       (blue << 0) |
+					       (transp << 24));
+	}
+#endif
+	return 0;
+}
+
+static void
+stifb_loadcmap(struct stifb_info *fb)
+{
+	u32 color;
+	int i;
+
+	if (!fb->cmap_reload)
+		return;
+
+	START_IMAGE_COLORMAP_ACCESS(fb);
+	for (i = 0; i < 256; i++) {
+		if (fb->var.bits_per_pixel > 8) {
+			color = (i << 16) | (i << 8) | i;
+		} else {
+			if (fb->var.grayscale) {
+				/* gray = 0.30*R + 0.59*G + 0.11*B */
+				color = ((fb->palette[i].red * 77) +
+					 (fb->palette[i].green * 151) +
+					 (fb->palette[i].blue * 28)) >> 8;
+			} else {
+				color = ((fb->palette[i].red << 16) |
+					 (fb->palette[i].green << 8) |
+					 (fb->palette[i].blue));
+			}
+		}
+		WRITE_IMAGE_COLOR(fb, i, color);
+	}
+	if (fb->id == S9000_ID_HCRX) {
+		NgleLutBltCtl lutBltCtl;
+
+		lutBltCtl = setHyperLutBltCtl(fb,
+				0,	/* Offset w/i LUT */
+				256);	/* Load entire LUT */
+		NGLE_BINC_SET_SRCADDR(fb,
+				NGLE_LONG_FB_ADDRESS(0, 0x100, 0)); 
+				/* 0x100 is same as used in WRITE_IMAGE_COLOR() */
+		START_COLORMAPLOAD(fb, lutBltCtl.all);
+		SETUP_FB(fb);
+	} else {
+		/* cleanup colormap hardware */
+		FINISH_IMAGE_COLORMAP_ACCESS(fb);
+	}
+	fb->cmap_reload = 0;
+}
+
+static int
+stifb_get_fix(struct fb_fix_screeninfo *fix, int con,
+	      struct fb_info *info)
+{
+	memcpy (fix, &((struct stifb_info *)info)->fix, sizeof (*fix));
+	return 0;
+}
+
+static int
+stifb_get_var(struct fb_var_screeninfo *var, int con,
+	      struct fb_info *info)
+{
+	memcpy (var, &((struct stifb_info *)info)->var, sizeof (*var));
+	return 0;
+}
+
+static int
+stifb_set_var(struct fb_var_screeninfo *var, int con,
+	      struct fb_info *info)
+{
+	struct display *disp;
+	
+	if (con >= 0)
+		disp = &fb_display[con];
+	else
+		disp = info->disp;
+
+	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+		if (disp->var.xres != var->xres ||
+		    disp->var.yres != var->yres ||
+		    disp->var.xres_virtual != var->xres_virtual ||
+		    disp->var.yres_virtual != var->yres_virtual ||
+		    disp->var.bits_per_pixel != var->bits_per_pixel ||
+		    disp->var.accel_flags != var->accel_flags)
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+stifb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+	       struct fb_info *info)
+{
+	struct stifb_info *fb = (struct stifb_info *)info;
+	
+	if (con == fb->currcon) /* current console ? */
+		return fb_get_cmap(cmap, kspc, stifb_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(fb_display[con].var.bits_per_pixel > 8 ? 16 : 256), cmap, kspc ? 0: 2);
+	return 0;
+}
+
+static int
+stifb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+	       struct fb_info *info)
+{
+	struct stifb_info *fb = (struct stifb_info *)info;
+	struct display *disp;
+	int err;
+	
+	if (con >= 0)
+		disp = &fb_display[con];
+	else
+		disp = info->disp;
+
+	if (!disp->cmap.len) { /* no colormap allocated ? */
+		if ((err = fb_alloc_cmap(&disp->cmap, disp->var.bits_per_pixel > 8 ? 16 : 256, 0)))
+			return err;
+	}
+	if (con == fb->currcon || con == -1) {
+		err = fb_set_cmap(cmap, kspc, stifb_setcolreg, info);
+		if (!err)
+			stifb_loadcmap ((struct stifb_info *)info);
+		return err;
+	} else
+		fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
+	return 0;
+}
+					 
+static void
+stifb_blank(int blank_mode, struct fb_info *info)
+{
+	struct stifb_info *fb = (struct stifb_info *) info;
+	int enable = (blank_mode == 0) ? ENABLE : DISABLE;
+
+	switch (fb->id) {
+	case S9000_ID_A1439A:
+		CRX24_ENABLE_DISABLE_DISPLAY(fb, enable);
+		break;
+	case CRT_ID_VISUALIZE_EG:
+	case S9000_ID_ARTIST:
+		ARTIST_ENABLE_DISABLE_DISPLAY(fb, enable);
+		break;
+	case S9000_ID_HCRX:
+		HYPER_ENABLE_DISABLE_DISPLAY(fb, enable);
+		break;
+	case S9000_ID_A1659A:;	/* fall through */
+	case S9000_ID_TIMBER:;
+	case CRX24_OVERLAY_PLANES:;
+	default:
+		ENABLE_DISABLE_DISPLAY(fb, enable);
+		break;
+	}
+	
+	SETUP_FB(fb);
+}
+
+static void
+stifb_set_disp(struct stifb_info *fb)
+{
+	int id = fb->id;
+
+	SETUP_FB(fb);
+
+	/* HCRX specific initialization */
+	SETUP_HCRX(fb);
+	
+	/*
+	if (id == S9000_ID_HCRX)
+		hyperInitSprite(fb);
+	else
+		ngleInitSprite(fb);
+	*/
+	
+	/* Initialize the image planes. */ 
+        switch (id) {
+	 case S9000_ID_HCRX:
+	    hyperResetPlanes(fb, ENABLE);
+	    break;
+	 case S9000_ID_A1439A:
+	    rattlerSetupPlanes(fb);
+	    break;
+	 case S9000_ID_A1659A:
+	 case S9000_ID_ARTIST:
+	 case CRT_ID_VISUALIZE_EG:
+	    elkSetupPlanes(fb);
+	    break;
+	}
+
+	/* Clear attribute planes on non HCRX devices. */
+        switch (id) {
+	 case S9000_ID_A1659A:
+	 case S9000_ID_A1439A:
+	    if (fb->var.bits_per_pixel == 32)
+		ngleSetupAttrPlanes(fb, BUFF1_CMAP3);
+	    else {
+		ngleSetupAttrPlanes(fb, BUFF1_CMAP0);
+	    }
+	    if (id == S9000_ID_A1439A)
+		ngleClearOverlayPlanes(fb, 0xff, 0);
+	    break;
+	 case S9000_ID_ARTIST:
+	 case CRT_ID_VISUALIZE_EG:
+	    if (fb->var.bits_per_pixel == 32)
+		ngleSetupAttrPlanes(fb, BUFF1_CMAP3);
+	    else {
+		ngleSetupAttrPlanes(fb, ARTIST_CMAP0);
+	    }
+	    break;
+	}
+	stifb_blank(0, (struct fb_info *)fb);	/* 0=enable screen */
+
+	SETUP_FB(fb);
+}
+
+static int
+stifb_switch(int con, struct fb_info *info)
+{
+	struct stifb_info *fb = (struct stifb_info *)info;
+	
+	/* Do we have to save the colormap ? */
+	if (fb->currcon != -1 && fb_display[fb->currcon].cmap.len)
+		fb_get_cmap(&fb_display[fb->currcon].cmap, 1, stifb_getcolreg, info);
+
+	fb->currcon = con;
+	/* Install new colormap */
+	if (fb_display[con].cmap.len)
+		fb_set_cmap(&fb_display[con].cmap, 1, stifb_setcolreg, info);
+	else
+		fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel > 8 ? 16 : 256),
+			    1, stifb_setcolreg, info);
+	stifb_loadcmap ((struct stifb_info *)info);
+	return 0;
+}
+
+static int
+stifb_update_var(int con, struct fb_info *info)
+{
+	return 0;
+}
+
+/* ------------ Interfaces to hardware functions ------------ */
+
+static struct fb_ops stifb_ops = {
+	owner:		THIS_MODULE,
+	fb_get_fix:	stifb_get_fix,
+	fb_get_var:	stifb_get_var,
+	fb_set_var:	stifb_set_var,
+	fb_get_cmap:	stifb_get_cmap,
+	fb_set_cmap:	stifb_set_cmap,
+//	fb_pan_display:	fbgen_pan_display,
+//	fb_ioctl:       xxxfb_ioctl,   /* optional */      
+};
+
+
+    /*
+     *  Initialization
+     */
+
+int __init
+stifb_init_fb(struct sti_struct *sti, int force_bpp)
+{
+	struct fb_fix_screeninfo *fix;
+	struct fb_var_screeninfo *var;
+	struct display *disp;
+	struct stifb_info *fb;
+	unsigned long sti_rom_address;
+	char *dev_name;
+	int bpp, xres, yres;
+
+	fb = kmalloc(sizeof(struct stifb_info), GFP_ATOMIC);
+	if (!fb) {
+		printk(KERN_ERR "stifb: Could not allocate stifb structure\n");
+		return -ENODEV;
+	}
+	
+	/* set struct to a known state */
+	memset(fb, 0, sizeof(struct stifb_info));
+	fix = &fb->fix;
+	var = &fb->var;
+	disp = &fb->disp;
+
+	fb->currcon = -1;
+	fb->cmap_reload = 1;
+	fb->sti = sti;
+	/* store upper 32bits of the graphics id */
+	fb->id = fb->sti->graphics_id[0];
+	fb->real_id = fb->id;	/* save the real id */
+
+	/* only supported cards are allowed */
+	switch (fb->id) {
+	case S9000_ID_ARTIST:
+	case S9000_ID_HCRX:
+	case S9000_ID_TIMBER:
+	case S9000_ID_A1659A:
+	case S9000_ID_A1439A:
+	case CRT_ID_VISUALIZE_EG:
+		break;
+	default:
+		printk(KERN_WARNING "stifb: Unsupported gfx card id 0x%08x\n",
+			fb->id);
+		goto out_err1;
+	}
+	
+	/* default to 8 bpp on most graphic chips */
+	bpp = 8;
+	xres = sti_onscreen_x(fb->sti);
+	yres = sti_onscreen_y(fb->sti);
+
+	ngleGetDeviceRomData(fb);
+
+	/* get (virtual) io region base addr */
+	fix->mmio_start = REGION_BASE(fb,2);
+	fix->mmio_len   = 0x400000;
+
+       	/* Reject any device not in the NGLE family */
+	switch (fb->id) {
+	case S9000_ID_A1659A:	/* CRX/A1659A */
+		break;
+	case S9000_ID_ELM:	/* GRX, grayscale but else same as A1659A */
+		var->grayscale = 1;
+		fb->id = S9000_ID_A1659A;
+		break;
+	case S9000_ID_TIMBER:	/* HP9000/710 Any (may be a grayscale device) */
+		dev_name = fb->sti->outptr.dev_name;
+		if (strstr(dev_name, "GRAYSCALE") || 
+		    strstr(dev_name, "Grayscale") ||
+		    strstr(dev_name, "grayscale"))
+			var->grayscale = 1;
+		break;
+	case S9000_ID_TOMCAT:	/* Dual CRX, behaves else like a CRX */
+		/* FIXME: TomCat supports two heads:
+		 * fb.iobase = REGION_BASE(fb_info,3);
+		 * fb.screen_base = (void*) REGION_BASE(fb_info,2);
+		 * for now we only support the left one ! */
+		xres = fb->ngle_rom.x_size_visible;
+		yres = fb->ngle_rom.y_size_visible;
+		fb->id = S9000_ID_A1659A;
+		break;
+	case S9000_ID_A1439A:	/* CRX24/A1439A */
+		bpp = 32;
+		break;
+	case S9000_ID_HCRX:	/* Hyperdrive/HCRX */
+		memset(&fb->ngle_rom, 0, sizeof(fb->ngle_rom));
+		if ((fb->sti->regions_phys[0] & 0xfc000000) ==
+		    (fb->sti->regions_phys[2] & 0xfc000000))
+			sti_rom_address = fb->sti->regions_phys[0];
+		else
+			sti_rom_address = fb->sti->regions_phys[1];
+#ifdef __LP64__
+	        sti_rom_address |= 0xffffffff00000000;
+#endif
+		fb->deviceSpecificConfig = __raw_readl(sti_rom_address);
+		if (IS_24_DEVICE(fb)) {
+			if (force_bpp == 8 || force_bpp == 32)
+				bpp = force_bpp;
+			else
+				bpp = 32;
+		} else
+			bpp = 8;
+		READ_WORD(fb, REG_15);
+		SETUP_HW(fb);
+		break;
+	case CRT_ID_VISUALIZE_EG:
+	case S9000_ID_ARTIST:	/* Artist */
+		break;
+	default: 
+#ifdef FALLBACK_TO_1BPP
+	       	printk(KERN_WARNING 
+			"stifb: Unsupported graphics card (id=0x%08x) "
+				"- now trying 1bpp mode instead\n",
+			fb->id);
+		bpp = 1;	/* default to 1 bpp */
+		break;
+#else
+	       	printk(KERN_WARNING 
+			"stifb: Unsupported graphics card (id=0x%08x) "
+				"- skipping.\n",
+			fb->id);
+		goto out_err1;
+#endif
+	}
+
+
+	/* get framebuffer pysical and virtual base addr & len (64bit ready) */
+	fix->smem_start = fb->sti->regions_phys[1] | 0xffffffff00000000;
+	fix->smem_len = fb->sti->regions[1].region_desc.length * 4096;
+
+	fix->line_length = (fb->sti->glob_cfg->total_x * bpp) / 8;
+	if (!fix->line_length)
+		fix->line_length = 2048; /* default */
+	fix->accel = FB_ACCEL_NONE;
+
+	switch (bpp) {
+	    case 1:
+		fix->type = FB_TYPE_PLANES;	/* well, sort of */
+		fix->visual = FB_VISUAL_MONO10;
+		disp->dispsw = &fbcon_sti;
+		break;
+#ifdef FBCON_HAS_CFB8		
+	    case 8:
+		fix->type = FB_TYPE_PACKED_PIXELS;
+		fix->visual = FB_VISUAL_PSEUDOCOLOR;
+	 	disp->dispsw = &fbcon_cfb8;
+		var->red.length = var->green.length = var->blue.length = 8;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	    case 32:
+		fix->type = FB_TYPE_PACKED_PIXELS;
+		fix->visual = FB_VISUAL_TRUECOLOR;
+		disp->dispsw = &fbcon_cfb32;
+		disp->dispsw_data = fb->fbcon_cmap.cfb32;
+		var->red.length = var->green.length = var->blue.length = var->transp.length = 8;
+		var->blue.offset = 0;
+		var->green.offset = 8;
+		var->red.offset = 16;
+		var->transp.offset = 24;
+		break;
+#endif
+	    default:
+		disp->dispsw = &fbcon_dummy;
+		break;
+	}
+	
+	var->xres = var->xres_virtual = xres;
+	var->yres = var->yres_virtual = yres;
+	var->bits_per_pixel = bpp;
+
+	disp->var = *var;
+	disp->visual = fix->visual;
+	disp->type = fix->type;
+	disp->type_aux = fix->type_aux;
+	disp->line_length = fix->line_length;
+	disp->var.activate = FB_ACTIVATE_NOW;
+	disp->screen_base = (void*) REGION_BASE(fb,1);
+	disp->can_soft_blank = 1;
+	disp->scrollmode = SCROLL_YREDRAW;
+	
+	strcpy(fb->info.modename, "stifb");
+	fb->info.node = -1;
+	fb->info.flags = FBINFO_FLAG_DEFAULT;
+	fb->info.fbops = &stifb_ops;
+	fb->info.disp = disp;
+	fb->info.changevar = NULL;
+	fb->info.switch_con = &stifb_switch;
+	fb->info.updatevar = &stifb_update_var;
+	fb->info.blank = &stifb_blank;
+	fb->info.flags = FBINFO_FLAG_DEFAULT;
+	
+	stifb_set_var(&disp->var, 1, &fb->info);
+	
+	stifb_set_disp(fb);
+
+	if (!request_mem_region(fix->smem_start, fix->smem_len, "stifb")) {
+		printk(KERN_ERR "stifb: cannot reserve fb region 0x%04lx-0x%04lx\n",
+				fix->smem_start, fix->smem_start+fix->smem_len);
+		goto out_err1;
+	}
+		
+	if (!request_mem_region(fix->mmio_start, fix->mmio_len, "stifb mmio")) {
+		printk(KERN_ERR "stifb: cannot reserve sti mmio region 0x%04lx-0x%04lx\n",
+				fix->mmio_start, fix->mmio_start+fix->mmio_len);
+		goto out_err2;
+	}
+
+	if (register_framebuffer(&fb->info) < 0)
+		goto out_err3;
+
+	printk(KERN_INFO 
+	    "fb%d: %s %dx%d-%d frame buffer device, id: %04x, mmio: 0x%04lx\n",
+		GET_FB_IDX(fb->info.node), 
+		fb->info.modename,
+		disp->var.xres, 
+		disp->var.yres,
+		disp->var.bits_per_pixel,
+		fb->id, 
+		fix->mmio_start);
+
+	return 0;
+
+
+out_err3:
+	release_mem_region(fix->mmio_start, fix->mmio_len);
+out_err2:
+	release_mem_region(fix->smem_start, fix->smem_len);
+out_err1:
+	kfree(fb);
+	return -ENXIO;
+}
+
+int __init
+stifb_init(void)
+{
+	struct sti_struct *sti;
+	int i;
+	
+	
+	if (sti_init_roms() == NULL)
+		return -ENXIO; /* no STI cards available */
+
+	for (i = 0; i < MAX_STI_ROMS; i++) {
+		sti = sti_get_rom(i);
+		if (sti)
+			stifb_init_fb (sti, stifb_force_bpp[i]);
+		else
+			break;
+	}
+	return 0;
+}
+
+/*
+ *  Cleanup
+ */
+
+void __exit
+stifb_cleanup(struct fb_info *info)
+{
+	// unregister_framebuffer(info); 
+}
+
+int __init
+stifb_setup(char *options)
+{
+	int i;
+	
+	if (!options || !*options)
+		return 0;
+	
+	if (strncmp(options, "bpp", 3) == 0) {
+		options += 3;
+		for (i = 0; i < MAX_STI_ROMS; i++) {
+			if (*options++ == ':')
+				stifb_force_bpp[i] = simple_strtoul(options, &options, 10);
+			else
+				break;
+		}
+	}
+	return 0;
+}
+
+__setup("stifb=", stifb_setup);
+
+#ifdef MODULE
+module_init(stifb_init);
+#endif
+module_exit(stifb_cleanup);
+
+MODULE_AUTHOR("Helge Deller <deller@gmx.de>, Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
+MODULE_DESCRIPTION("Framebuffer driver for HP's NGLE series graphics cards in HP PARISC machines");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(bpp, "i");
+MODULE_PARM_DESC(mem, "Bits per pixel (default: 8)");
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)