Commit 87a8254b authored by James Simmons's avatar James Simmons Committed by Dave Kleikamp

[PATCH] New Permedia2 framebuffer driver.

This fixes the permedia2 framebuffer driver to the new API.

[ Currently it doesn't even compile.  This only touches the current
  permedia driver.  ]
parent 95996ddf
...@@ -55,7 +55,7 @@ config FB_CIRRUS ...@@ -55,7 +55,7 @@ config FB_CIRRUS
config FB_PM2 config FB_PM2
tristate "Permedia2 support" tristate "Permedia2 support"
depends on FB && (AMIGA || PCI) && BROKEN depends on FB && (AMIGA || PCI)
help help
This is the frame buffer device driver for the Permedia2 AGP frame This is the frame buffer device driver for the Permedia2 AGP frame
buffer card from ASK, aka `Graphic Blaster Exxtreme'. There is a buffer card from ASK, aka `Graphic Blaster Exxtreme'. There is a
...@@ -66,21 +66,7 @@ config FB_PM2_FIFO_DISCONNECT ...@@ -66,21 +66,7 @@ config FB_PM2_FIFO_DISCONNECT
bool "enable FIFO disconnect feature" bool "enable FIFO disconnect feature"
depends on FB_PM2 && PCI depends on FB_PM2 && PCI
help help
Support the Permedia2 FIFOI disconnect feature (see CONFIG_FB_PM2). Support the Permedia2 FIFO disconnect feature (see CONFIG_FB_PM2).
config FB_PM2_PCI
bool "generic Permedia2 PCI board support"
depends on FB_PM2 && PCI
help
Say Y to enable support for Permedia2 AGP frame buffer card from
3Dlabs (aka `Graphic Blaster Exxtreme') on the PCI bus.
config FB_PM2_CVPPC
bool "Phase5 CVisionPPC/BVisionPPC support"
depends on FB_PM2 && AMIGA
help
Say Y to enable support for the Amiga Phase 5 CVisionPPC BVisionPPC
framebuffer cards. Phase 5 is no longer with us, alas.
config FB_ACORN config FB_ACORN
bool "Acorn VIDC support" bool "Acorn VIDC support"
......
...@@ -15,7 +15,7 @@ endif ...@@ -15,7 +15,7 @@ endif
obj-$(CONFIG_FB_ACORN) += acornfb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_ACORN) += acornfb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o
obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o
obj-$(CONFIG_FB_PM2) += pm2fb.o obj-$(CONFIG_FB_PM2) += pm2fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o
obj-$(CONFIG_FB_PM3) += pm3fb.o obj-$(CONFIG_FB_PM3) += pm3fb.o
obj-$(CONFIG_FB_APOLLO) += dnfb.o cfbfillrect.o cfbimgblt.o obj-$(CONFIG_FB_APOLLO) += dnfb.o cfbfillrect.o cfbimgblt.o
obj-$(CONFIG_FB_Q40) += q40fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_Q40) += q40fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o
......
/* /*
* Permedia2 framebuffer driver. * Permedia2 framebuffer driver.
*
* 2.5/2.6 driver:
* Copyright (c) 2003 Jim Hague (jim.hague@acm.org)
*
* based on 2.4 driver:
* Copyright (c) 1998-2000 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) * Copyright (c) 1998-2000 Ilario Nardinocchi (nardinoc@CS.UniBO.IT)
* Copyright (c) 1999 Jakub Jelinek (jakub@redhat.com) * Copyright (c) 1999 Jakub Jelinek (jakub@redhat.com)
* Based on linux/drivers/video/skeletonfb.c by Geert Uytterhoeven. *
* -------------------------------------------------------------------------- * and additional input from James Simmon's port of Hannu Mallat's tdfx
* $Id: pm2fb.c,v 1.213 2000/09/19 01:03:19 illo Exp $ * driver.
* -------------------------------------------------------------------------- *
* History: * $Id$
* 1999/05/09 added Jim Hague's 'var' kernel option (thanks Jim!) *
* 2002/04/23 Jim Hague <jim.hague@acm.org> * I have a Creative Graphics Blaster Exxtreme card - pm2fb on x86.
* * Integrated Illo's last changes, No changelist available. * I have no access to other pm2fb implementations, and cannot test
* Major items: acceleration support, hardware cursor code * on them. Therefore for now I am omitting Sparc and CVision code.
* (not yet enabled). *
* * Fixed -vsync, added lowhsync/lowvsync overrides for use with * Multiple boards support has been on the TODO list for ages.
* XFree GLINT driver. * Don't expect this to change.
* -------------------------------------------------------------------------- *
* TODO multiple boards support
* --------------------------------------------------------------------------
* This file is subject to the terms and conditions of the GNU General Public * 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 * License. See the file COPYING in the main directory of this archive for
* for more details. * more details.
*
*
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -30,43 +35,24 @@ ...@@ -30,43 +35,24 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/fb.h> #include <linux/fb.h>
#include <linux/selection.h>
#include <linux/console.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <asm/system.h>
#include <asm/io.h> #include <video/permedia2.h>
#include <asm/uaccess.h> #include <video/cvisionppc.h>
#include <video/fbcon.h>
#include <video/fbcon-cfb8.h>
#include <video/fbcon-cfb16.h>
#include <video/fbcon-cfb24.h>
#include <video/fbcon-cfb32.h>
#include <video/pm2fb.h>
#include "cvisionppc.h"
#ifdef __sparc__
#include <asm/pbm.h>
#include <asm/fbio.h>
#endif
#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN) #if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
#error "The endianness of the target host has not been defined." #error "The endianness of the target host has not been defined."
#endif #endif
#if defined(__BIG_ENDIAN) && !defined(__sparc__) && (!defined(CONFIG_PPC) || defined(CONFIG_FB_PM2_CVPPC)) #if defined(__BIG_ENDIAN) && !defined(__sparc__)
#define PM2FB_BE_APERTURE #define PM2FB_BE_APERTURE
#endif #endif
/* Need to debug this some more */ #if !defined(CONFIG_PCI)
#undef PM2FB_HW_CURSOR #error "Only generic PCI cards supported."
#if defined(CONFIG_FB_PM2_PCI) && !defined(CONFIG_PCI)
#undef CONFIG_FB_PM2_PCI
#warning "support for Permedia2 PCI boards with no generic PCI support!"
#endif #endif
#undef PM2FB_MASTER_DEBUG #undef PM2FB_MASTER_DEBUG
...@@ -76,20 +62,7 @@ ...@@ -76,20 +62,7 @@
#define DPRINTK(a,b...) #define DPRINTK(a,b...)
#endif #endif
#define PICOS2KHZ(a) (1000000000UL/(a)) /* Memory barriers. */
#define KHZ2PICOS(a) (1000000000UL/(a))
/*
* The _DEFINITIVE_ memory mapping/unmapping functions.
* This is due to the fact that they're changing soooo often...
*/
#define MMAP(a,b) ioremap((unsigned long )(a), b)
#define UNMAP(a,b) iounmap(a)
/*
* The _DEFINITIVE_ memory i/o barrier functions.
* This is due to the fact that they're changing soooo often...
*/
#ifdef __mc68000__ #ifdef __mc68000__
#define DEFW() #define DEFW()
#define DEFR() #define DEFR()
...@@ -100,356 +73,105 @@ ...@@ -100,356 +73,105 @@
#define DEFRW() mb() #define DEFRW() mb()
#endif #endif
#ifndef MIN /*
#define MIN(a,b) ((a)<(b)?(a):(b)) * Driver data
#endif */
static char *mode __initdata = NULL;
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
#define VIDEO_MASK 0x00011e7f /* r/w values for VIDEO_CONTROL */
#define PM2FF_ACCEL (1L<<0)
struct pm2fb_par {
u32 pixclock; /* pixclock in KHz */
u32 width; /* width of virtual screen */
u32 height; /* height of virtual screen */
u32 hsstart; /* horiz. sync start */
u32 hsend; /* horiz. sync end */
u32 hbend; /* horiz. blank end (also gate end) */
u32 htotal; /* total width (w/ sync & blank) */
u32 vsstart; /* vert. sync start */
u32 vsend; /* vert. sync end */
u32 vbend; /* vert. blank end */
u32 vtotal; /* total height (w/ sync & blank) */
u32 stride; /* screen stride */
u32 base; /* screen base (xoffset+yoffset) */
u32 depth; /* screen depth (8, 16, 24 or 32) */
u32 video; /* video control (hsync,vsync) */
u32 flags; /* internal flags (PM2FF_xxxx) */
};
#define OPTF_OLD_MEM (1L<<0)
#define OPTF_YPAN (1L<<1)
#define OPTF_VIRTUAL (1L<<2)
#define OPTF_USER (1L<<3)
#define OPTF_USER_VAR (1L<<4)
#define OPTF_LOW_HSYNC (1L<<5)
#define OPTF_LOW_VSYNC (1L<<6)
static struct {
char font[40];
u32 flags;
struct pm2fb_par user_mode;
} pm2fb_options =
#ifdef __sparc__
/* For some reason Raptor is not happy with the low-end mode */
{"\0", 0L, {31499,640,480,4,20,50,209,0,3,20,499,80,0,8,121}};
#else
{"\0", 0L, {25174,640,480,4,28,40,199,9,11,45,524,80,0,8,121}};
#endif
static char curblink __initdata = 1;
static struct fb_var_screeninfo user_var __initdata={0,};
#define DEFAULT_USER_MODE 0
static const struct {
char name[16];
struct pm2fb_par par;
} user_mode[] __initdata = {
{"640x480-60",
{25174,640,480,4,28,40,199,9,11,45,524,80,0,8,121,PM2FF_ACCEL}},
{"640x480-72",
{31199,640,480,6,16,48,207,8,10,39,518,80,0,8,121,PM2FF_ACCEL}},
{"640x480-75",
{31499,640,480,4,20,50,209,0,3,20,499,80,0,8,121,PM2FF_ACCEL}},
{"640x480-90",
{39909,640,480,8,18,48,207,24,38,53,532,80,0,8,121,PM2FF_ACCEL}},
{"640x480-100",
{44899,640,480,8,40,52,211,21,33,51,530,80,0,8,121,PM2FF_ACCEL}},
{"800x600-56",
{35999,800,600,6,24,56,255,0,2,25,624,100,0,8,41,PM2FF_ACCEL}},
{"800x600-60",
{40000,800,600,10,42,64,263,0,4,28,627,100,0,8,41,PM2FF_ACCEL}},
{"800x600-70",
{44899,800,600,6,42,52,251,8,20,36,635,100,0,8,105,PM2FF_ACCEL}},
{"800x600-72",
{50000,800,600,14,44,60,259,36,42,66,665,100,0,8,41,PM2FF_ACCEL}},
{"800x600-75",
{49497,800,600,4,24,64,263,0,3,25,624,100,0,8,41,PM2FF_ACCEL}},
{"800x600-90",
{56637,800,600,2,18,48,247,7,18,35,634,100,0,8,41,PM2FF_ACCEL}},
{"800x600-100",
{67499,800,600,0,16,70,269,6,10,25,624,100,0,8,41,PM2FF_ACCEL}},
{"1024x768-60",
{64998,1024,768,6,40,80,335,2,8,38,805,128,0,8,121,PM2FF_ACCEL}},
{"1024x768-70",
{74996,1024,768,6,40,76,331,2,8,38,805,128,0,8,121,PM2FF_ACCEL}},
{"1024x768-72",
{74996,1024,768,6,40,66,321,2,8,38,805,128,0,8,121,PM2FF_ACCEL}},
{"1024x768-75",
{78932,1024,768,4,28,72,327,0,3,32,799,128,0,8,41,PM2FF_ACCEL}},
{"1024x768-90",
{100000,1024,768,0,24,72,327,20,35,77,844,128,0,8,121,PM2FF_ACCEL}},
{"1024x768-100",
{109998,1024,768,0,22,92,347,0,7,24,791,128,0,8,121,PM2FF_ACCEL}},
{"1024x768-illo",
{120336,1024,768,12,48,120,375,3,7,32,799,128,0,8,41,PM2FF_ACCEL}},
{"1152x864-60",
{80000,1152,864,16,44,76,363,5,10,52,915,144,0,8,41,PM2FF_ACCEL}},
{"1152x864-70",
{100000,1152,864,10,48,90,377,12,23,81,944,144,0,8,41,PM2FF_ACCEL}},
{"1152x864-75",
{109998,1152,864,6,42,78,365,44,52,138,1001,144,0,8,41,PM2FF_ACCEL}},
{"1152x864-80",
{109998,1152,864,4,32,72,359,29,36,94,957,144,0,8,41,PM2FF_ACCEL}},
{"1152x900-66-sun",
{92940,1152,900,16,80,176,751,1,5,37,936,288,0,16,121,PM2FF_ACCEL}},
{"1280x1024-60",
{107991,1280,1024,12,40,102,421,0,3,42,1065,160,0,8,41,PM2FF_ACCEL}},
{"1280x1024-70",
{125992,1280,1024,20,48,102,421,0,5,42,1065,160,0,8,41,PM2FF_ACCEL}},
{"1280x1024-74",
{134989,1280,1024,8,44,108,427,0,29,40,1063,160,0,8,41,PM2FF_ACCEL}},
{"1280x1024-75",
{134989,1280,1024,4,40,102,421,0,3,42,1065,160,0,8,41,PM2FF_ACCEL}},
{"1600x1200-60",
{155981,1600,1200,8,48,112,511,9,17,70,1269,200,0,8,121,PM2FF_ACCEL}},
{"1600x1200-66",
{171998,1600,1200,10,44,120,519,2,5,53,1252,200,0,8,121,PM2FF_ACCEL}},
{"1600x1200-76",
{197980,1600,1200,10,44,120,519,2,7,50,1249,200,0,8,121,PM2FF_ACCEL}},
{"\0", },
};
#ifdef CONFIG_FB_PM2_PCI
struct pm2pci_par {
u32 mem_config;
u32 mem_control;
u32 boot_address;
struct pci_dev* dev;
};
#endif
#define DEFAULT_CURSOR_BLINK_RATE (20)
#define CURSOR_DRAW_DELAY (2)
struct pm2_cursor {
int enable;
int on;
int vbl_cnt;
int blink_rate;
struct {
u16 x, y;
} pos, hot, size;
u8 color[6];
u8 bits[8][64];
u8 mask[8][64];
struct timer_list *timer;
};
static const char permedia2_name[16]="Permedia2"; /*
* The XFree GLINT driver will (I think to implement hardware cursor
* support on TVP4010 and similar where there is no RAMDAC - see
* comment in set_video) always request +ve sync regardless of what
* the mode requires. This screws me because I have a Sun
* fixed-frequency monitor which absolutely has to have -ve sync. So
* these flags allow the user to specify that requests for +ve sync
* should be silently turned in -ve sync.
*/
static int lowhsync __initdata = 0;
static int lowvsync __initdata = 0;
static struct pm2fb_info { /*
struct fb_info_gen gen; * The hardware state of the graphics card that isn't part of the
int board; /* Permedia2 board index (see * screeninfo.
board_table[] below) */ */
pm2type_t type; /* Permedia2 board type */ struct pm2fb_par
struct { {
unsigned long fb_base; /* physical framebuffer memory base */ pm2type_t type; /* Board type */
u32 fb_size; /* framebuffer memory size */ u32 fb_size; /* framebuffer memory size */
unsigned long rg_base; /* physical register memory base */
unsigned long p_fb; /* physical address of frame buffer */
unsigned char* v_fb; /* virtual address of frame buffer */ unsigned char* v_fb; /* virtual address of frame buffer */
unsigned long p_regs; /* physical address of registers
region, must be rg_base or
rg_base+PM2_REGS_SIZE depending on
the host endianness */
unsigned char* v_regs; /* virtual address of p_regs */ unsigned char* v_regs; /* virtual address of p_regs */
} regions; u32 memclock; /* memclock */
union { /* here, the per-board par structs */ u32 video; /* video flags before blanking */
#ifdef CONFIG_FB_PM2_CVPPC };
struct cvppc_par cvppc; /* CVisionPPC data */
#endif
#ifdef CONFIG_FB_PM2_PCI
struct pm2pci_par pci; /* Permedia2 PCI boards data */
#endif
} board_par;
struct pm2fb_par current_par; /* displayed screen */
int current_par_valid;
int is_blank;
u32 memclock; /* memclock (set by the per-board
init routine) */
struct display disp;
struct {
u8 transp;
u8 red;
u8 green;
u8 blue;
} palette[256];
union {
#ifdef FBCON_HAS_CFB16
u16 cmap16[16];
#endif
#ifdef FBCON_HAS_CFB24
u32 cmap24[16];
#endif
#ifdef FBCON_HAS_CFB32
u32 cmap32[16];
#endif
} cmap;
struct pm2_cursor *cursor;
} fb_info;
#ifdef CONFIG_FB_PM2_CVPPC
static int cvppc_detect(struct pm2fb_info*);
static void cvppc_init(struct pm2fb_info*);
#endif
#ifdef CONFIG_FB_PM2_PCI
static int pm2pci_detect(struct pm2fb_info*);
static void pm2pci_init(struct pm2fb_info*);
#endif
#ifdef PM2FB_HW_CURSOR
static void pm2fb_cursor(struct display *p, int mode, int x, int y);
static int pm2fb_set_font(struct display *d, int width, int height);
static struct pm2_cursor *pm2_init_cursor(struct pm2fb_info *fb);
static void pm2v_set_cursor_color(struct pm2fb_info *fb, u8 *red, u8 *green, u8 *blue);
static void pm2v_set_cursor_shape(struct pm2fb_info *fb);
static u8 cursor_color_map[2] = { 0, 0xff };
#else
#define pm2fb_cursor NULL
#define pm2fb_set_font NULL
#endif
/* /*
* Table of the supported Permedia2 based boards. * Here we define the default structs fb_fix_screeninfo and fb_var_screeninfo
* Three hooks are defined for each board: * if we don't use modedb.
* detect(): should return 1 if the related board has been detected, 0
* otherwise. It should also fill the fields 'regions.fb_base',
* 'regions.fb_size', 'regions.rg_base' and 'memclock' in the
* passed pm2fb_info structure.
* init(): called immediately after the reset of the Permedia2 chip.
* It should reset the memory controller if needed (the MClk
* is set shortly afterwards by the caller).
* cleanup(): called after the driver has been unregistered.
*
* the init and cleanup pointers can be NULL.
*/ */
static const struct { static struct fb_fix_screeninfo pm2fb_fix __initdata = {
int (*detect)(struct pm2fb_info*); .id = "",
void (*init)(struct pm2fb_info*); .type = FB_TYPE_PACKED_PIXELS,
void (*cleanup)(struct pm2fb_info*); .visual = FB_VISUAL_PSEUDOCOLOR,
char name[32]; .xpanstep = 1,
} board_table[] = { .ypanstep = 1,
#ifdef CONFIG_FB_PM2_PCI .ywrapstep = 0,
{ pm2pci_detect, pm2pci_init, NULL, "Permedia2 PCI board" }, .accel = FB_ACCEL_NONE,
#endif
#ifdef CONFIG_FB_PM2_CVPPC
{ cvppc_detect, cvppc_init, NULL, "CVisionPPC/BVisionPPC" },
#endif
{ NULL, }
}; };
/* /*
* partial products for the supported horizontal resolutions. * Default video mode. In case the modedb doesn't work, or we're
* a module (in which case modedb doesn't really work).
*/ */
#define PACKPP(p0,p1,p2) (((p2)<<6)|((p1)<<3)|(p0)) static struct fb_var_screeninfo pm2fb_var __initdata = {
static const struct { /* "640x480, 8 bpp @ 60 Hz */
u16 width; .xres = 640,
u16 pp; .yres = 480,
} pp_table[] = { .xres_virtual = 640,
{ 32, PACKPP(1, 0, 0) }, { 64, PACKPP(1, 1, 0) }, .yres_virtual = 480,
{ 96, PACKPP(1, 1, 1) }, { 128, PACKPP(2, 1, 1) }, .bits_per_pixel =8,
{ 160, PACKPP(2, 2, 1) }, { 192, PACKPP(2, 2, 2) }, .red = {0, 8, 0},
{ 224, PACKPP(3, 2, 1) }, { 256, PACKPP(3, 2, 2) }, .blue = {0, 8, 0},
{ 288, PACKPP(3, 3, 1) }, { 320, PACKPP(3, 3, 2) }, .green = {0, 8, 0},
{ 384, PACKPP(3, 3, 3) }, { 416, PACKPP(4, 3, 1) }, .activate = FB_ACTIVATE_NOW,
{ 448, PACKPP(4, 3, 2) }, { 512, PACKPP(4, 3, 3) }, .height = -1,
{ 544, PACKPP(4, 4, 1) }, { 576, PACKPP(4, 4, 2) }, .width = -1,
{ 640, PACKPP(4, 4, 3) }, { 768, PACKPP(4, 4, 4) }, .accel_flags = 0,
{ 800, PACKPP(5, 4, 1) }, { 832, PACKPP(5, 4, 2) }, .pixclock = 39721,
{ 896, PACKPP(5, 4, 3) }, { 1024, PACKPP(5, 4, 4) }, .left_margin = 40,
{ 1056, PACKPP(5, 5, 1) }, { 1088, PACKPP(5, 5, 2) }, .right_margin = 24,
{ 1152, PACKPP(5, 5, 3) }, { 1280, PACKPP(5, 5, 4) }, .upper_margin = 32,
{ 1536, PACKPP(5, 5, 5) }, { 1568, PACKPP(6, 5, 1) }, .lower_margin = 11,
{ 1600, PACKPP(6, 5, 2) }, { 1664, PACKPP(6, 5, 3) }, .hsync_len = 96,
{ 1792, PACKPP(6, 5, 4) }, { 2048, PACKPP(6, 5, 5) }, .vsync_len = 2,
{ 0, 0 } }; .vmode = FB_VMODE_NONINTERLACED
static void pm2fb_detect(void);
static int pm2fb_encode_fix(struct fb_fix_screeninfo* fix,
const void* par, struct fb_info_gen* info);
static int pm2fb_decode_var(const struct fb_var_screeninfo* var,
void* par, struct fb_info_gen* info);
static int pm2fb_encode_var(struct fb_var_screeninfo* var,
const void* par, struct fb_info_gen* info);
static void pm2fb_get_par(void* par, struct fb_info_gen* info);
static void pm2fb_set_par(const void* par, struct fb_info_gen* info);
static int pm2fb_getcolreg(unsigned regno,
unsigned* red, unsigned* green, unsigned* blue,
unsigned* transp, struct fb_info* info);
static int pm2fb_blank(int blank_mode, struct fb_info_gen* info);
static int pm2fb_pan_display(const struct fb_var_screeninfo* var,
struct fb_info_gen* info);
static void pm2fb_set_disp(const void* par, struct display* disp,
struct fb_info_gen* info);
static struct fbgen_hwswitch pm2fb_hwswitch={
pm2fb_detect, pm2fb_encode_fix, pm2fb_decode_var,
pm2fb_encode_var, pm2fb_get_par, pm2fb_set_par,
pm2fb_getcolreg, pm2fb_pan_display,
pm2fb_blank, pm2fb_set_disp
};
static int pm2fb_setcolreg(unsigned regno,
unsigned red, unsigned green, unsigned blue,
unsigned transp, struct fb_info* info);
static struct fb_ops pm2fb_ops={
.owner = THIS_MODULE,
.fb_get_fix = fbgen_get_fix,
.fb_get_var = fbgen_get_var,
.fb_set_var = fbgen_set_var,
.fb_get_cmap = fbgen_get_cmap,
.fb_set_cmap = fbgen_set_cmap,
.fb_pan_display =fbgen_pan_display,
.fb_setcolreg = pm2fb_setcolreg,
.fb_blank = fbgen_blank,
}; };
/*************************************************************************** /*
* Begin of Permedia2 specific functions * Utility functions
***************************************************************************/ */
inline static u32 RD32(unsigned char* base, s32 off) {
return fb_readl(base+off); inline static u32 RD32(unsigned char* base, s32 off)
{
return fb_readl(base + off);
} }
inline static void WR32(unsigned char* base, s32 off, u32 v) { inline static void WR32(unsigned char* base, s32 off, u32 v)
{
fb_writel(v, base+off); fb_writel(v, base + off);
} }
inline static u32 pm2_RD(struct pm2fb_info* p, s32 off) { inline static u32 pm2_RD(struct pm2fb_par* p, s32 off)
{
return RD32(p->regions.v_regs, off); return RD32(p->v_regs, off);
} }
inline static void pm2_WR(struct pm2fb_info* p, s32 off, u32 v) { inline static void pm2_WR(struct pm2fb_par* p, s32 off, u32 v)
{
WR32(p->regions.v_regs, off, v); WR32(p->v_regs, off, v);
} }
inline static u32 pm2_RDAC_RD(struct pm2fb_info* p, s32 idx) { inline static u32 pm2_RDAC_RD(struct pm2fb_par* p, s32 idx)
{
int index = PM2R_RD_INDEXED_DATA; int index = PM2R_RD_INDEXED_DATA;
switch (p->type) { switch (p->type) {
case PM2_TYPE_PERMEDIA2: case PM2_TYPE_PERMEDIA2:
...@@ -464,9 +186,8 @@ inline static u32 pm2_RDAC_RD(struct pm2fb_info* p, s32 idx) { ...@@ -464,9 +186,8 @@ inline static u32 pm2_RDAC_RD(struct pm2fb_info* p, s32 idx) {
return pm2_RD(p, index); return pm2_RD(p, index);
} }
inline static void pm2_RDAC_WR(struct pm2fb_info* p, s32 idx, inline static void pm2_RDAC_WR(struct pm2fb_par* p, s32 idx, u32 v)
u32 v) { {
int index = PM2R_RD_INDEXED_DATA; int index = PM2R_RD_INDEXED_DATA;
switch (p->type) { switch (p->type) {
case PM2_TYPE_PERMEDIA2: case PM2_TYPE_PERMEDIA2:
...@@ -481,16 +202,15 @@ inline static void pm2_RDAC_WR(struct pm2fb_info* p, s32 idx, ...@@ -481,16 +202,15 @@ inline static void pm2_RDAC_WR(struct pm2fb_info* p, s32 idx,
pm2_WR(p, index, v); pm2_WR(p, index, v);
} }
inline static u32 pm2v_RDAC_RD(struct pm2fb_info* p, s32 idx) { inline static u32 pm2v_RDAC_RD(struct pm2fb_par* p, s32 idx)
{
pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff);
DEFRW(); DEFRW();
return pm2_RD(p, PM2VR_RD_INDEXED_DATA); return pm2_RD(p, PM2VR_RD_INDEXED_DATA);
} }
inline static void pm2v_RDAC_WR(struct pm2fb_info* p, s32 idx, inline static void pm2v_RDAC_WR(struct pm2fb_par* p, s32 idx, u32 v)
u32 v) { {
pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff);
DEFRW(); DEFRW();
pm2_WR(p, PM2VR_RD_INDEXED_DATA, v); pm2_WR(p, PM2VR_RD_INDEXED_DATA, v);
...@@ -499,79 +219,88 @@ inline static void pm2v_RDAC_WR(struct pm2fb_info* p, s32 idx, ...@@ -499,79 +219,88 @@ inline static void pm2v_RDAC_WR(struct pm2fb_info* p, s32 idx,
#ifdef CONFIG_FB_PM2_FIFO_DISCONNECT #ifdef CONFIG_FB_PM2_FIFO_DISCONNECT
#define WAIT_FIFO(p,a) #define WAIT_FIFO(p,a)
#else #else
inline static void WAIT_FIFO(struct pm2fb_info* p, u32 a) { inline static void WAIT_FIFO(struct pm2fb_par* p, u32 a)
{
while(pm2_RD(p, PM2R_IN_FIFO_SPACE)<a); while( pm2_RD(p, PM2R_IN_FIFO_SPACE) < a );
DEFRW(); DEFRW();
} }
#endif #endif
static u32 partprod(u32 xres) { /*
* partial products for the supported horizontal resolutions.
*/
#define PACKPP(p0,p1,p2) (((p2) << 6) | ((p1) << 3) | (p0))
static const struct {
u16 width;
u16 pp;
} pp_table[] = {
{ 32, PACKPP(1, 0, 0) }, { 64, PACKPP(1, 1, 0) },
{ 96, PACKPP(1, 1, 1) }, { 128, PACKPP(2, 1, 1) },
{ 160, PACKPP(2, 2, 1) }, { 192, PACKPP(2, 2, 2) },
{ 224, PACKPP(3, 2, 1) }, { 256, PACKPP(3, 2, 2) },
{ 288, PACKPP(3, 3, 1) }, { 320, PACKPP(3, 3, 2) },
{ 384, PACKPP(3, 3, 3) }, { 416, PACKPP(4, 3, 1) },
{ 448, PACKPP(4, 3, 2) }, { 512, PACKPP(4, 3, 3) },
{ 544, PACKPP(4, 4, 1) }, { 576, PACKPP(4, 4, 2) },
{ 640, PACKPP(4, 4, 3) }, { 768, PACKPP(4, 4, 4) },
{ 800, PACKPP(5, 4, 1) }, { 832, PACKPP(5, 4, 2) },
{ 896, PACKPP(5, 4, 3) }, { 1024, PACKPP(5, 4, 4) },
{ 1056, PACKPP(5, 5, 1) }, { 1088, PACKPP(5, 5, 2) },
{ 1152, PACKPP(5, 5, 3) }, { 1280, PACKPP(5, 5, 4) },
{ 1536, PACKPP(5, 5, 5) }, { 1568, PACKPP(6, 5, 1) },
{ 1600, PACKPP(6, 5, 2) }, { 1664, PACKPP(6, 5, 3) },
{ 1792, PACKPP(6, 5, 4) }, { 2048, PACKPP(6, 5, 5) },
{ 0, 0 } };
static u32 partprod(u32 xres)
{
int i; int i;
for (i=0; pp_table[i].width && pp_table[i].width!=xres; i++); for (i = 0; pp_table[i].width && pp_table[i].width != xres; i++)
if (!pp_table[i].width) ;
if ( pp_table[i].width == 0 )
DPRINTK("invalid width %u\n", xres); DPRINTK("invalid width %u\n", xres);
return pp_table[i].pp; return pp_table[i].pp;
} }
static u32 to3264(u32 timing, int bpp, int is64) { static u32 to3264(u32 timing, int bpp, int is64)
{
switch (bpp) {
case 8:
timing=timing>>(2+is64);
break;
case 16:
timing=timing>>(1+is64);
break;
case 24:
timing=(timing*3)>>(2+is64);
break;
case 32:
if (is64)
timing=timing>>1;
break;
}
return timing;
}
static u32 from3264(u32 timing, int bpp, int is64) {
switch (bpp) { switch (bpp) {
case 8: case 8:
timing=timing<<(2+is64); timing >>= 2 + is64;
break; break;
case 16: case 16:
timing=timing<<(1+is64); timing >>= 1 + is64;
break; break;
case 24: case 24:
timing=(timing<<(2+is64))/3; timing = (timing * 3) >> (2 + is64);
break; break;
case 32: case 32:
if (is64) if (is64)
timing=timing<<1; timing >>= 1;
break; break;
} }
return timing; return timing;
} }
static void pm2_mnp(u32 clk, unsigned char* mm, unsigned char* nn, static void pm2_mnp(u32 clk, unsigned char* mm, unsigned char* nn,
unsigned char* pp) { unsigned char* pp)
{
unsigned char m; unsigned char m;
unsigned char n; unsigned char n;
unsigned char p; unsigned char p;
u32 f; u32 f;
s32 curr; s32 curr;
s32 delta=100000; s32 delta = 100000;
*mm=*nn=*pp=0; *mm = *nn = *pp = 0;
for (n=2; n<15; n++) { for (n = 2; n < 15; n++) {
for (m=2; m; m++) { for (m = 2; m; m++) {
f=PM2_REFERENCE_CLOCK*m/n; f = PM2_REFERENCE_CLOCK * m / n;
if (f>=150000 && f<=300000) { if (f >= 150000 && f <= 300000) {
for (p=0; p<5; p++, f>>=1) { for ( p = 0; p < 5; p++, f >>= 1) {
curr=clk>f?clk-f:f-clk; curr = ( clk > f ) ? clk - f : f - clk;
if (curr<delta) { if ( curr < delta ) {
delta=curr; delta=curr;
*mm=m; *mm=m;
*nn=n; *nn=n;
...@@ -584,20 +313,21 @@ static void pm2_mnp(u32 clk, unsigned char* mm, unsigned char* nn, ...@@ -584,20 +313,21 @@ static void pm2_mnp(u32 clk, unsigned char* mm, unsigned char* nn,
} }
static void pm2v_mnp(u32 clk, unsigned char* mm, unsigned char* nn, static void pm2v_mnp(u32 clk, unsigned char* mm, unsigned char* nn,
unsigned char* pp) { unsigned char* pp)
{
unsigned char m; unsigned char m;
unsigned char n; unsigned char n;
unsigned char p; unsigned char p;
u32 f; u32 f;
s32 delta=1000; s32 delta = 1000;
*mm=*nn=*pp=0; *mm = *nn = *pp = 0;
for (n=1; n; n++) { for (n = 1; n; n++) {
for (m=1; m; m++) { for ( m = 1; m; m++) {
for (p=0; p<2; p++) { for ( p = 0; p < 2; p++) {
f=PM2_REFERENCE_CLOCK*n/(m * (1<<(p+1))); f = PM2_REFERENCE_CLOCK * n / (m * (1 << (p + 1)));
if (clk>f-delta && clk<f+delta) { if ( clk > f - delta && clk < f + delta ) {
delta=clk>f?clk-f:f-clk; delta = ( clk > f ) ? clk - f : f - clk;
*mm=m; *mm=m;
*nn=n; *nn=n;
*pp=p; *pp=p;
...@@ -607,72 +337,7 @@ static void pm2v_mnp(u32 clk, unsigned char* mm, unsigned char* nn, ...@@ -607,72 +337,7 @@ static void pm2v_mnp(u32 clk, unsigned char* mm, unsigned char* nn,
} }
} }
static void wait_pm2(struct pm2fb_info* i) { static void clear_palette(struct pm2fb_par* p) {
WAIT_FIFO(i, 1);
pm2_WR(i, PM2R_SYNC, 0);
DEFRW();
do {
while (pm2_RD(i, PM2R_OUT_FIFO_WORDS)==0);
DEFR();
} while (pm2_RD(i, PM2R_OUT_FIFO)!=PM2TAG(PM2R_SYNC));
}
static void pm2_set_memclock(struct pm2fb_info* info, u32 clk) {
int i;
unsigned char m, n, p;
pm2_mnp(clk, &m, &n, &p);
WAIT_FIFO(info, 10);
pm2_RDAC_WR(info, PM2I_RD_MEMORY_CLOCK_3, 6);
DEFW();
pm2_RDAC_WR(info, PM2I_RD_MEMORY_CLOCK_1, m);
pm2_RDAC_WR(info, PM2I_RD_MEMORY_CLOCK_2, n);
DEFW();
pm2_RDAC_WR(info, PM2I_RD_MEMORY_CLOCK_3, 8|p);
DEFW();
pm2_RDAC_RD(info, PM2I_RD_MEMORY_CLOCK_STATUS);
DEFR();
for (i=256; i &&
!(pm2_RD(info, PM2R_RD_INDEXED_DATA)&PM2F_PLL_LOCKED); i--);
}
static void pm2_set_pixclock(struct pm2fb_info* info, u32 clk) {
int i;
unsigned char m, n, p;
switch (info->type) {
case PM2_TYPE_PERMEDIA2:
pm2_mnp(clk, &m, &n, &p);
WAIT_FIFO(info, 8);
pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A3, 0);
DEFW();
pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A1, m);
pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A2, n);
DEFW();
pm2_RDAC_WR(info, PM2I_RD_PIXEL_CLOCK_A3, 8|p);
DEFW();
pm2_RDAC_RD(info, PM2I_RD_PIXEL_CLOCK_STATUS);
DEFR();
for (i=256;
i && !(pm2_RD(info, PM2R_RD_INDEXED_DATA)&PM2F_PLL_LOCKED);
i--)
;
break;
case PM2_TYPE_PERMEDIA2V:
pm2v_mnp(clk/2, &m, &n, &p);
WAIT_FIFO(info, 8);
pm2_WR(info, PM2VR_RD_INDEX_HIGH,
PM2VI_RD_CLK0_PRESCALE >> 8);
pm2v_RDAC_WR(info, PM2VI_RD_CLK0_PRESCALE, m);
pm2v_RDAC_WR(info, PM2VI_RD_CLK0_FEEDBACK, n);
pm2v_RDAC_WR(info, PM2VI_RD_CLK0_POSTSCALE, p);
pm2_WR(info, PM2VR_RD_INDEX_HIGH, 0);
break;
}
}
static void clear_palette(struct pm2fb_info* p) {
int i=256; int i=256;
WAIT_FIFO(p, 1); WAIT_FIFO(p, 1);
...@@ -686,297 +351,35 @@ static void clear_palette(struct pm2fb_info* p) { ...@@ -686,297 +351,35 @@ static void clear_palette(struct pm2fb_info* p) {
} }
} }
static void set_color(struct pm2fb_info* p, unsigned char regno, #if 0
unsigned char r, unsigned char g, unsigned char b) { /*
* FIXME:
WAIT_FIFO(p, 4); * The 2.4 driver calls this at init time, where it also sets the
pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, regno); * initial mode. I don't think the driver should touch the chip
DEFW(); * until the console sets a video mode. So I was calling this
pm2_WR(p, PM2R_RD_PALETTE_DATA, r); * at the start of setting a mode. However, certainly on 1280x1024
DEFW(); * depth 16 this causes the display to smear slightly.
pm2_WR(p, PM2R_RD_PALETTE_DATA, g); * I don't know why. Guesses to jim.hague@acm.org.
DEFW();
pm2_WR(p, PM2R_RD_PALETTE_DATA, b);
}
static void set_aperture(struct pm2fb_info* i, struct pm2fb_par* p) {
WAIT_FIFO(i, 4);
#ifdef __LITTLE_ENDIAN
pm2_WR(i, PM2R_APERTURE_ONE, 0);
pm2_WR(i, PM2R_APERTURE_TWO, 0);
#else
switch (p->depth) {
case 8:
case 24:
pm2_WR(i, PM2R_APERTURE_ONE, 0);
pm2_WR(i, PM2R_APERTURE_TWO, 1);
break;
case 16:
pm2_WR(i, PM2R_APERTURE_ONE, 2);
pm2_WR(i, PM2R_APERTURE_TWO, 1);
break;
case 32:
pm2_WR(i, PM2R_APERTURE_ONE, 1);
pm2_WR(i, PM2R_APERTURE_TWO, 1);
break;
}
#endif
}
static void set_video(struct pm2fb_info* i, u32 video) {
u32 tmp;
u32 vsync;
vsync=video;
/*
* The hardware cursor needs +vsync to recognise vert retrace.
* We may not be using the hardware cursor, but the X Glint
* driver may well. So always set +hsync/+vsync and then set
* the RAMDAC to invert the sync if necessary.
*/ */
vsync&=~(PM2F_HSYNC_MASK|PM2F_VSYNC_MASK); static void reset_card(struct pm2fb_par* p)
vsync|=PM2F_HSYNC_ACT_HIGH|PM2F_VSYNC_ACT_HIGH; {
if (p->type == PM2_TYPE_PERMEDIA2V)
WAIT_FIFO(i, 5); pm2_WR(p, PM2VR_RD_INDEX_HIGH, 0);
pm2_WR(i, PM2R_VIDEO_CONTROL, vsync); pm2_WR(p, PM2R_RESET_STATUS, 0);
DEFRW();
switch (i->type) { while (pm2_RD(p, PM2R_RESET_STATUS) & PM2F_BEING_RESET)
case PM2_TYPE_PERMEDIA2: ;
tmp = PM2F_RD_PALETTE_WIDTH_8; DEFRW();
if ((video & PM2F_HSYNC_MASK) == PM2F_HSYNC_ACT_LOW) #ifdef CONFIG_FB_PM2_FIFO_DISCONNECT
tmp |= 4; /* invert hsync */ DPRINTK("FIFO disconnect enabled\n");
if ((video & PM2F_VSYNC_MASK) == PM2F_VSYNC_ACT_LOW) pm2_WR(p, PM2R_FIFO_DISCON, 1);
tmp |= 8; /* invert vsync */ DEFRW();
pm2_RDAC_WR(i, PM2I_RD_MISC_CONTROL, tmp); #endif
break;
case PM2_TYPE_PERMEDIA2V:
tmp = 0;
if ((video & PM2F_HSYNC_MASK) == PM2F_HSYNC_ACT_LOW)
tmp |= 1; /* invert hsync */
if ((video & PM2F_VSYNC_MASK) == PM2F_VSYNC_ACT_LOW)
tmp |= 4; /* invert vsync */
pm2v_RDAC_WR(i, PM2VI_RD_SYNC_CONTROL, tmp);
pm2v_RDAC_WR(i, PM2VI_RD_MISC_CONTROL, 1);
break;
}
} }
#endif
static void get_screen(struct pm2fb_info* i, struct pm2fb_par* p) { static void reset_config(struct pm2fb_par* p)
u32 clrmode; {
u32 readpx;
u32 misc;
memset(p, 0, sizeof(struct pm2fb_par));
p->base=pm2_RD(i, PM2R_SCREEN_BASE);
p->video=pm2_RD(i, PM2R_VIDEO_CONTROL) & VIDEO_MASK;
switch (i->type) {
case PM2_TYPE_PERMEDIA2:
misc=pm2_RDAC_RD(i, PM2I_RD_MISC_CONTROL);
if ( misc & 4 )
/* Hsync is actually low */
p->video |= PM2F_HSYNC_ACT_LOW;
if ( misc & 8 )
/* Vsync is actually low */
p->video |= PM2F_VSYNC_ACT_LOW;
break;
case PM2_TYPE_PERMEDIA2V:
misc=pm2_RDAC_RD(i, PM2VI_RD_SYNC_CONTROL);
if ( misc & 1 )
/* Hsync is actually low */
p->video |= PM2F_HSYNC_ACT_LOW;
if ( misc & 4 )
/* Vsync is actually low */
p->video |= PM2F_VSYNC_ACT_LOW;
break;
}
p->width=pm2_RD(i, PM2R_SCREEN_SIZE) & 0xffff;
p->height=pm2_RD(i, PM2R_SCREEN_SIZE) >> 16;
p->htotal=pm2_RD(i, PM2R_H_TOTAL);
p->hsstart=pm2_RD(i, PM2R_HS_START);
p->hsend=pm2_RD(i, PM2R_HS_END);
p->hbend=pm2_RD(i, PM2R_HB_END);
p->vtotal=pm2_RD(i, PM2R_V_TOTAL);
p->vsstart=pm2_RD(i, PM2R_VS_START);
p->vsend=pm2_RD(i, PM2R_VS_END);
p->vbend=pm2_RD(i, PM2R_VB_END);
p->stride=pm2_RD(i, PM2R_SCREEN_STRIDE);
clrmode=pm2_RDAC_RD(i, PM2I_RD_COLOR_MODE);
readpx=pm2_RD(i, PM2R_FB_READ_PIXEL);
if (clrmode & PM2F_RD_GUI_ACTIVE) {
clrmode &= ~(PM2F_RD_COLOR_MODE_RGB|PM2F_RD_GUI_ACTIVE);
if (clrmode==0 && readpx==0)
p->depth=8;
else if (clrmode==(PM2F_RD_TRUECOLOR|0x06) && readpx==1)
p->depth=16;
else if (clrmode==(PM2F_RD_TRUECOLOR|0x08) && readpx==2)
p->depth=32;
else if (clrmode==(PM2F_RD_TRUECOLOR|0x09) && readpx==4)
p->depth=24;
}
/*
* Somehow I have to manage this unretrievable fields.
* To say the truth, 'flags' field ought to be somewhere else.
*/
if (i->current_par_valid) {
p->pixclock=i->current_par.pixclock;
p->flags=i->current_par.flags;
}
}
static void set_screen(struct pm2fb_info* i, struct pm2fb_par* p) {
u32 clrmode=PM2F_RD_COLOR_MODE_RGB;
u32 txtmap=0;
u32 pixsize=0;
u32 clrformat=0;
u32 xres;
if (i->type == PM2_TYPE_PERMEDIA2V) {
WAIT_FIFO(i, 1);
pm2_WR(i, PM2VR_RD_INDEX_HIGH, 0);
}
xres=(p->width+31)&~31;
set_aperture(i, p);
DEFRW();
WAIT_FIFO(i, 19);
pm2_RDAC_WR(i, PM2I_RD_COLOR_KEY_CONTROL, p->depth==8?0:
PM2F_COLOR_KEY_TEST_OFF);
switch (p->depth) {
case 8:
pm2_WR(i, PM2R_FB_READ_PIXEL, 0);
clrformat=0x0e;
break;
case 16:
pm2_WR(i, PM2R_FB_READ_PIXEL, 1);
clrmode|=PM2F_RD_TRUECOLOR|0x06;
txtmap=PM2F_TEXTEL_SIZE_16;
pixsize=1;
clrformat=0x70;
break;
case 32:
pm2_WR(i, PM2R_FB_READ_PIXEL, 2);
clrmode|=PM2F_RD_TRUECOLOR|0x08;
txtmap=PM2F_TEXTEL_SIZE_32;
pixsize=2;
clrformat=0x20;
break;
case 24:
pm2_WR(i, PM2R_FB_READ_PIXEL, 4);
clrmode|=PM2F_RD_TRUECOLOR|0x09;
#ifndef PM2FB_BE_APERTURE
clrmode&=~PM2F_RD_COLOR_MODE_RGB;
#endif
txtmap=PM2F_TEXTEL_SIZE_24;
pixsize=4;
clrformat=0x20;
break;
}
pm2_WR(i, PM2R_FB_WRITE_MODE, PM2F_FB_WRITE_ENABLE);
pm2_WR(i, PM2R_FB_READ_MODE, partprod(xres));
pm2_WR(i, PM2R_LB_READ_MODE, partprod(xres));
pm2_WR(i, PM2R_TEXTURE_MAP_FORMAT, txtmap|partprod(xres));
pm2_WR(i, PM2R_H_TOTAL, p->htotal);
pm2_WR(i, PM2R_HS_START, p->hsstart);
pm2_WR(i, PM2R_HS_END, p->hsend);
pm2_WR(i, PM2R_HG_END, p->hbend);
pm2_WR(i, PM2R_HB_END, p->hbend);
pm2_WR(i, PM2R_V_TOTAL, p->vtotal);
pm2_WR(i, PM2R_VS_START, p->vsstart);
pm2_WR(i, PM2R_VS_END, p->vsend);
pm2_WR(i, PM2R_VB_END, p->vbend);
pm2_WR(i, PM2R_SCREEN_STRIDE, p->stride);
DEFW();
pm2_WR(i, PM2R_WINDOW_ORIGIN, 0);
pm2_WR(i, PM2R_SCREEN_SIZE, (p->height<<16)|p->width);
pm2_WR(i, PM2R_SCISSOR_MODE, PM2F_SCREEN_SCISSOR_ENABLE);
DEFW();
pm2_WR(i, PM2R_SCREEN_BASE, p->base);
DEFW();
set_video(i, p->video);
WAIT_FIFO(i, 4);
switch (i->type) {
case PM2_TYPE_PERMEDIA2:
pm2_RDAC_WR(i, PM2I_RD_COLOR_MODE, PM2F_RD_COLOR_MODE_RGB|
PM2F_RD_GUI_ACTIVE|clrmode);
break;
case PM2_TYPE_PERMEDIA2V:
pm2v_RDAC_WR(i, PM2VI_RD_PIXEL_SIZE, pixsize);
pm2v_RDAC_WR(i, PM2VI_RD_COLOR_FORMAT, clrformat);
break;
}
pm2_set_pixclock(i, p->pixclock);
}
static int screen_is_valid(struct pm2fb_info* i) {
struct pm2fb_par actual;
get_screen(i, &actual);
return i->current_par_valid &&
!memcmp(&actual, &i->current_par, sizeof(struct pm2fb_par));
}
/*
* copy with packed pixels (8/16bpp only).
*/
static void pm2fb_pp_copy(struct pm2fb_info* i, s32 xsrc, s32 ysrc,
s32 x, s32 y, s32 w, s32 h) {
s32 scale=i->current_par.depth==8?2:1;
s32 offset;
if (!w || !h)
return;
WAIT_FIFO(i, 6);
pm2_WR(i, PM2R_CONFIG, PM2F_CONFIG_FB_WRITE_ENABLE|
PM2F_CONFIG_FB_PACKED_DATA|
PM2F_CONFIG_FB_READ_SOURCE_ENABLE);
pm2_WR(i, PM2R_FB_SOURCE_DELTA, ((ysrc-y)&0xfff)<<16|
((xsrc-x)&0xfff));
offset=(x&0x3)-(xsrc&0x3);
pm2_WR(i, PM2R_RECTANGLE_ORIGIN, (y<<16)|(x>>scale));
pm2_WR(i, PM2R_RECTANGLE_SIZE, (h<<16)|((w+7)>>scale));
pm2_WR(i, PM2R_PACKED_DATA_LIMITS, (offset<<29)|(x<<16)|(x+w));
DEFW();
pm2_WR(i, PM2R_RENDER, PM2F_RENDER_RECTANGLE|
(x<xsrc?PM2F_INCREASE_X:0)|
(y<ysrc?PM2F_INCREASE_Y:0));
wait_pm2(i);
}
/*
* block operation. copy=0: rectangle fill, copy=1: rectangle copy.
*/
static void pm2fb_block_op(struct pm2fb_info* i, int copy,
s32 xsrc, s32 ysrc,
s32 x, s32 y, s32 w, s32 h,
u32 color) {
if (!w || !h)
return;
WAIT_FIFO(i, 5);
pm2_WR(i, PM2R_CONFIG, PM2F_CONFIG_FB_WRITE_ENABLE|
(copy?PM2F_CONFIG_FB_READ_SOURCE_ENABLE:0));
if (copy)
pm2_WR(i, PM2R_FB_SOURCE_DELTA, ((ysrc-y)&0xfff)<<16|
((xsrc-x)&0xfff));
else
pm2_WR(i, PM2R_FB_BLOCK_COLOR, color);
pm2_WR(i, PM2R_RECTANGLE_ORIGIN, (y<<16)|x);
pm2_WR(i, PM2R_RECTANGLE_SIZE, (h<<16)|w);
DEFW();
pm2_WR(i, PM2R_RENDER, PM2F_RENDER_RECTANGLE|
(x<xsrc?PM2F_INCREASE_X:0)|
(y<ysrc?PM2F_INCREASE_Y:0)|
(copy?0:PM2F_RENDER_FASTFILL));
wait_pm2(i);
}
/***************************************************************************
* Begin of generic initialization functions
***************************************************************************/
static void reset_units(struct pm2fb_info* p) {
WAIT_FIFO(p, 52); WAIT_FIFO(p, 52);
pm2_WR(p, PM2R_CHIP_CONFIG, pm2_RD(p, PM2R_CHIP_CONFIG)& pm2_WR(p, PM2R_CHIP_CONFIG, pm2_RD(p, PM2R_CHIP_CONFIG)&
~(PM2F_VGA_ENABLE|PM2F_VGA_FIXED)); ~(PM2F_VGA_ENABLE|PM2F_VGA_FIXED));
...@@ -1031,1396 +434,847 @@ static void reset_units(struct pm2fb_info* p) { ...@@ -1031,1396 +434,847 @@ static void reset_units(struct pm2fb_info* p) {
pm2_RDAC_WR(p, PM2I_RD_BLUE_KEY, 0); pm2_RDAC_WR(p, PM2I_RD_BLUE_KEY, 0);
} }
static void pm2fb_reset(struct pm2fb_info* p) { static void set_aperture(struct pm2fb_par* p)
{
if (p->type == PM2_TYPE_PERMEDIA2V) WAIT_FIFO(p, 4);
pm2_WR(p, PM2VR_RD_INDEX_HIGH, 0); #ifdef __LITTLE_ENDIAN
pm2_WR(p, PM2R_RESET_STATUS, 0); pm2_WR(p, PM2R_APERTURE_ONE, 0);
DEFRW(); pm2_WR(p, PM2R_APERTURE_TWO, 0);
while (pm2_RD(p, PM2R_RESET_STATUS)&PM2F_BEING_RESET);
DEFRW();
#ifdef CONFIG_FB_PM2_FIFO_DISCONNECT
DPRINTK("FIFO disconnect enabled\n");
pm2_WR(p, PM2R_FIFO_DISCON, 1);
DEFRW();
#endif
if (board_table[p->board].init)
board_table[p->board].init(p);
reset_units(p);
clear_palette(p);
if (p->memclock)
pm2_set_memclock(p, p->memclock);
}
static int __init pm2fb_conf(struct pm2fb_info* p) {
for (p->board=0; board_table[p->board].detect &&
!(board_table[p->board].detect(p)); p->board++);
if (!board_table[p->board].detect) {
DPRINTK("no board found.\n");
return 0;
}
DPRINTK("found board: %s\n", board_table[p->board].name);
p->regions.p_fb=p->regions.fb_base;
if (!request_mem_region((unsigned long )p->regions.p_fb,
p->regions.fb_size, "pm2fb")) {
printk (KERN_ERR "pm2fb: cannot reserve fb memory, abort.\n");
return 0;
}
p->regions.v_fb=MMAP(p->regions.p_fb, p->regions.fb_size);
#ifndef PM2FB_BE_APERTURE
p->regions.p_regs=p->regions.rg_base;
#else #else
p->regions.p_regs=p->regions.rg_base+PM2_REGS_SIZE; switch (p->depth) {
#endif case 8:
if (!request_mem_region((unsigned long )p->regions.p_regs, case 24:
PM2_REGS_SIZE, "pm2fb")) { pm2_WR(p, PM2R_APERTURE_ONE, 0);
printk (KERN_ERR "pm2fb: cannot reserve mmio memory, abort.\n"); pm2_WR(p, PM2R_APERTURE_TWO, 1);
UNMAP(p->regions.v_fb, p->regions.fb_size); break;
return 0; case 16:
pm2_WR(p, PM2R_APERTURE_ONE, 2);
pm2_WR(p, PM2R_APERTURE_TWO, 1);
break;
case 32:
pm2_WR(p, PM2R_APERTURE_ONE, 1);
pm2_WR(p, PM2R_APERTURE_TWO, 1);
break;
} }
p->regions.v_regs=MMAP(p->regions.p_regs, PM2_REGS_SIZE);
#ifdef PM2FB_HW_CURSOR
p->cursor = pm2_init_cursor(p);
#endif #endif
return 1;
} }
/*************************************************************************** static void set_color(struct pm2fb_par* p, unsigned char regno,
* Begin of per-board initialization functions unsigned char r, unsigned char g, unsigned char b)
***************************************************************************/ {
WAIT_FIFO(p, 4);
/* pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, regno);
* Phase5 CvisionPPC/BVisionPPC
*/
#ifdef CONFIG_FB_PM2_CVPPC
static int cvppc_PCI_init(struct cvppc_par* p) {
extern u32 powerup_PCI_present;
if (!powerup_PCI_present) {
DPRINTK("no PCI bridge detected\n");
return 0;
}
if (!(p->pci_config=MMAP(CVPPC_PCI_CONFIG, 256))) {
DPRINTK("unable to map PCI config region\n");
return 0;
}
if (RD32(p->pci_config, PCI_VENDOR_ID)!=
((PCI_DEVICE_ID_TI_TVP4020<<16)|PCI_VENDOR_ID_TI)) {
DPRINTK("bad vendorID/deviceID\n");
return 0;
}
if (!(p->pci_bridge=MMAP(CSPPC_PCI_BRIDGE, 256))) {
DPRINTK("unable to map PCI bridge\n");
return 0;
}
WR32(p->pci_bridge, CSPPC_BRIDGE_ENDIAN, CSPPCF_BRIDGE_BIG_ENDIAN);
DEFW(); DEFW();
if (pm2fb_options.flags & OPTF_OLD_MEM) pm2_WR(p, PM2R_RD_PALETTE_DATA, r);
WR32(p->pci_config, PCI_CACHE_LINE_SIZE, 0xff00);
WR32(p->pci_config, PCI_BASE_ADDRESS_0, CVPPC_REGS_REGION);
WR32(p->pci_config, PCI_BASE_ADDRESS_1, CVPPC_FB_APERTURE_ONE);
WR32(p->pci_config, PCI_BASE_ADDRESS_2, CVPPC_FB_APERTURE_TWO);
WR32(p->pci_config, PCI_ROM_ADDRESS, CVPPC_ROM_ADDRESS);
DEFW(); DEFW();
WR32(p->pci_config, PCI_COMMAND, 0xef000000 | pm2_WR(p, PM2R_RD_PALETTE_DATA, g);
PCI_COMMAND_IO |
PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER);
return 1;
}
static int __init cvppc_detect(struct pm2fb_info* p) {
if (!cvppc_PCI_init(&p->board_par.cvppc))
return 0;
p->type=PM2_TYPE_PERMEDIA2;
p->regions.fb_base=CVPPC_FB_APERTURE_ONE;
p->regions.fb_size=CVPPC_FB_SIZE;
p->regions.rg_base=CVPPC_REGS_REGION;
p->memclock=CVPPC_MEMCLOCK;
return 1;
}
static void cvppc_init(struct pm2fb_info* p) {
WAIT_FIFO(p, 3);
pm2_WR(p, PM2R_MEM_CONTROL, 0);
pm2_WR(p, PM2R_BOOT_ADDRESS, 0x30);
DEFW(); DEFW();
if (pm2fb_options.flags & OPTF_OLD_MEM) pm2_WR(p, PM2R_RD_PALETTE_DATA, b);
pm2_WR(p, PM2R_MEM_CONFIG, CVPPC_MEM_CONFIG_OLD);
else
pm2_WR(p, PM2R_MEM_CONFIG, CVPPC_MEM_CONFIG_NEW);
} }
#endif /* CONFIG_FB_PM2_CVPPC */
/* static void set_pixclock(struct pm2fb_par* par, u32 clk)
* Generic PCI detection routines {
*/
#ifdef CONFIG_FB_PM2_PCI
struct {
unsigned short vendor, device;
char *name;
pm2type_t type;
} pm2pci_cards[] __initdata = {
{ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TVP4020,
"Texas Instruments TVP4020", PM2_TYPE_PERMEDIA2 },
{ PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2,
"3dLabs Permedia 2", PM2_TYPE_PERMEDIA2 },
{ PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2V,
"3dLabs Permedia 2v", PM2_TYPE_PERMEDIA2V },
{ 0, 0 }
};
static int __init pm2pci_detect(struct pm2fb_info* p) {
struct pm2pci_par* pci=&p->board_par.pci;
struct pci_dev* dev = NULL;
int i; int i;
unsigned char* m; unsigned char m, n, p;
#ifdef __sparc__
struct pcidev_cookie *pcp;
#endif
memset(pci, 0, sizeof(struct pm2pci_par));
DPRINTK("scanning PCI bus for known chipsets...\n");
while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { switch (par->type) {
for (i = 0; pm2pci_cards[i].vendor; i++) case PM2_TYPE_PERMEDIA2:
if (pm2pci_cards[i].vendor == dev->vendor && pm2_mnp(clk, &m, &n, &p);
pm2pci_cards[i].device == dev->device) { WAIT_FIFO(par, 8);
pci->dev = dev; pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A3, 0);
p->type = pm2pci_cards[i].type; DEFW();
DPRINTK("... found %s\n", pm2pci_cards[i].name); pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A1, m);
break; pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A2, n);
} DEFW();
if (pci->dev) pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A3, 8|p);
break; DEFW();
} pm2_RDAC_RD(par, PM2I_RD_PIXEL_CLOCK_STATUS);
if (!pci->dev) { DEFR();
DPRINTK("no PCI board found.\n"); for (i = 256;
return 0; i && !(pm2_RD(par, PM2R_RD_INDEXED_DATA) & PM2F_PLL_LOCKED);
} i--)
DPRINTK("PCI board @%08lx %08lx %08lx rom %08lx\n",
pci->dev->resource[0].start,
pci->dev->resource[1].start,
pci->dev->resource[2].start,
pci->dev->resource[PCI_ROM_RESOURCE].start);
#ifdef __sparc__
p->regions.rg_base= pci->dev->resource[0].start;
p->regions.fb_base= pci->dev->resource[1].start;
pcp = pci->dev->sysdata;
/* If the user has not asked for a particular mode, lets guess */
if (pcp->prom_node &&
!(pm2fb_options.flags & (OPTF_USER|OPTF_USER_VAR))) {
char timing[256], *q, *r;
unsigned long w, h;
int i;
prom_getstring(pcp->prom_node, "timing-numbers", timing, 256);
/* FIXME: Find out what the actual pixclock is
* and other values as well */
if (timing[0]) {
w = simple_strtoul(timing, &q, 0);
h = 0;
if (q == timing) w = 0;
if (w) {
for (i = 0; i < 3; i++) {
for (r = q;
*r && (*r < '0' || *r > '9');
r++)
; ;
simple_strtoul(r, &q, 0);
if (r == q) break;
}
if (i < 3) w = 0;
}
if (w) {
for (r = q; *r && (*r < '0' || *r > '9'); r++);
h = simple_strtoul(r, &q, 0);
if (r == q) w = 0;
}
if (w == 640 && h == 480) w = 0;
if (w) {
for (i=0; user_mode[i].name[0] &&
(w != user_mode[i].par.width ||
h != user_mode[i].par.height); i++);
if (user_mode[i].name[0])
memcpy(&p->current_par,
&user_mode[i].par,
sizeof(user_mode[i].par));
}
}
}
#else
if (pm2fb_options.flags & OPTF_VIRTUAL) {
p->regions.rg_base = __pa(pci_resource_start(pci->dev, 0));
p->regions.fb_base = __pa(pci_resource_start(pci->dev, 1));
}
else {
p->regions.rg_base = pci_resource_start(pci->dev, 0);
p->regions.fb_base = pci_resource_start(pci->dev, 1);
}
#endif
#ifdef PM2FB_BE_APERTURE
p->regions.rg_base += PM2_REGS_SIZE;
#endif
if ((m=MMAP(p->regions.rg_base, PM2_REGS_SIZE))) {
pci->mem_control=RD32(m, PM2R_MEM_CONTROL);
pci->boot_address=RD32(m, PM2R_BOOT_ADDRESS);
pci->mem_config=RD32(m, PM2R_MEM_CONFIG);
switch (pci->mem_config & PM2F_MEM_CONFIG_RAM_MASK) {
case PM2F_MEM_BANKS_1:
p->regions.fb_size=0x200000;
break; break;
case PM2F_MEM_BANKS_2: case PM2_TYPE_PERMEDIA2V:
p->regions.fb_size=0x400000; pm2v_mnp(clk/2, &m, &n, &p);
break; WAIT_FIFO(par, 8);
case PM2F_MEM_BANKS_3: pm2_WR(par, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CLK0_PRESCALE >> 8);
p->regions.fb_size=0x600000; pm2v_RDAC_WR(par, PM2VI_RD_CLK0_PRESCALE, m);
break; pm2v_RDAC_WR(par, PM2VI_RD_CLK0_FEEDBACK, n);
case PM2F_MEM_BANKS_4: pm2v_RDAC_WR(par, PM2VI_RD_CLK0_POSTSCALE, p);
p->regions.fb_size=0x800000; pm2_WR(par, PM2VR_RD_INDEX_HIGH, 0);
break; break;
} }
p->memclock=CVPPC_MEMCLOCK;
UNMAP(m, PM2_REGS_SIZE);
return 1;
}
DPRINTK("MMAP() failed.\n");
return 0;
} }
static void pm2pci_init(struct pm2fb_info* p) { static void set_video(struct pm2fb_par* p, u32 video) {
struct pm2pci_par* pci=&p->board_par.pci; u32 tmp;
u32 vsync;
WAIT_FIFO(p, 3); vsync = video;
pm2_WR(p, PM2R_MEM_CONTROL, pci->mem_control);
pm2_WR(p, PM2R_BOOT_ADDRESS, pci->boot_address);
DEFW();
pm2_WR(p, PM2R_MEM_CONFIG, pci->mem_config);
}
#endif /* CONFIG_FB_PM2_PCI */
/*************************************************************************** /*
* Console hw acceleration * The hardware cursor needs +vsync to recognise vert retrace.
***************************************************************************/ * We may not be using the hardware cursor, but the X Glint
* driver may well. So always set +hsync/+vsync and then set
* the RAMDAC to invert the sync if necessary.
*/
vsync &= ~(PM2F_HSYNC_MASK|PM2F_VSYNC_MASK);
vsync |= PM2F_HSYNC_ACT_HIGH|PM2F_VSYNC_ACT_HIGH;
static int pm2fb_blank(int blank_mode, struct fb_info_gen* info) { WAIT_FIFO(p, 5);
struct pm2fb_info* i=(struct pm2fb_info* )info; pm2_WR(p, PM2R_VIDEO_CONTROL, vsync);
u32 video;
if (!i->current_par_valid) switch (p->type) {
return 1; case PM2_TYPE_PERMEDIA2:
video=i->current_par.video; tmp = PM2F_RD_PALETTE_WIDTH_8;
if (blank_mode>0) { if ((video & PM2F_HSYNC_MASK) == PM2F_HSYNC_ACT_LOW)
i->is_blank=1; tmp |= 4; /* invert hsync */
switch (blank_mode-1) { if ((video & PM2F_VSYNC_MASK) == PM2F_VSYNC_ACT_LOW)
case VESA_NO_BLANKING: /* FIXME */ tmp |= 8; /* invert vsync */
video=video&~(PM2F_VIDEO_ENABLE); pm2_RDAC_WR(p, PM2I_RD_MISC_CONTROL, tmp);
break;
case VESA_HSYNC_SUSPEND:
video=video&~(PM2F_HSYNC_MASK|
PM2F_BLANK_LOW);
break;
case VESA_VSYNC_SUSPEND:
video=video&~(PM2F_VSYNC_MASK|
PM2F_BLANK_LOW);
break; break;
case VESA_POWERDOWN: case PM2_TYPE_PERMEDIA2V:
video=video&~(PM2F_VSYNC_MASK| tmp = 0;
PM2F_HSYNC_MASK| if ((video & PM2F_HSYNC_MASK) == PM2F_HSYNC_ACT_LOW)
PM2F_BLANK_LOW); tmp |= 1; /* invert hsync */
if ((video & PM2F_VSYNC_MASK) == PM2F_VSYNC_ACT_LOW)
tmp |= 4; /* invert vsync */
pm2v_RDAC_WR(p, PM2VI_RD_SYNC_CONTROL, tmp);
pm2v_RDAC_WR(p, PM2VI_RD_MISC_CONTROL, 1);
break; break;
} }
}
else
i->is_blank=0;
set_video(i, video);
return 0;
} }
static int pm2fb_pan_display(const struct fb_var_screeninfo* var, /*
struct fb_info_gen* info) { *
struct pm2fb_info* i=(struct pm2fb_info* )info; */
if (!i->current_par_valid)
return -EINVAL;
i->current_par.base=to3264(var->yoffset*i->current_par.width+
var->xoffset, i->current_par.depth, 1);
WAIT_FIFO(i, 1);
pm2_WR(i, PM2R_SCREEN_BASE, i->current_par.base);
return 0;
}
static void pm2fb_pp_bmove(struct display* p, int sy, int sx, /**
int dy, int dx, int height, int width) { * pm2fb_check_var - Optional function. Validates a var passed in.
* @var: frame buffer variable screen structure
* @info: frame buffer structure that represents a single frame buffer
*
* Checks to see if the hardware supports the state requested by
* var passed in.
*
* Returns negative errno on error, or zero on success.
*/
static int pm2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
u32 lpitch;
if (fontwidthlog(p)) { if (var->bits_per_pixel != 8 && var->bits_per_pixel != 16 &&
sx=sx<<fontwidthlog(p); var->bits_per_pixel != 24 && var->bits_per_pixel != 32) {
dx=dx<<fontwidthlog(p); DPRINTK("depth not supported: %u\n", var->bits_per_pixel);
width=width<<fontwidthlog(p); return -EINVAL;
}
else {
sx=sx*fontwidth(p);
dx=dx*fontwidth(p);
width=width*fontwidth(p);
} }
sy=sy*fontheight(p);
dy=dy*fontheight(p);
height=height*fontheight(p);
pm2fb_pp_copy((struct pm2fb_info* )p->fb_info, sx, sy, dx,
dy, width, height);
}
static void pm2fb_bmove(struct display* p, int sy, int sx, if (var->xres != var->xres_virtual) {
int dy, int dx, int height, int width) { DPRINTK("virtual x resolution != physical x resolution not supported\n");
return -EINVAL;
if (fontwidthlog(p)) {
sx=sx<<fontwidthlog(p);
dx=dx<<fontwidthlog(p);
width=width<<fontwidthlog(p);
}
else {
sx=sx*fontwidth(p);
dx=dx*fontwidth(p);
width=width*fontwidth(p);
} }
sy=sy*fontheight(p);
dy=dy*fontheight(p);
height=height*fontheight(p);
pm2fb_block_op((struct pm2fb_info* )p->fb_info, 1, sx, sy, dx, dy,
width, height, 0);
}
#ifdef FBCON_HAS_CFB8 if (var->yres > var->yres_virtual) {
static void pm2fb_clear8(struct vc_data* conp, struct display* p, DPRINTK("virtual y resolution < physical y resolution not possible\n");
int sy, int sx, int height, int width) { return -EINVAL;
u32 c; }
sx=sx*fontwidth(p);
width=width*fontwidth(p);
sy=sy*fontheight(p);
height=height*fontheight(p);
c=attr_bgcol_ec(p, conp);
c|=c<<8;
c|=c<<16;
pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0, sx, sy,
width, height, c);
}
static void pm2fb_clear_margins8(struct vc_data* conp, struct display* p, if (var->xoffset) {
int bottom_only) { DPRINTK("xoffset not supported\n");
u32 c; return -EINVAL;
u32 sx; }
u32 sy;
c=attr_bgcol_ec(p, conp);
c|=c<<8;
c|=c<<16;
sx=conp->vc_cols*fontwidth(p);
sy=conp->vc_rows*fontheight(p);
if (!bottom_only)
pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0,
sx, 0, (p->var.xres-sx), p->var.yres_virtual, c);
pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0,
0, p->var.yoffset+sy, sx, p->var.yres-sy, c);
}
static struct display_switch pm2_cfb8 = { if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
.setup = fbcon_cfb8_setup, DPRINTK("interlace not supported\n");
.bmove = pm2fb_pp_bmove, return -EINVAL;
#ifdef __alpha__ }
/* Not sure why, but this works and the other does not. */
/* Also, perhaps we need a separate routine to wait for the
blitter to stop before doing this? */
/* In addition, maybe we need to do this for 16 and 32 bit depths? */
.clear = fbcon_cfb8_clear,
#else
.clear = pm2fb_clear8,
#endif
.putc = fbcon_cfb8_putc,
.putcs = fbcon_cfb8_putcs,
.revc = fbcon_cfb8_revc,
.cursor = pm2fb_cursor,
.set_font = pm2fb_set_font,
.clear_margins = pm2fb_clear_margins8,
.fontwidthmask = FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16) };
#endif /* FBCON_HAS_CFB8 */
#ifdef FBCON_HAS_CFB16
static void pm2fb_clear16(struct vc_data* conp, struct display* p,
int sy, int sx, int height, int width) {
u32 c;
sx=sx*fontwidth(p);
width=width*fontwidth(p);
sy=sy*fontheight(p);
height=height*fontheight(p);
c=((u16 *)p->dispsw_data)[attr_bgcol_ec(p, conp)];
c|=c<<16;
pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0, sx, sy,
width, height, c);
}
static void pm2fb_clear_margins16(struct vc_data* conp, struct display* p, var->xres = (var->xres + 15) & ~15; /* could sometimes be 8 */
int bottom_only) { lpitch = var->xres * ((var->bits_per_pixel + 7)>>3);
u32 c;
u32 sx;
u32 sy;
c=((u16 *)p->dispsw_data)[attr_bgcol_ec(p, conp)];
c|=c<<16;
sx=conp->vc_cols*fontwidth(p);
sy=conp->vc_rows*fontheight(p);
if (!bottom_only)
pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0,
sx, 0, (p->var.xres-sx), p->var.yres_virtual, c);
pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0,
0, p->var.yoffset+sy, sx, p->var.yres-sy, c);
}
static struct display_switch pm2_cfb16 = { if (var->xres < 320 || var->xres > 1600) {
.setup = fbcon_cfb16_setup, DPRINTK("width not supported: %u\n", var->xres);
.bmove = pm2fb_pp_bmove, return -EINVAL;
.clear = pm2fb_clear16, }
.putc = fbcon_cfb16_putc,
.putcs = fbcon_cfb16_putcs,
.revc = fbcon_cfb16_revc,
.cursor = pm2fb_cursor,
.set_font = pm2fb_set_font,
.clear_margins =pm2fb_clear_margins16,
.fontwidthmask =FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
};
#endif /* FBCON_HAS_CFB16 */
#ifdef FBCON_HAS_CFB24 if (var->yres < 200 || var->yres > 1200) {
/* DPRINTK("height not supported: %u\n", var->yres);
* fast fill for 24bpp works only when red==green==blue return -EINVAL;
*/
static void pm2fb_clear24(struct vc_data* conp, struct display* p,
int sy, int sx, int height, int width) {
struct pm2fb_info* i=(struct pm2fb_info* )p->fb_info;
u32 c;
c=attr_bgcol_ec(p, conp);
if ( i->palette[c].red==i->palette[c].green &&
i->palette[c].green==i->palette[c].blue) {
c=((u32 *)p->dispsw_data)[c];
c|=(c&0xff0000)<<8;
sx=sx*fontwidth(p);
width=width*fontwidth(p);
sy=sy*fontheight(p);
height=height*fontheight(p);
pm2fb_block_op(i, 0, 0, 0, sx, sy, width, height, c);
} }
else
fbcon_cfb24_clear(conp, p, sy, sx, height, width);
} if (lpitch * var->yres_virtual > info->fix.smem_len) {
DPRINTK("no memory for screen (%ux%ux%u)\n",
var->xres, var->yres_virtual, var->bits_per_pixel);
return -EINVAL;
}
static void pm2fb_clear_margins24(struct vc_data* conp, struct display* p, if (PICOS2KHZ(var->pixclock) > PM2_MAX_PIXCLOCK) {
int bottom_only) { DPRINTK("pixclock too high (%ldKHz)\n", PICOS2KHZ(var->pixclock));
struct pm2fb_info* i=(struct pm2fb_info* )p->fb_info; return -EINVAL;
u32 sx; }
u32 sy;
sx=conp->vc_cols*fontwidth(p);
sy=conp->vc_rows*fontheight(p);
if (!bottom_only)
pm2fb_block_op(i, 0, 0, 0, sx, 0, (p->var.xres-sx),
p->var.yres_virtual, 0L);
pm2fb_block_op(i, 0, 0, 0, 0, p->var.yoffset+sy, sx, p->var.yres-sy, 0L);
}
static struct display_switch pm2_cfb24 = { switch(var->bits_per_pixel) {
.setup = fbcon_cfb24_setup, case 8:
.bmove = pm2fb_bmove, var->red.length = var->green.length = var->blue.length = 8;
.clear = pm2fb_clear24, break;
.putc = fbcon_cfb24_putc, case 16:
.putcs = fbcon_cfb24_putcs, var->red.offset = 11;
.revc = fbcon_cfb24_revc, var->red.length = 5;
.cursor = pm2fb_cursor, var->green.offset = 5;
.set_font = pm2fb_set_font, var->green.length = 6;
.clear_margins =pm2fb_clear_margins24, var->blue.offset = 0;
.fontwidthmask =FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16) var->blue.length = 5;
}; break;
#endif /* FBCON_HAS_CFB24 */ case 24:
var->red.offset = 16;
#ifdef FBCON_HAS_CFB32 var->green.offset = 8;
static void pm2fb_clear32(struct vc_data* conp, struct display* p, var->blue.offset = 0;
int sy, int sx, int height, int width) { var->red.length = var->green.length = var->blue.length = 8;
u32 c; case 32:
var->red.offset = 16;
sx=sx*fontwidth(p); var->green.offset = 8;
width=width*fontwidth(p); var->blue.offset = 0;
sy=sy*fontheight(p); var->red.length = var->green.length = var->blue.length = 8;
height=height*fontheight(p); break;
c=((u32 *)p->dispsw_data)[attr_bgcol_ec(p, conp)]; }
pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0, sx, sy, var->height = var->width = -1;
width, height, c);
}
static void pm2fb_clear_margins32(struct vc_data* conp, struct display* p, var->accel_flags = 0; /* Can't mmap if this is on */
int bottom_only) {
u32 c;
u32 sx;
u32 sy;
c=((u32 *)p->dispsw_data)[attr_bgcol_ec(p, conp)];
sx=conp->vc_cols*fontwidth(p);
sy=conp->vc_rows*fontheight(p);
if (!bottom_only)
pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0,
sx, 0, (p->var.xres-sx), p->var.yres_virtual, c);
pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0,
0, p->var.yoffset+sy, sx, p->var.yres-sy, c);
}
static struct display_switch pm2_cfb32 = { DPRINTK("Checking graphics mode at %dx%d depth %d\n",
.setup = fbcon_cfb32_setup, var->xres, var->yres, var->bits_per_pixel);
.bmove = pm2fb_bmove,
.clear = pm2fb_clear32,
.putc = fbcon_cfb32_putc,
.putcs = fbcon_cfb32_putcs,
.revc = fbcon_cfb32_revc,
.cursor = pm2fb_cursor,
.set_font = pm2fb_set_font,
.clear_margins =pm2fb_clear_margins32,
.fontwidthmask =FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
};
#endif /* FBCON_HAS_CFB32 */
/***************************************************************************
* Framebuffer functions
***************************************************************************/
static void pm2fb_detect(void) {}
static int pm2fb_encode_fix(struct fb_fix_screeninfo* fix,
const void* par, struct fb_info_gen* info) {
struct pm2fb_info* i=(struct pm2fb_info* )info;
struct pm2fb_par* p=(struct pm2fb_par* )par;
strcpy(fix->id, permedia2_name);
fix->smem_start=(unsigned long )i->regions.p_fb;
fix->smem_len=i->regions.fb_size;
fix->mmio_start=(unsigned long )i->regions.p_regs;
fix->mmio_len=PM2_REGS_SIZE;
fix->accel=FB_ACCEL_3DLABS_PERMEDIA2;
fix->type=FB_TYPE_PACKED_PIXELS;
fix->visual=p->depth==8?FB_VISUAL_PSEUDOCOLOR:FB_VISUAL_TRUECOLOR;
fix->line_length=p->width*p->depth/8;
fix->xpanstep=p->depth==24?8:64/p->depth;
fix->ypanstep=1;
fix->ywrapstep=0;
return 0; return 0;
} }
#ifdef PM2FB_MASTER_DEBUG /**
static void pm2fb_display_var(const struct fb_var_screeninfo* var) { * pm2fb_set_par - Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
printk( KERN_DEBUG *
"- struct fb_var_screeninfo ---------------------------------------------------\n"); * Using the fb_var_screeninfo in fb_info we set the resolution of the
printk( KERN_DEBUG * this particular framebuffer.
"resolution: %ux%ux%u (virtual %ux%u+%u+%u)\n", */
var->xres, var->yres, var->bits_per_pixel, static int pm2fb_set_par(struct fb_info *info)
var->xres_virtual, var->yres_virtual, {
var->xoffset, var->yoffset); struct pm2fb_par *par = (struct pm2fb_par *) info->par;
printk( KERN_DEBUG u32 pixclock;
"color: %c%c " u32 width, height, depth;
"R(%u,%u,%u), G(%u,%u,%u), B(%u,%u,%u), T(%u,%u,%u)\n", u32 hsstart, hsend, hbend, htotal;
var->grayscale?'G':'C', var->nonstd?'N':'S', u32 vsstart, vsend, vbend, vtotal;
var->red.offset, var->red.length, var->red.msb_right, u32 stride;
var->green.offset, var->green.length, var->green.msb_right, u32 base;
var->blue.offset, var->blue.length, var->blue.msb_right, u32 video = 0;
var->transp.offset, var->transp.length, u32 clrmode = PM2F_RD_COLOR_MODE_RGB;
var->transp.msb_right); u32 txtmap = 0;
printk( KERN_DEBUG u32 pixsize = 0;
"timings: %ups (%u,%u)-(%u,%u)+%u+%u\n", u32 clrformat = 0;
var->pixclock,
var->left_margin, var->upper_margin, var->right_margin,
var->lower_margin, var->hsync_len, var->vsync_len);
printk( KERN_DEBUG
"activate %08x accel_flags %08x sync %08x vmode %08x\n",
var->activate, var->accel_flags, var->sync, var->vmode);
printk( KERN_DEBUG
"------------------------------------------------------------------------------\n");
}
#define pm2fb_decode_var pm2fb_wrapped_decode_var
#endif
static int pm2fb_decode_var(const struct fb_var_screeninfo* var,
void* par, struct fb_info_gen* info) {
struct pm2fb_info* i=(struct pm2fb_info* )info;
struct pm2fb_par p;
u32 xres; u32 xres;
int data64; int data64;
memset(&p, 0, sizeof(struct pm2fb_par)); reset_config(par);
if (var->accel_flags & FB_ACCELF_TEXT) clear_palette(par);
p.flags |= PM2FF_ACCEL;
p.width=(var->xres_virtual+7)&~7; width = (info->var.xres_virtual + 7) & ~7;
p.height=var->yres_virtual; height = info->var.yres_virtual;
p.depth=(var->bits_per_pixel+7)&~7; depth = (info->var.bits_per_pixel + 7) & ~7;
p.depth=p.depth>32?32:p.depth; depth = (depth > 32) ? 32 : depth;
data64=p.depth>8 || i->type==PM2_TYPE_PERMEDIA2V; data64 = depth > 8 || par->type == PM2_TYPE_PERMEDIA2V;
xres=(var->xres+31)&~31;
if (p.width<xres+var->xoffset) xres = (info->var.xres + 31) & ~31;
p.width=xres+var->xoffset; pixclock = PICOS2KHZ(info->var.pixclock);
if (p.height<var->yres+var->yoffset) if (pixclock > PM2_MAX_PIXCLOCK) {
p.height=var->yres+var->yoffset; DPRINTK("pixclock too high (%uKHz)\n", pixclock);
if (!partprod(xres)) {
DPRINTK("width not supported: %u\n", xres);
return -EINVAL;
}
if (p.width>2047) {
DPRINTK("virtual width not supported: %u\n", p.width);
return -EINVAL;
}
if (var->yres<200) {
DPRINTK("height not supported: %u\n",
(u32 )var->yres);
return -EINVAL;
}
if (p.height<200 || p.height>2047) {
DPRINTK("virtual height not supported: %u\n", p.height);
return -EINVAL;
}
if (p.depth>32) {
DPRINTK("depth not supported: %u\n", p.depth);
return -EINVAL;
}
if (p.width*p.height*p.depth/8>i->regions.fb_size) {
DPRINTK("no memory for screen (%ux%ux%u)\n",
p.width, p.height, p.depth);
return -EINVAL;
}
p.pixclock=PICOS2KHZ(var->pixclock);
if (p.pixclock>PM2_MAX_PIXCLOCK) {
DPRINTK("pixclock too high (%uKHz)\n", p.pixclock);
return -EINVAL; return -EINVAL;
} }
p.hsstart=to3264(var->right_margin, p.depth, data64);
p.hsend=p.hsstart+to3264(var->hsync_len, p.depth, data64); hsstart = to3264(info->var.right_margin, depth, data64);
p.hbend=p.hsend+to3264(var->left_margin, p.depth, data64); hsend = hsstart + to3264(info->var.hsync_len, depth, data64);
p.htotal=to3264(xres, p.depth, data64)+p.hbend-1; hbend = hsend + to3264(info->var.left_margin, depth, data64);
p.vsstart=var->lower_margin?var->lower_margin-1:0; /* FIXME! */ htotal = to3264(xres, depth, data64) + hbend - 1;
p.vsend=var->lower_margin+var->vsync_len-1; vsstart = (info->var.lower_margin)
p.vbend=var->lower_margin+var->vsync_len+var->upper_margin; ? info->var.lower_margin - 1
p.vtotal=var->yres+p.vbend-1; : 0; /* FIXME! */
p.stride=to3264(p.width, p.depth, 1); vsend = info->var.lower_margin + info->var.vsync_len - 1;
p.base=to3264(var->yoffset*xres+var->xoffset, p.depth, 1); vbend = info->var.lower_margin + info->var.vsync_len + info->var.upper_margin;
vtotal = info->var.yres + vbend - 1;
stride = to3264(width, depth, 1);
base = to3264(info->var.yoffset * xres + info->var.xoffset, depth, 1);
if (data64) if (data64)
p.video|=PM2F_DATA_64_ENABLE; video |= PM2F_DATA_64_ENABLE;
if (var->sync & FB_SYNC_HOR_HIGH_ACT) {
if (pm2fb_options.flags & OPTF_LOW_HSYNC) { if (info->var.sync & FB_SYNC_HOR_HIGH_ACT) {
if (lowhsync) {
DPRINTK("ignoring +hsync, using -hsync.\n"); DPRINTK("ignoring +hsync, using -hsync.\n");
p.video|=PM2F_HSYNC_ACT_LOW; video |= PM2F_HSYNC_ACT_LOW;
} else } else
p.video|=PM2F_HSYNC_ACT_HIGH; video |= PM2F_HSYNC_ACT_HIGH;
} }
else else
p.video|=PM2F_HSYNC_ACT_LOW; video |= PM2F_HSYNC_ACT_LOW;
if (var->sync & FB_SYNC_VERT_HIGH_ACT) { if (info->var.sync & FB_SYNC_VERT_HIGH_ACT) {
if (pm2fb_options.flags & OPTF_LOW_VSYNC) { if (lowvsync) {
DPRINTK("ignoring +vsync, using -vsync.\n"); DPRINTK("ignoring +vsync, using -vsync.\n");
p.video|=PM2F_VSYNC_ACT_LOW; video |= PM2F_VSYNC_ACT_LOW;
} else } else
p.video|=PM2F_VSYNC_ACT_HIGH; video |= PM2F_VSYNC_ACT_HIGH;
} }
else else
p.video|=PM2F_VSYNC_ACT_LOW; video |= PM2F_VSYNC_ACT_LOW;
if ((var->vmode & FB_VMODE_MASK)==FB_VMODE_INTERLACED) { if ((info->var.vmode & FB_VMODE_MASK)==FB_VMODE_INTERLACED) {
DPRINTK("interlaced not supported\n"); DPRINTK("interlaced not supported\n");
return -EINVAL; return -EINVAL;
} }
if ((var->vmode & FB_VMODE_MASK)==FB_VMODE_DOUBLE) if ((info->var.vmode & FB_VMODE_MASK)==FB_VMODE_DOUBLE)
p.video|=PM2F_LINE_DOUBLE; video |= PM2F_LINE_DOUBLE;
if (var->activate==FB_ACTIVATE_NOW) if (info->var.activate==FB_ACTIVATE_NOW)
p.video|=PM2F_VIDEO_ENABLE; video |= PM2F_VIDEO_ENABLE;
*((struct pm2fb_par* )par)=p; par->video = video;
return 0;
}
#ifdef PM2FB_MASTER_DEBUG
#undef pm2fb_decode_var
static int pm2fb_decode_var(const struct fb_var_screeninfo* var, info->fix.visual =
void* par, struct fb_info_gen* info) { (depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
int result; info->fix.line_length =
info->var.xres * ((info->var.bits_per_pixel + 7) >> 3);
info->cmap.len = 256;
result=pm2fb_wrapped_decode_var(var, par, info); /*
pm2fb_display_var(var); * Settings calculated. Now write them out.
return result; */
} if (par->type == PM2_TYPE_PERMEDIA2V) {
#endif WAIT_FIFO(par, 1);
pm2_WR(par, PM2VR_RD_INDEX_HIGH, 0);
}
static void pm2fb_par2var(struct fb_var_screeninfo* v, set_aperture(par);
const struct pm2fb_par* p) {
u32 base;
memset(v, 0, sizeof(struct fb_var_screeninfo)); DEFRW();
if (p->flags & PM2FF_ACCEL) WAIT_FIFO(par, 19);
v->accel_flags |= FB_ACCELF_TEXT; pm2_RDAC_WR(par, PM2I_RD_COLOR_KEY_CONTROL,
v->xres_virtual=p->width; ( depth == 8 ) ? 0 : PM2F_COLOR_KEY_TEST_OFF);
v->yres_virtual=p->height; switch (depth) {
v->xres=(p->htotal+1)-p->hbend;
v->yres=(p->vtotal+1)-p->vbend;
v->right_margin=p->hsstart;
v->hsync_len=p->hsend-p->hsstart;
v->left_margin=p->hbend-p->hsend;
v->lower_margin=p->vsstart+1;
v->vsync_len=p->vsend-v->lower_margin+1;
v->upper_margin=p->vbend-v->lower_margin-v->vsync_len;
v->bits_per_pixel=p->depth;
if (p->video & PM2F_DATA_64_ENABLE) {
v->xres=v->xres<<1;
v->right_margin=v->right_margin<<1;
v->hsync_len=v->hsync_len<<1;
v->left_margin=v->left_margin<<1;
}
switch (p->depth) {
case 8: case 8:
v->red.length=v->green.length=v->blue.length=8; pm2_WR(par, PM2R_FB_READ_PIXEL, 0);
v->xres=v->xres<<2; clrformat = 0x0e;
v->right_margin=v->right_margin<<2;
v->hsync_len=v->hsync_len<<2;
v->left_margin=v->left_margin<<2;
break; break;
case 16: case 16:
v->red.offset=11; pm2_WR(par, PM2R_FB_READ_PIXEL, 1);
v->red.length=5; clrmode |= PM2F_RD_TRUECOLOR | 0x06;
v->green.offset=5; txtmap = PM2F_TEXTEL_SIZE_16;
v->green.length=6; pixsize = 1;
v->blue.length=5; clrformat = 0x70;
v->xres=v->xres<<1;
v->right_margin=v->right_margin<<1;
v->hsync_len=v->hsync_len<<1;
v->left_margin=v->left_margin<<1;
break; break;
case 32: case 32:
v->transp.offset=24; pm2_WR(par, PM2R_FB_READ_PIXEL, 2);
v->red.offset=16; clrmode |= PM2F_RD_TRUECOLOR | 0x08;
v->green.offset=8; txtmap = PM2F_TEXTEL_SIZE_32;
v->red.length=v->green.length=v->blue.length= pixsize = 2;
v->transp.length=8; clrformat = 0x20;
break; break;
case 24: case 24:
v->blue.offset=16; pm2_WR(par, PM2R_FB_READ_PIXEL, 4);
v->green.offset=8; clrmode |= PM2F_RD_TRUECOLOR | 0x09;
v->red.length=v->green.length=v->blue.length=8; #ifndef PM2FB_BE_APERTURE
v->xres=(v->xres<<2)/3; clrmode &= ~PM2F_RD_COLOR_MODE_RGB;
v->right_margin=(v->right_margin<<2)/3; #endif
v->hsync_len=(v->hsync_len<<2)/3; txtmap = PM2F_TEXTEL_SIZE_24;
v->left_margin=(v->left_margin<<2)/3; pixsize = 4;
clrformat = 0x20;
break; break;
} }
base=from3264(p->base, p->depth, 1); pm2_WR(par, PM2R_FB_WRITE_MODE, PM2F_FB_WRITE_ENABLE);
v->xoffset=base%v->xres; pm2_WR(par, PM2R_FB_READ_MODE, partprod(xres));
v->yoffset=base/v->xres; pm2_WR(par, PM2R_LB_READ_MODE, partprod(xres));
v->height=v->width=-1; pm2_WR(par, PM2R_TEXTURE_MAP_FORMAT, txtmap | partprod(xres));
v->pixclock=KHZ2PICOS(p->pixclock); pm2_WR(par, PM2R_H_TOTAL, htotal);
if ((p->video & PM2F_HSYNC_MASK)==PM2F_HSYNC_ACT_HIGH) pm2_WR(par, PM2R_HS_START, hsstart);
v->sync|=FB_SYNC_HOR_HIGH_ACT; pm2_WR(par, PM2R_HS_END, hsend);
if ((p->video & PM2F_VSYNC_MASK)==PM2F_VSYNC_ACT_HIGH) pm2_WR(par, PM2R_HG_END, hbend);
v->sync|=FB_SYNC_VERT_HIGH_ACT; pm2_WR(par, PM2R_HB_END, hbend);
if (p->video & PM2F_LINE_DOUBLE) pm2_WR(par, PM2R_V_TOTAL, vtotal);
v->vmode=FB_VMODE_DOUBLE; pm2_WR(par, PM2R_VS_START, vsstart);
} pm2_WR(par, PM2R_VS_END, vsend);
pm2_WR(par, PM2R_VB_END, vbend);
pm2_WR(par, PM2R_SCREEN_STRIDE, stride);
static int pm2fb_encode_var(struct fb_var_screeninfo* var, DEFW();
const void* par, struct fb_info_gen* info) { pm2_WR(par, PM2R_WINDOW_ORIGIN, 0);
pm2_WR(par, PM2R_SCREEN_SIZE, (height << 16) | width);
pm2fb_par2var(var, (struct pm2fb_par* )par); pm2_WR(par, PM2R_SCISSOR_MODE, PM2F_SCREEN_SCISSOR_ENABLE);
return 0; DEFW();
} pm2_WR(par, PM2R_SCREEN_BASE, base);
DEFW();
static void set_user_mode(struct pm2fb_info* i) { set_video(par, video);
WAIT_FIFO(par, 4);
memcpy(&i->current_par, &pm2fb_options.user_mode, switch (par->type) {
sizeof(i->current_par)); case PM2_TYPE_PERMEDIA2:
if (pm2fb_options.flags & OPTF_YPAN) { pm2_RDAC_WR(par, PM2I_RD_COLOR_MODE,
i->current_par.height=i->regions.fb_size/ PM2F_RD_COLOR_MODE_RGB | PM2F_RD_GUI_ACTIVE | clrmode);
(i->current_par.width*i->current_par.depth/8); break;
i->current_par.height=MIN(i->current_par.height,2047); case PM2_TYPE_PERMEDIA2V:
i->current_par.height=MAX(i->current_par.height, pm2v_RDAC_WR(par, PM2VI_RD_PIXEL_SIZE, pixsize);
pm2fb_options.user_mode.height); pm2v_RDAC_WR(par, PM2VI_RD_COLOR_FORMAT, clrformat);
break;
} }
set_pixclock(par, pixclock);
return 0;
} }
static void pm2fb_get_par(void* par, struct fb_info_gen* info) { /**
struct pm2fb_info* i=(struct pm2fb_info* )info; * pm2fb_setcolreg - Sets a color register.
* @regno: boolean, 0 copy local, 1 get_user() function
if (!i->current_par_valid) { * @red: frame buffer colormap structure
set_user_mode(i); * @green: The green value which can be up to 16 bits wide
pm2fb_set_par(&i->current_par, info); * @blue: The blue value which can be up to 16 bits wide.
} * @transp: If supported the alpha value which can be up to 16 bits wide.
get_screen(i, (struct pm2fb_par* )par); * @info: frame buffer info structure
} *
* Set a single color register. The values supplied have a 16 bit
* magnitude which needs to be scaled in this function for the hardware.
* Pretty much a direct lift from tdfxfb.c.
*
* Returns negative errno on error, or zero on success.
*/
static int pm2fb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
{
struct pm2fb_par *par = (struct pm2fb_par *) info->par;
static void pm2fb_set_par(const void* par, struct fb_info_gen* info) { if (regno >= info->cmap.len) /* no. of hw registers */
struct pm2fb_info* i=(struct pm2fb_info* )info; return 1;
struct pm2fb_par* p=(struct pm2fb_par* )par; /*
* Program hardware... do anything you want with transp
*/
if (screen_is_valid(i)) { /* grayscale works only partially under directcolor */
i->current_par.base=p->base; if (info->var.grayscale) {
if (!memcmp(p, &i->current_par, sizeof(struct pm2fb_par))) { /* grayscale = 0.30*R + 0.59*G + 0.11*B */
WAIT_FIFO(i, 1); red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
pm2_WR(i, PM2R_SCREEN_BASE, p->base);
return;
}
} }
wait_pm2(i); /* Directcolor:
reset_units(i); * var->{color}.offset contains start of bitfield
set_screen(i, p); * var->{color}.length contains length of bitfield
i->current_par=*p; * {hardwarespecific} contains width of DAC
i->current_par_valid=1; * cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset)
#ifdef PM2FB_HW_CURSOR * RAMDAC[X] is programmed to (red, green, blue)
if (i->cursor) { *
pm2v_set_cursor_color(i, cursor_color_map, * Pseudocolor:
cursor_color_map, cursor_color_map); * uses offset = 0 && length = DAC register width.
pm2v_set_cursor_shape(i); * var->{color}.offset is 0
* var->{color}.length contains widht of DAC
* cmap is not used
* DAC[X] is programmed to (red, green, blue)
* Truecolor:
* does not use RAMDAC (usually has 3 of them).
* var->{color}.offset contains start of bitfield
* var->{color}.length contains length of bitfield
* cmap is programmed to (red << red.offset) | (green << green.offset) |
* (blue << blue.offset) | (transp << transp.offset)
* RAMDAC does not exist
*/
#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
switch (info->fix.visual) {
case FB_VISUAL_TRUECOLOR:
case FB_VISUAL_PSEUDOCOLOR:
red = CNVT_TOHW(red, info->var.red.length);
green = CNVT_TOHW(green, info->var.green.length);
blue = CNVT_TOHW(blue, info->var.blue.length);
transp = CNVT_TOHW(transp, info->var.transp.length);
set_color(par, regno, red, green, blue);
break;
case FB_VISUAL_DIRECTCOLOR:
/* example here assumes 8 bit DAC. Might be different
* for your hardware */
red = CNVT_TOHW(red, 8);
green = CNVT_TOHW(green, 8);
blue = CNVT_TOHW(blue, 8);
/* hey, there is bug in transp handling... */
transp = CNVT_TOHW(transp, 8);
break;
} }
#endif #undef CNVT_TOHW
} /* Truecolor has hardware independent palette */
if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
static int pm2fb_getcolreg(unsigned regno, u32 v;
unsigned* red, unsigned* green, unsigned* blue,
unsigned* transp, struct fb_info* info) {
struct pm2fb_info* i=(struct pm2fb_info* )info;
if (regno<256) { if (regno >= 16)
*red=i->palette[regno].red<<8|i->palette[regno].red; return 1;
*green=i->palette[regno].green<<8|i->palette[regno].green;
*blue=i->palette[regno].blue<<8|i->palette[regno].blue;
*transp=i->palette[regno].transp<<8|i->palette[regno].transp;
}
return regno>255;
}
static int pm2fb_setcolreg(unsigned regno, v = (red << info->var.red.offset) |
unsigned red, unsigned green, unsigned blue, (green << info->var.green.offset) |
unsigned transp, struct fb_info* info) { (blue << info->var.blue.offset) |
struct pm2fb_info* i=(struct pm2fb_info* )info; (transp << info->var.transp.offset);
if (regno<16) { switch (info->var.bits_per_pixel) {
switch (i->current_par.depth) {
#ifdef FBCON_HAS_CFB8
case 8: case 8:
/* Yes some hand held devices have this. */
((u8*)(info->pseudo_palette))[regno] = v;
break; break;
#endif
#ifdef FBCON_HAS_CFB16
case 16: case 16:
i->cmap.cmap16[regno]= ((u16*)(info->pseudo_palette))[regno] = v;
((u32 )red & 0xf800) |
(((u32 )green & 0xfc00)>>5) |
(((u32 )blue & 0xf800)>>11);
break; break;
#endif
#ifdef FBCON_HAS_CFB24
case 24: case 24:
i->cmap.cmap24[regno]=
(((u32 )blue & 0xff00) << 8) |
((u32 )green & 0xff00) |
(((u32 )red & 0xff00) >> 8);
break;
#endif
#ifdef FBCON_HAS_CFB32
case 32: case 32:
i->cmap.cmap32[regno]= ((u32*)(info->pseudo_palette))[regno] = v;
(((u32 )transp & 0xff00) << 16) |
(((u32 )red & 0xff00) << 8) |
(((u32 )green & 0xff00)) |
(((u32 )blue & 0xff00) >> 8);
break;
#endif
default:
DPRINTK("bad depth %u\n",
i->current_par.depth);
break; break;
} }
return 0;
} }
if (regno<256) { /* ... */
i->palette[regno].red=red >> 8; return 0;
i->palette[regno].green=green >> 8;
i->palette[regno].blue=blue >> 8;
i->palette[regno].transp=transp >> 8;
if (i->current_par.depth==8)
set_color(i, regno, red>>8, green>>8, blue>>8);
}
return regno>255;
} }
static void pm2fb_set_disp(const void* par, struct display* disp, /**
struct fb_info_gen* info) { * pm2fb_pan_display - Pans the display.
struct pm2fb_info* i=(struct pm2fb_info* )info; * @var: frame buffer variable screen structure
struct pm2fb_par* p=(struct pm2fb_par* )par; * @info: frame buffer structure that represents a single frame buffer
unsigned long flags; *
* Pan (or wrap, depending on the `vmode' field) the display using the
* `xoffset' and `yoffset' fields of the `var' structure.
* If the values don't fit, return -EINVAL.
*
* Returns negative errno on error, or zero on success.
*
*/
static int pm2fb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct pm2fb_par *p = (struct pm2fb_par *) info->par;
u32 base;
u32 depth;
u32 xres;
xres = (var->xres + 31) & ~31;
depth = (var->bits_per_pixel + 7) & ~7;
depth = (depth > 32) ? 32 : depth;
base = to3264(var->yoffset * xres + var->xoffset, depth, 1);
WAIT_FIFO(p, 1);
pm2_WR(p, PM2R_SCREEN_BASE, base);
return 0;
}
local_irq_save(flags); /**
#ifdef __alpha__ * pm2fb_blank - Blanks the display.
disp->screen_base=i->regions.v_fb + dense_mem(i->regions.v_fb); * @blank_mode: the blank mode we want.
#else * @info: frame buffer structure that represents a single frame buffer
disp->screen_base=i->regions.v_fb; *
#endif * Blank the screen if blank_mode != 0, else unblank. Return 0 if
switch (p->depth) { * blanking succeeded, != 0 if un-/blanking failed due to e.g. a
#ifdef FBCON_HAS_CFB8 * video mode which doesn't support it. Implements VESA suspend
case 8: * and powerdown modes on hardware that supports disabling hsync/vsync:
if (p->flags & PM2FF_ACCEL) * blank_mode == 2: suspend vsync
disp->dispsw=&pm2_cfb8; * blank_mode == 3: suspend hsync
else * blank_mode == 4: powerdown
disp->dispsw=&fbcon_cfb8; *
* Returns negative errno on error, or zero on success.
*
*/
static int pm2fb_blank(int blank_mode, struct fb_info *info)
{
struct pm2fb_par *par = (struct pm2fb_par *) info->par;
u32 video = par->video;
switch (blank_mode) {
case 0: /* Screen: On; HSync: On, VSync: On */
break; break;
#endif case 1: /* Screen: Off; HSync: On, VSync: On */
#ifdef FBCON_HAS_CFB16 video &= ~PM2F_VIDEO_ENABLE;
case 16:
if (p->flags & PM2FF_ACCEL)
disp->dispsw=&pm2_cfb16;
else
disp->dispsw=&fbcon_cfb16;
disp->dispsw_data=i->cmap.cmap16;
break; break;
#endif case 2: /* Screen: Off; HSync: On, VSync: Off */
#ifdef FBCON_HAS_CFB24 video &= ~(PM2F_VIDEO_ENABLE | PM2F_VSYNC_MASK | PM2F_BLANK_LOW );
case 24:
if (p->flags & PM2FF_ACCEL)
disp->dispsw=&pm2_cfb24;
else
disp->dispsw=&fbcon_cfb24;
disp->dispsw_data=i->cmap.cmap24;
break; break;
#endif case 3: /* Screen: Off; HSync: Off, VSync: On */
#ifdef FBCON_HAS_CFB32 video &= ~(PM2F_VIDEO_ENABLE | PM2F_HSYNC_MASK | PM2F_BLANK_LOW );
case 32:
if (p->flags & PM2FF_ACCEL)
disp->dispsw=&pm2_cfb32;
else
disp->dispsw=&fbcon_cfb32;
disp->dispsw_data=i->cmap.cmap32;
break; break;
#endif case 4: /* Screen: Off; HSync: Off, VSync: Off */
default: video &= ~(PM2F_VIDEO_ENABLE | PM2F_VSYNC_MASK | PM2F_HSYNC_MASK|
disp->dispsw=&fbcon_dummy; PM2F_BLANK_LOW);
break; break;
} }
local_irq_restore(flags); set_video(par, video);
return 0;
} }
#ifdef PM2FB_HW_CURSOR /* ------------ Hardware Independent Functions ------------ */
/***************************************************************************
* Hardware cursor support
***************************************************************************/
static u8 cursor_bits_lookup[16] = { /*
0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54, * Frame buffer operations
0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55 */
};
static u8 cursor_mask_lookup[16] = { static struct fb_ops pm2fb_ops = {
0x00, 0x80, 0x20, 0xa0, 0x08, 0x88, 0x28, 0xa8, .owner = THIS_MODULE,
0x02, 0x82, 0x22, 0xa2, 0x0a, 0x8a, 0x2a, 0xaa .fb_check_var = pm2fb_check_var,
.fb_set_par = pm2fb_set_par,
.fb_setcolreg = pm2fb_setcolreg,
.fb_blank = pm2fb_blank,
.fb_pan_display = pm2fb_pan_display,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_cursor = soft_cursor,
}; };
static void pm2v_set_cursor_color(struct pm2fb_info *fb, u8 *red, u8 *green, u8 *blue) /*
{ * PCI stuff
struct pm2_cursor *c = fb->cursor; */
int i;
for (i = 0; i < 2; i++) {
c->color[3*i] = red[i];
c->color[3*i+1] = green[i];
c->color[3*i+2] = blue[i];
}
WAIT_FIFO(fb, 14);
pm2_WR(fb, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CURSOR_PALETTE >> 8);
for (i = 0; i < 6; i++)
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PALETTE+i, c->color[i]);
pm2_WR(fb, PM2VR_RD_INDEX_HIGH, 0);
}
static void pm2v_set_cursor_shape(struct pm2fb_info *fb) /**
* Device initialisation
*
* Initialise and allocate resource for PCI device.
*
* @param pdev PCI device.
* @param id PCI device ID.
*/
static int __devinit pm2fb_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{ {
struct pm2_cursor *c = fb->cursor; struct pm2fb_par *default_par;
u8 m, b; struct fb_info *info;
int i, x, y; int size, err;
u32 pci_mem_config;
WAIT_FIFO(fb, 1); int err_retval = -ENXIO;
pm2_WR(fb, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CURSOR_PATTERN >> 8);
for (y = 0, i = 0; y < c->size.y; y++) { err = pci_enable_device(pdev);
WAIT_FIFO(fb, 32); if ( err ) {
for (x = 0; x < c->size.x >> 3; x++) { printk(KERN_WARNING "pm2fb: Can't enable pdev: %d\n", err);
m = c->mask[x][y]; return err;
b = c->bits[x][y];
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i,
cursor_mask_lookup[m >> 4] |
cursor_bits_lookup[(b & m) >> 4]);
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i + 1,
cursor_mask_lookup[m & 0x0f] |
cursor_bits_lookup[(b & m) & 0x0f]);
i+=2;
}
for ( ; x < 8; x++) {
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i, 0);
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i + 1, 0);
i+=2;
}
}
for (; y < 64; y++) {
WAIT_FIFO(fb, 32);
for (x = 0; x < 8; x++) {
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i, 0);
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i + 1, 0);
i+=2;
}
} }
WAIT_FIFO(fb, 1);
pm2_WR(fb, PM2VR_RD_INDEX_HIGH, 0);
}
static void pm2v_set_cursor(struct pm2fb_info *fb, int on) size = sizeof(struct fb_info) + sizeof(struct pm2fb_par) + 256 * sizeof(u32);
{
struct pm2_cursor *c = fb->cursor;
int x = c->pos.x;
if (!on) x = 4000;
WAIT_FIFO(fb, 14);
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_X_LOW, x & 0xff);
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_X_HIGH, (x >> 8) & 0x0f);
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_Y_LOW, c->pos.y & 0xff);
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_Y_HIGH, (c->pos.y >> 8) & 0x0f);
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_X_HOT, c->hot.x & 0x3f);
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_Y_HOT, c->hot.y & 0x3f);
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_MODE, 0x11);
}
static void pm2_cursor_timer_handler(unsigned long dev_addr) info = framebuffer_alloc(size, &pdev->dev);
{ if ( !info )
struct pm2fb_info *fb = (struct pm2fb_info *)dev_addr; return -ENOMEM;
memset(info, 0, size);
if (!fb->cursor->enable) default_par = info->par;
goto out;
if (fb->cursor->vbl_cnt && --fb->cursor->vbl_cnt == 0) { switch (pdev->device) {
fb->cursor->on ^= 1; case PCI_DEVICE_ID_TI_TVP4020:
pm2v_set_cursor(fb, fb->cursor->on); strcpy(pm2fb_fix.id, "TVP4020");
fb->cursor->vbl_cnt = fb->cursor->blink_rate; default_par->type = PM2_TYPE_PERMEDIA2;
break;
case PCI_DEVICE_ID_3DLABS_PERMEDIA2:
strcpy(pm2fb_fix.id, "Permedia2");
default_par->type = PM2_TYPE_PERMEDIA2;
break;
case PCI_DEVICE_ID_3DLABS_PERMEDIA2V:
strcpy(pm2fb_fix.id, "Permedia2v");
default_par->type = PM2_TYPE_PERMEDIA2V;
break;
} }
out: pm2fb_fix.mmio_start = pci_resource_start(pdev, 0);
fb->cursor->timer->expires = jiffies + (HZ / 50); pm2fb_fix.mmio_len = PM2_REGS_SIZE;
add_timer(fb->cursor->timer);
}
static void pm2fb_cursor(struct display *p, int mode, int x, int y)
{
struct pm2fb_info *fb = (struct pm2fb_info *)p->fb_info;
struct pm2_cursor *c = fb->cursor;
if (!c) return;
x *= fontwidth(p); #ifdef PM2FB_BE_APERTURE
y *= fontheight(p); pm2fb_fix.mmio_start += PM2_REGS_SIZE;
if (c->pos.x == x && c->pos.y == y && (mode == CM_ERASE) == !c->enable) #endif
return;
c->enable = 0; /* Registers - request region and map it. */
if (c->on) if ( !request_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len,
pm2v_set_cursor(fb, 0); "pm2fb regbase") ) {
c->pos.x = x; printk(KERN_WARNING "pm2fb: Can't reserve regbase.\n");
c->pos.y = y; goto err_exit_neither;
}
default_par->v_regs =
ioremap_nocache(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len);
if ( !default_par->v_regs ) {
printk(KERN_WARNING "pm2fb: Can't remap %s register area.\n",
pm2fb_fix.id);
release_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len);
goto err_exit_neither;
}
switch (mode) { /* Now work out how big lfb is going to be. */
case CM_ERASE: pci_mem_config = RD32(default_par->v_regs, PM2R_MEM_CONFIG);
c->on = 0; switch(pci_mem_config & PM2F_MEM_CONFIG_RAM_MASK) {
case PM2F_MEM_BANKS_1:
default_par->fb_size=0x200000;
break; break;
case PM2F_MEM_BANKS_2:
case CM_DRAW: default_par->fb_size=0x400000;
case CM_MOVE: break;
if (c->on) case PM2F_MEM_BANKS_3:
pm2v_set_cursor(fb, 1); default_par->fb_size=0x600000;
else break;
c->vbl_cnt = CURSOR_DRAW_DELAY; case PM2F_MEM_BANKS_4:
c->enable = 1; default_par->fb_size=0x800000;
break; break;
} }
} default_par->memclock = CVPPC_MEMCLOCK;
pm2fb_fix.smem_start = pci_resource_start(pdev, 1);
pm2fb_fix.smem_len = default_par->fb_size;
static struct pm2_cursor * __init pm2_init_cursor(struct pm2fb_info *fb) /* Linear frame buffer - request region and map it. */
{ if ( !request_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len,
struct pm2_cursor *cursor; "pm2fb smem") ) {
printk(KERN_WARNING "pm2fb: Can't reserve smem.\n");
goto err_exit_mmio;
}
info->screen_base = default_par->v_fb =
ioremap_nocache(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
if ( !default_par->v_fb ) {
printk(KERN_WARNING "pm2fb: Can't ioremap smem area.\n");
release_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
goto err_exit_mmio;
}
if (fb->type != PM2_TYPE_PERMEDIA2V) info->fbops = &pm2fb_ops;
return 0; /* FIXME: Support hw cursor everywhere */ info->fix = pm2fb_fix;
info->par = default_par;
info->pseudo_palette = (void *)(default_par + 1);
info->flags = FBINFO_FLAG_DEFAULT;
cursor = kmalloc(sizeof(struct pm2_cursor), GFP_ATOMIC); #ifndef MODULE
if (!cursor) if (!mode)
return 0; mode = "640x480@60";
memset(cursor, 0, sizeof(*cursor));
cursor->timer = kmalloc(sizeof(*cursor->timer), GFP_KERNEL); err = fb_find_mode(&info->var, info, mode, NULL, 0, NULL, 8);
if (!cursor->timer) { if (!err || err == 4)
kfree(cursor); #endif
return 0; info->var = pm2fb_var;
}
memset(cursor->timer, 0, sizeof(*cursor->timer));
cursor->blink_rate = DEFAULT_CURSOR_BLINK_RATE; if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
goto err_exit_all;
if (curblink) { if (register_framebuffer(info) < 0)
init_timer(cursor->timer); goto err_exit_both;
cursor->timer->expires = jiffies + (HZ / 50);
cursor->timer->data = (unsigned long)fb;
cursor->timer->function = pm2_cursor_timer_handler;
add_timer(cursor->timer);
}
return cursor; printk(KERN_INFO "fb%d: %s frame buffer device, memory = %dK.\n",
} info->node, info->fix.id, default_par->fb_size / 1024);
static int pm2fb_set_font(struct display *d, int width, int height) /*
{ * Our driver data
struct pm2fb_info *fb = (struct pm2fb_info *)d->fb_info; */
struct pm2_cursor *c = fb->cursor; pci_set_drvdata(pdev, info);
int i, j;
if (c) {
if (!width || !height) {
width = 8;
height = 16;
}
c->hot.x = 0; return 0;
c->hot.y = 0;
c->size.x = width; err_exit_all:
c->size.y = height; fb_dealloc_cmap(&info->cmap);
err_exit_both:
iounmap((void*) pm2fb_fix.smem_start);
release_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
err_exit_mmio:
iounmap((void*) pm2fb_fix.mmio_start);
release_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len);
err_exit_neither:
framebuffer_release(info);
return err_retval;
}
/**
* Device removal.
*
* Release all device resources.
*
* @param pdev PCI device to clean up.
*/
static void __devexit pm2fb_remove(struct pci_dev *pdev)
{
struct fb_info* info = pci_get_drvdata(pdev);
struct fb_fix_screeninfo* fix = &info->fix;
memset(c->bits, 0xff, sizeof(c->bits)); unregister_framebuffer(info);
memset(c->mask, 0, sizeof(c->mask));
for (i = 0, j = width; j >= 0; j -= 8, i++) { iounmap((void*) fix->smem_start);
c->mask[i][height-2] = (j >= 8) ? 0xff : (0xff << (8 - j)); release_mem_region(fix->smem_start, fix->smem_len);
c->mask[i][height-1] = (j >= 8) ? 0xff : (0xff << (8 - j)); iounmap((void*) fix->mmio_start);
} release_mem_region(fix->mmio_start, fix->mmio_len);
pm2v_set_cursor_color(fb, cursor_color_map, cursor_color_map, cursor_color_map); pci_set_drvdata(pdev, NULL);
pm2v_set_cursor_shape(fb); kfree(info);
}
return 1;
} }
#endif /* PM2FB_HW_CURSOR */
/*************************************************************************** static struct pci_device_id pm2fb_id_table[] = {
* Begin of public functions { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TVP4020,
***************************************************************************/ PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16,
0xff0000, 0 },
{ PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2,
PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16,
0xff0000, 0 },
{ PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2V,
PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16,
0xff0000, 0 },
{ 0, }
};
#ifdef MODULE static struct pci_driver pm2fb_driver = {
static void pm2fb_cleanup(void) { .name = "pm2fb",
struct pm2fb_info* i=&fb_info; .id_table = pm2fb_id_table,
.probe = pm2fb_probe,
.remove = __devexit_p(pm2fb_remove),
};
unregister_framebuffer((struct fb_info* )info); MODULE_DEVICE_TABLE(pci, pm2fb_id_table);
pm2fb_reset(i);
UNMAP(i->regions.v_fb, i->regions.fb_size);
release_mem_region(i->regions.p_fb, i->regions.fb_size);
UNMAP(i->regions.v_regs, PM2_REGS_SIZE); /*
release_mem_region(i->regions.p_regs, PM2_REGS_SIZE); * Initialization
*/
if (board_table[i->board].cleanup) int __init pm2fb_init(void)
board_table[i->board].cleanup(i); {
return pci_module_init(&pm2fb_driver);
} }
#endif /* MODULE */
int __init pm2fb_init(void) {
MOD_INC_USE_COUNT; /*
memset(&fb_info, 0, sizeof(fb_info)); * Cleanup
*/
if (!pm2fb_conf(&fb_info)) {
MOD_DEC_USE_COUNT;
return -ENXIO;
}
/* Pick up user_var settings if set. */ static void __exit pm2fb_exit(void)
if ((pm2fb_options.flags & OPTF_USER_VAR) && {
pm2fb_decode_var(&user_var, &pm2fb_options.user_mode, pci_unregister_driver(&pm2fb_driver);
&fb_info.gen)<0) {
printk("pm2fb: user supplied var: mode is bad.\n");
memcpy(&pm2fb_options.user_mode,
&user_mode[DEFAULT_USER_MODE].par,
sizeof(struct pm2fb_par));
}
memcpy(&fb_info.current_par, &pm2fb_options.user_mode,
sizeof(fb_info.current_par));
pm2fb_reset(&fb_info);
fb_info.disp.scrollmode=SCROLL_YNOMOVE;
fb_info.gen.parsize=sizeof(struct pm2fb_par);
fb_info.gen.fbhw=&pm2fb_hwswitch;
strcpy(fb_info.gen.info.modename, permedia2_name);
fb_info.gen.info.flags=FBINFO_FLAG_DEFAULT;
fb_info.gen.info.fbops=&pm2fb_ops;
fb_info.gen.info.disp=&fb_info.disp;
strcpy(fb_info.gen.info.fontname, pm2fb_options.font);
fb_info.gen.info.switch_con=&fbgen_switch;
fb_info.gen.info.updatevar=&fbgen_update_var;
fbgen_get_var(&fb_info.disp.var, -1, &fb_info.gen.info);
fbgen_do_set_var(&fb_info.disp.var, 1, &fb_info.gen);
fbgen_set_disp(-1, &fb_info.gen);
fbgen_install_cmap(0, &fb_info.gen);
if (register_framebuffer(&fb_info.gen.info)<0) {
printk(KERN_ERR "pm2fb: unable to register.\n");
MOD_DEC_USE_COUNT;
return -EINVAL;
}
printk(KERN_INFO "fb%d: %s (%s), using %uK of video memory.\n",
fb_info.gen.info.node,
board_table[fb_info.board].name,
permedia2_name,
(u32 )(fb_info.regions.fb_size>>10));
return 0;
} }
static void __init pm2fb_mode_setup(char* options) { /*
int i; * Setup
*/
for (i=0; user_mode[i].name[0] && /**
strcmp(options, user_mode[i].name); i++); * Parse user speficied options.
if (user_mode[i].name[0]) { *
memcpy(&pm2fb_options.user_mode, &user_mode[i].par, * This is, comma-separated options following `video=pm2fb:'.
sizeof(pm2fb_options.user_mode)); */
pm2fb_options.flags|=OPTF_USER; int __init pm2fb_setup(char *options)
} {
} char* this_opt;
static void __init pm2fb_font_setup(char* options) { if (!options || !*options)
strlcpy(pm2fb_options.font, options, sizeof(pm2fb_options.font)); return 0;
}
static void __init pm2fb_var_setup(char* options) { while ((this_opt = strsep(&options, ",")) != NULL) {
char* next; if (!*this_opt)
continue;
pm2fb_par2var(&user_var, &pm2fb_options.user_mode); if(!strcmp(this_opt, "lowhsync")) {
lowhsync = 1;
while (options) { } else if(!strcmp(this_opt, "lowvsync")) {
if ((next=strchr(options, ';'))) lowvsync = 1;
*(next++)='\0'; } else {
if (!strncmp(options, "bpp:", 4)) mode = this_opt;
user_var.bits_per_pixel=
simple_strtoul(options+4, NULL, 0);
else if (!strncmp(options, "xres:", 5))
user_var.xres=simple_strtoul(options+5, NULL, 0);
else if (!strncmp(options, "yres:", 5))
user_var.yres=simple_strtoul(options+5, NULL, 0);
else if (!strncmp(options, "vxres:", 6))
user_var.xres_virtual=
simple_strtoul(options+6, NULL, 0);
else if (!strncmp(options, "vyres:", 6))
user_var.yres_virtual=
simple_strtoul(options+6, NULL, 0);
else if (!strncmp(options, "left:", 5))
user_var.left_margin=
simple_strtoul(options+5, NULL, 0);
else if (!strncmp(options, "right:", 6))
user_var.right_margin=
simple_strtoul(options+6, NULL, 0);
else if (!strncmp(options, "lower:", 6))
user_var.lower_margin=
simple_strtoul(options+6, NULL, 0);
else if (!strncmp(options, "upper:", 6))
user_var.upper_margin=
simple_strtoul(options+6, NULL, 0);
else if (!strncmp(options, "hslen:", 6))
user_var.hsync_len=simple_strtoul(options+6, NULL, 0);
else if (!strncmp(options, "vslen:", 6))
user_var.vsync_len=simple_strtoul(options+6, NULL, 0);
else if (!strncmp(options, "pixclock:", 9))
user_var.pixclock=simple_strtoul(options+9, NULL, 0);
else if (!strcmp(options, "+hsync"))
user_var.sync|=FB_SYNC_HOR_HIGH_ACT;
else if (!strcmp(options, "-hsync"))
user_var.sync&=~FB_SYNC_HOR_HIGH_ACT;
else if (!strcmp(options, "+vsync"))
user_var.sync|=FB_SYNC_VERT_HIGH_ACT;
else if (!strcmp(options, "-vsync"))
user_var.sync&=~FB_SYNC_VERT_HIGH_ACT;
else if (!strcmp(options, "+double"))
user_var.vmode|=FB_VMODE_DOUBLE;
else if (!strcmp(options, "-double"))
user_var.vmode&=~FB_VMODE_DOUBLE;
else if (!strcmp(options, "+accel"))
user_var.accel_flags|=FB_ACCELF_TEXT;
else if (!strcmp(options, "-accel"))
user_var.accel_flags&=~FB_ACCELF_TEXT;
options=next;
} }
pm2fb_options.flags|=OPTF_USER_VAR;
}
int __init pm2fb_setup(char* options) {
char* next;
while (options) {
if ((next=strchr(options, ',')))
*(next++)='\0';
if (!strncmp(options, "font:", 5))
pm2fb_font_setup(options+5);
else if (!strncmp(options, "mode:", 5))
pm2fb_mode_setup(options+5);
else if (!strncmp(options, "var:", 4))
pm2fb_var_setup(options+4);
else if (!strcmp(options, "ypan"))
pm2fb_options.flags |= OPTF_YPAN;
else if (!strcmp(options, "oldmem"))
pm2fb_options.flags |= OPTF_OLD_MEM;
else if (!strcmp(options, "virtual"))
pm2fb_options.flags |= OPTF_VIRTUAL;
else if (!strcmp(options, "lowhsync"))
pm2fb_options.flags |= OPTF_LOW_HSYNC;
else if (!strcmp(options, "lowvsync"))
pm2fb_options.flags |= OPTF_LOW_VSYNC;
else if (!strcmp(options, "noblink"))
curblink=0;
options=next;
} }
user_var.activate=FB_ACTIVATE_NOW;
return 0; return 0;
} }
/***************************************************************************
* Begin of module functions
***************************************************************************/
#ifdef MODULE /* ------------------------------------------------------------------------- */
MODULE_LICENSE("GPL"); /* ------------------------------------------------------------------------- */
static char *mode = NULL;
MODULE_PARM(mode, "s");
int init_module(void) { #ifdef MODULE
module_init(pm2fb_init);
if (mode) #endif
pm2fb_mode_setup(mode); module_exit(pm2fb_exit);
return pm2fb_init();
}
void cleanup_module(void) {
pm2fb_cleanup(); MODULE_PARM(mode,"s");
} MODULE_PARM(lowhsync,"i");
#endif /* MODULE */ MODULE_PARM(lowvsync,"i");
/*************************************************************************** MODULE_AUTHOR("Jim Hague <jim.hague@acm.org>");
* That's all folks! MODULE_DESCRIPTION("Permedia2 framebuffer device driver");
***************************************************************************/ MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment