Logo Search packages:      
Sourcecode: linux version File versions  Download package

stifb.c

/*
 * 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", \
                                          __FUNCTION__, 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", \
                                          __FUNCTION__, 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)
{
      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);

      fb_memset((void*)fb->info.fix.smem_start, 0xff,
            fb->info.var.yres*fb->info.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->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 enought, but lets clear all 4 bits */
      else
            controlPlaneReg = 0x00000F00; /* 0x00000100 should be enought, 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 = (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 = (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
 */

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;
      /* 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(sti->outptr.dev_name, "DX")) {
               printk(KERN_WARNING "WARNING: stifb framebuffer driver does not "
                  "support '%s' in double-buffer mode.\n"
                  KERN_WARNING "WARNING: Please disable the double-buffer mode "
                  "in IPL menu (the PARISC-BIOS).\n",
                  sti->outptr.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",
                  sti->outptr.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) */
            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 = 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 been done !!! */
      fb_alloc_cmap(&info->cmap, NR_PALETTE, 0);
      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_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;

      sti->info = info; /* save for unregister_framebuffer() */

      printk(KERN_INFO 
          "fb%d: %s %dx%d-%d frame buffer device, %s, id: %04x, mmio: 0x%04lx\n",
            fb->info.node, 
            fix->id,
            var->xres, 
            var->yres,
            var->bits_per_pixel,
            sti->outptr.dev_name,
            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:
      iounmap(info->screen_base);
      fb_dealloc_cmap(&info->cmap);
out_err0:
      kfree(fb);
      return -ENXIO;
}

static int stifb_disabled __initdata;

int __init
stifb_setup(char *options);

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);
                    kfree(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");

Generated by  Doxygen 1.6.0   Back to index