Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / video / fbdev / stifb.c
diff --git a/kernel/drivers/video/fbdev/stifb.c b/kernel/drivers/video/fbdev/stifb.c
new file mode 100644 (file)
index 0000000..86621fa
--- /dev/null
@@ -0,0 +1,1417 @@
+/*
+ * linux/drivers/video/stifb.c - 
+ * Low level Frame buffer driver for HP workstations with 
+ * STI (standard text interface) video firmware.
+ *
+ * Copyright (C) 2001-2006 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:
+ *     - 1bpp mode is completely untested
+ *     - add support for h/w acceleration
+ *     - add hardware cursor
+ *     - automatically disable double buffering (e.g. on RDI precisionbook laptop)
+ */
+
+
+/* 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
+
+#undef DEBUG_STIFB_REGS                /* debug sti register accesses */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+
+#include <asm/grfioctl.h>      /* for HP-UX compatibility */
+#include <asm/uaccess.h>
+
+#include "sticore.h"
+
+/* REGION_BASE(fb_info, index) returns the virtual address for region <index> */
+#define REGION_BASE(fb_info, index) \
+       F_EXTEND(fb_info->sti->glob_cfg->region_ptrs[index])
+
+#define NGLEDEVDEPROM_CRT_REGION 1
+
+#define NR_PALETTE 256
+
+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;
+       unsigned int id;
+       ngle_rom_t ngle_rom;
+       struct sti_struct *sti;
+       int deviceSpecificConfig;
+       u32 pseudo_palette[16];
+};
+
+static int __initdata stifb_bpp_pref[MAX_STI_ROMS];
+
+/* ------------------- 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)              gsc_readb((fb)->info.fix.mmio_start + (reg))
+#define READ_WORD(fb,reg)              gsc_readl((fb)->info.fix.mmio_start + (reg))
+
+
+#ifndef DEBUG_STIFB_REGS
+# define  DEBUG_OFF()
+# define  DEBUG_ON()
+# define WRITE_BYTE(value,fb,reg)      gsc_writeb((value),(fb)->info.fix.mmio_start + (reg))
+# define WRITE_WORD(value,fb,reg)      gsc_writel((value),(fb)->info.fix.mmio_start + (reg))
+#else
+  static int debug_on = 1;
+# define  DEBUG_OFF() debug_on=0
+# define  DEBUG_ON()  debug_on=1
+# define WRITE_BYTE(value,fb,reg)      do { if (debug_on) \
+                                               printk(KERN_DEBUG "%30s: WRITE_BYTE(0x%06x) = 0x%02x (old=0x%02x)\n", \
+                                                       __func__, reg, value, READ_BYTE(fb,reg));                 \
+                                       gsc_writeb((value),(fb)->info.fix.mmio_start + (reg)); } while (0)
+# define WRITE_WORD(value,fb,reg)      do { if (debug_on) \
+                                               printk(KERN_DEBUG "%30s: WRITE_WORD(0x%06x) = 0x%08x (old=0x%08x)\n", \
+                                                       __func__, reg, value, READ_WORD(fb,reg));                 \
+                                       gsc_writel((value),(fb)->info.fix.mmio_start + (reg)); } while (0)
+#endif /* DEBUG_STIFB_REGS */
+
+
+#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->info.var.bits_per_pixel == 32)                                          
+                               reg10_value = 0xBBA0A000;
+                       else 
+                               reg10_value = 0x13601000;
+                       break;
+               case S9000_ID_HCRX:
+                       if (fb->info.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->info.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) 
+{
+       /* REG_6 seems to have special values when run on a 
+          RDI precisionbook parisc laptop (INTERNAL_EG_DX1024 or
+          INTERNAL_EG_X1024).  The values are:
+               0x2f0: internal (LCD) & external display enabled
+               0x2a0: external display only
+               0x000: zero on standard artist graphic cards
+       */ 
+       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->info.var.xres, fb->info.var.yres);
+       FINISH_ATTR_ACCESS(fb);
+       SETUP_FB(fb);
+}
+
+
+static void
+rattlerSetupPlanes(struct stifb_info *fb)
+{
+       int saved_id, y;
+
+       /* Write RAMDAC pixel read mask register so all overlay
+        * planes are display-enabled.  (CRX24 uses Bt462 pixel
+        * read mask register for overlay planes, not image planes).
+        */
+       CRX24_SETUP_RAMDAC(fb);
+    
+       /* change fb->id temporarily to fool SETUP_FB() */
+       saved_id = fb->id;
+       fb->id = CRX24_OVERLAY_PLANES;
+       SETUP_FB(fb);
+       fb->id = saved_id;
+
+       for (y = 0; y < fb->info.var.yres; ++y)
+               memset(fb->info.screen_base + y * fb->info.fix.line_length,
+                       0xff, fb->info.var.xres * fb->info.var.bits_per_pixel/8);
+
+       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->info.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->info.var.xres << 16) | fb->info.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->info.var.xres << 16) | fb->info.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->info.var.bits_per_pixel == 32)
+                       controlPlaneReg = 0x04000F00;
+               else
+                       controlPlaneReg = 0x00000F00;   /* 0x00000800 should be enough, but lets clear all 4 bits */
+       else
+               controlPlaneReg = 0x00000F00; /* 0x00000100 should be enough, but lets clear all 4 bits */
+
+       switch (enable) {
+       case 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 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->info.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_setcolreg(u_int regno, u_int red, u_int green,
+             u_int blue, u_int transp, struct fb_info *info)
+{
+       struct stifb_info *fb = container_of(info, struct stifb_info, info);
+       u32 color;
+
+       if (regno >= NR_PALETTE)
+               return 1;
+
+       red   >>= 8;
+       green >>= 8;
+       blue  >>= 8;
+
+       DEBUG_OFF();
+
+       START_IMAGE_COLORMAP_ACCESS(fb);
+
+       if (unlikely(fb->info.var.grayscale)) {
+               /* gray = 0.30*R + 0.59*G + 0.11*B */
+               color = ((red * 77) +
+                        (green * 151) +
+                        (blue * 28)) >> 8;
+       } else {
+               color = ((red << 16) |
+                        (green << 8) |
+                        (blue));
+       }
+
+       if (fb->info.fix.visual == FB_VISUAL_DIRECTCOLOR) {
+               struct fb_var_screeninfo *var = &fb->info.var;
+               if (regno < 16)
+                       ((u32 *)fb->info.pseudo_palette)[regno] =
+                               regno << var->red.offset |
+                               regno << var->green.offset |
+                               regno << var->blue.offset;
+       }
+
+       WRITE_IMAGE_COLOR(fb, regno, 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);
+       }
+
+       DEBUG_ON();
+
+       return 0;
+}
+
+static int
+stifb_blank(int blank_mode, struct fb_info *info)
+{
+       struct stifb_info *fb = container_of(info, 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);
+       return 0;
+}
+
+static void __init
+stifb_init_display(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->info.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->info.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);
+}
+
+/* ------------ Interfaces to hardware functions ------------ */
+
+static struct fb_ops stifb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_setcolreg   = stifb_setcolreg,
+       .fb_blank       = stifb_blank,
+       .fb_fillrect    = cfb_fillrect,
+       .fb_copyarea    = cfb_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+};
+
+
+/*
+ *  Initialization
+ */
+
+static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref)
+{
+       struct fb_fix_screeninfo *fix;
+       struct fb_var_screeninfo *var;
+       struct stifb_info *fb;
+       struct fb_info *info;
+       unsigned long sti_rom_address;
+       char *dev_name;
+       int bpp, xres, yres;
+
+       fb = kzalloc(sizeof(*fb), GFP_ATOMIC);
+       if (!fb) {
+               printk(KERN_ERR "stifb: Could not allocate stifb structure\n");
+               return -ENODEV;
+       }
+       
+       info = &fb->info;
+
+       /* set struct to a known state */
+       fix = &info->fix;
+       var = &info->var;
+
+       fb->sti = sti;
+       dev_name = sti->sti_data->inq_outptr.dev_name;
+       /* store upper 32bits of the graphics id */
+       fb->id = fb->sti->graphics_id[0];
+
+       /* only supported cards are allowed */
+       switch (fb->id) {
+       case CRT_ID_VISUALIZE_EG:
+               /* Visualize cards can run either in "double buffer" or
+                 "standard" mode. Depending on the mode, the card reports
+                 a different device name, e.g. "INTERNAL_EG_DX1024" in double
+                 buffer mode and "INTERNAL_EG_X1024" in standard mode.
+                 Since this driver only supports standard mode, we check
+                 if the device name contains the string "DX" and tell the
+                 user how to reconfigure the card. */
+               if (strstr(dev_name, "DX")) {
+                  printk(KERN_WARNING
+"WARNING: stifb framebuffer driver does not support '%s' in double-buffer mode.\n"
+"WARNING: Please disable the double-buffer mode in IPL menu (the PARISC-BIOS).\n",
+                       dev_name);
+                  goto out_err0;
+               }
+               /* fall though */
+       case S9000_ID_ARTIST:
+       case S9000_ID_HCRX:
+       case S9000_ID_TIMBER:
+       case S9000_ID_A1659A:
+       case S9000_ID_A1439A:
+               break;
+       default:
+               printk(KERN_WARNING "stifb: '%s' (id: 0x%08x) not supported.\n",
+                       dev_name, fb->id);
+               goto out_err0;
+       }
+       
+       /* 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) */
+               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 = ioremap_nocache(REGION_BASE(fb_info,2),xxx);
+                * 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 = F_EXTEND(fb->sti->regions_phys[0]);
+               else
+                       sti_rom_address = F_EXTEND(fb->sti->regions_phys[1]);
+
+               fb->deviceSpecificConfig = gsc_readl(sti_rom_address);
+               if (IS_24_DEVICE(fb)) {
+                       if (bpp_pref == 8 || bpp_pref == 32)
+                               bpp = bpp_pref;
+                       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_err0;
+#endif
+       }
+
+
+       /* get framebuffer physical and virtual base addr & len (64bit ready) */
+       fix->smem_start = F_EXTEND(fb->sti->regions_phys[1]);
+       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 */
+       
+       /* limit fbsize to max visible screen size */
+       if (fix->smem_len > yres*fix->line_length)
+               fix->smem_len = yres*fix->line_length;
+       
+       fix->accel = FB_ACCEL_NONE;
+
+       switch (bpp) {
+           case 1:
+               fix->type = FB_TYPE_PLANES;     /* well, sort of */
+               fix->visual = FB_VISUAL_MONO10;
+               var->red.length = var->green.length = var->blue.length = 1;
+               break;
+           case 8:
+               fix->type = FB_TYPE_PACKED_PIXELS;
+               fix->visual = FB_VISUAL_PSEUDOCOLOR;
+               var->red.length = var->green.length = var->blue.length = 8;
+               break;
+           case 32:
+               fix->type = FB_TYPE_PACKED_PIXELS;
+               fix->visual = FB_VISUAL_DIRECTCOLOR;
+               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;
+           default:
+               break;
+       }
+       
+       var->xres = var->xres_virtual = xres;
+       var->yres = var->yres_virtual = yres;
+       var->bits_per_pixel = bpp;
+
+       strcpy(fix->id, "stifb");
+       info->fbops = &stifb_ops;
+       info->screen_base = ioremap_nocache(REGION_BASE(fb,1), fix->smem_len);
+       info->screen_size = fix->smem_len;
+       info->flags = FBINFO_DEFAULT;
+       info->pseudo_palette = &fb->pseudo_palette;
+
+       /* This has to be done !!! */
+       if (fb_alloc_cmap(&info->cmap, NR_PALETTE, 0))
+               goto out_err1;
+       stifb_init_display(fb);
+
+       if (!request_mem_region(fix->smem_start, fix->smem_len, "stifb fb")) {
+               printk(KERN_ERR "stifb: cannot reserve fb region 0x%04lx-0x%04lx\n",
+                               fix->smem_start, fix->smem_start+fix->smem_len);
+               goto out_err2;
+       }
+               
+       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_err3;
+       }
+
+       if (register_framebuffer(&fb->info) < 0)
+               goto out_err4;
+
+       sti->info = info; /* save for unregister_framebuffer() */
+
+       fb_info(&fb->info, "%s %dx%d-%d frame buffer device, %s, id: %04x, mmio: 0x%04lx\n",
+               fix->id,
+               var->xres, 
+               var->yres,
+               var->bits_per_pixel,
+               dev_name,
+               fb->id, 
+               fix->mmio_start);
+
+       return 0;
+
+
+out_err4:
+       release_mem_region(fix->mmio_start, fix->mmio_len);
+out_err3:
+       release_mem_region(fix->smem_start, fix->smem_len);
+out_err2:
+       fb_dealloc_cmap(&info->cmap);
+out_err1:
+       iounmap(info->screen_base);
+out_err0:
+       kfree(fb);
+       return -ENXIO;
+}
+
+static int stifb_disabled __initdata;
+
+int __init
+stifb_setup(char *options);
+
+static int __init stifb_init(void)
+{
+       struct sti_struct *sti;
+       struct sti_struct *def_sti;
+       int i;
+       
+#ifndef MODULE
+       char *option = NULL;
+
+       if (fb_get_options("stifb", &option))
+               return -ENODEV;
+       stifb_setup(option);
+#endif
+       if (stifb_disabled) {
+               printk(KERN_INFO "stifb: disabled by \"stifb=off\" kernel parameter\n");
+               return -ENXIO;
+       }
+       
+       def_sti = sti_get_rom(0);
+       if (def_sti) {
+               for (i = 1; i <= MAX_STI_ROMS; i++) {
+                       sti = sti_get_rom(i);
+                       if (!sti)
+                               break;
+                       if (sti == def_sti) {
+                               stifb_init_fb(sti, stifb_bpp_pref[i - 1]);
+                               break;
+                       }
+               }
+       }
+
+       for (i = 1; i <= MAX_STI_ROMS; i++) {
+               sti = sti_get_rom(i);
+               if (!sti)
+                       break;
+               if (sti == def_sti)
+                       continue;
+               stifb_init_fb(sti, stifb_bpp_pref[i - 1]);
+       }
+       return 0;
+}
+
+/*
+ *  Cleanup
+ */
+
+static void __exit
+stifb_cleanup(void)
+{
+       struct sti_struct *sti;
+       int i;
+       
+       for (i = 1; i <= MAX_STI_ROMS; i++) {
+               sti = sti_get_rom(i);
+               if (!sti)
+                       break;
+               if (sti->info) {
+                       struct fb_info *info = sti->info;
+                       unregister_framebuffer(sti->info);
+                       release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
+                       release_mem_region(info->fix.smem_start, info->fix.smem_len);
+                               if (info->screen_base)
+                                       iounmap(info->screen_base);
+                       fb_dealloc_cmap(&info->cmap);
+                       framebuffer_release(info);
+               }
+               sti->info = NULL;
+       }
+}
+
+int __init
+stifb_setup(char *options)
+{
+       int i;
+       
+       if (!options || !*options)
+               return 1;
+       
+       if (strncmp(options, "off", 3) == 0) {
+               stifb_disabled = 1;
+               options += 3;
+       }
+
+       if (strncmp(options, "bpp", 3) == 0) {
+               options += 3;
+               for (i = 0; i < MAX_STI_ROMS; i++) {
+                       if (*options++ != ':')
+                               break;
+                       stifb_bpp_pref[i] = simple_strtoul(options, &options, 10);
+               }
+       }
+       return 1;
+}
+
+__setup("stifb=", stifb_setup);
+
+module_init(stifb_init);
+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 v2");