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
...@@ -74,22 +60,9 @@ ...@@ -74,22 +60,9 @@
#define DPRINTK(a,b...) printk(KERN_DEBUG "pm2fb: %s: " a, __FUNCTION__ , ## b) #define DPRINTK(a,b...) printk(KERN_DEBUG "pm2fb: %s: " a, __FUNCTION__ , ## b)
#else #else
#define DPRINTK(a,b...) #define DPRINTK(a,b...)
#endif #endif
#define PICOS2KHZ(a) (1000000000UL/(a))
#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)
/* /* Memory barriers. */
* 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";
static struct pm2fb_info {
struct fb_info_gen gen;
int board; /* Permedia2 board index (see
board_table[] below) */
pm2type_t type; /* Permedia2 board type */
struct {
unsigned long fb_base; /* physical framebuffer memory base */
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 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 */
} regions;
union { /* here, the per-board par structs */
#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); * The XFree GLINT driver will (I think to implement hardware cursor
static int pm2fb_set_font(struct display *d, int width, int height); * support on TVP4010 and similar where there is no RAMDAC - see
static struct pm2_cursor *pm2_init_cursor(struct pm2fb_info *fb); * comment in set_video) always request +ve sync regardless of what
static void pm2v_set_cursor_color(struct pm2fb_info *fb, u8 *red, u8 *green, u8 *blue); * the mode requires. This screws me because I have a Sun
static void pm2v_set_cursor_shape(struct pm2fb_info *fb); * fixed-frequency monitor which absolutely has to have -ve sync. So
static u8 cursor_color_map[2] = { 0, 0xff }; * these flags allow the user to specify that requests for +ve sync
#else * should be silently turned in -ve sync.
#define pm2fb_cursor NULL */
#define pm2fb_set_font NULL static int lowhsync __initdata = 0;
#endif static int lowvsync __initdata = 0;
/* /*
* Table of the supported Permedia2 based boards. * The hardware state of the graphics card that isn't part of the
* Three hooks are defined for each board: * screeninfo.
* 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 { struct pm2fb_par
int (*detect)(struct pm2fb_info*); {
void (*init)(struct pm2fb_info*); pm2type_t type; /* Board type */
void (*cleanup)(struct pm2fb_info*); u32 fb_size; /* framebuffer memory size */
char name[32]; unsigned char* v_fb; /* virtual address of frame buffer */
} board_table[] = { unsigned char* v_regs; /* virtual address of p_regs */
#ifdef CONFIG_FB_PM2_PCI u32 memclock; /* memclock */
{ pm2pci_detect, pm2pci_init, NULL, "Permedia2 PCI board" }, u32 video; /* video flags before blanking */
#endif
#ifdef CONFIG_FB_PM2_CVPPC
{ cvppc_detect, cvppc_init, NULL, "CVisionPPC/BVisionPPC" },
#endif
{ NULL, }
}; };
/* /*
* partial products for the supported horizontal resolutions. * Here we define the default structs fb_fix_screeninfo and fb_var_screeninfo
* if we don't use modedb.
*/ */
#define PACKPP(p0,p1,p2) (((p2)<<6)|((p1)<<3)|(p0)) static struct fb_fix_screeninfo pm2fb_fix __initdata = {
static const struct { .id = "",
u16 width; .type = FB_TYPE_PACKED_PIXELS,
u16 pp; .visual = FB_VISUAL_PSEUDOCOLOR,
} pp_table[] = { .xpanstep = 1,
{ 32, PACKPP(1, 0, 0) }, { 64, PACKPP(1, 1, 0) }, .ypanstep = 1,
{ 96, PACKPP(1, 1, 1) }, { 128, PACKPP(2, 1, 1) }, .ywrapstep = 0,
{ 160, PACKPP(2, 2, 1) }, { 192, PACKPP(2, 2, 2) }, .accel = FB_ACCEL_NONE,
{ 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 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, * Default video mode. In case the modedb doesn't work, or we're
unsigned red, unsigned green, unsigned blue, * a module (in which case modedb doesn't really work).
unsigned transp, struct fb_info* info); */
static struct fb_var_screeninfo pm2fb_var __initdata = {
static struct fb_ops pm2fb_ops={ /* "640x480, 8 bpp @ 60 Hz */
.owner = THIS_MODULE, .xres = 640,
.fb_get_fix = fbgen_get_fix, .yres = 480,
.fb_get_var = fbgen_get_var, .xres_virtual = 640,
.fb_set_var = fbgen_set_var, .yres_virtual = 480,
.fb_get_cmap = fbgen_get_cmap, .bits_per_pixel =8,
.fb_set_cmap = fbgen_set_cmap, .red = {0, 8, 0},
.fb_pan_display =fbgen_pan_display, .blue = {0, 8, 0},
.fb_setcolreg = pm2fb_setcolreg, .green = {0, 8, 0},
.fb_blank = fbgen_blank, .activate = FB_ACTIVATE_NOW,
.height = -1,
.width = -1,
.accel_flags = 0,
.pixclock = 39721,
.left_margin = 40,
.right_margin = 24,
.upper_margin = 32,
.lower_margin = 11,
.hsync_len = 96,
.vsync_len = 2,
.vmode = FB_VMODE_NONINTERLACED
}; };
/*************************************************************************** /*
* 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,300 +351,38 @@ static void clear_palette(struct pm2fb_info* p) { ...@@ -686,300 +351,38 @@ 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 reset_card(struct pm2fb_par* p)
} {
if (p->type == PM2_TYPE_PERMEDIA2V)
static void set_aperture(struct pm2fb_info* i, struct pm2fb_par* p) { pm2_WR(p, PM2VR_RD_INDEX_HIGH, 0);
pm2_WR(p, PM2R_RESET_STATUS, 0);
WAIT_FIFO(i, 4); DEFRW();
#ifdef __LITTLE_ENDIAN while (pm2_RD(p, PM2R_RESET_STATUS) & PM2F_BEING_RESET)
pm2_WR(i, PM2R_APERTURE_ONE, 0); ;
pm2_WR(i, PM2R_APERTURE_TWO, 0); DEFRW();
#else #ifdef CONFIG_FB_PM2_FIFO_DISCONNECT
switch (p->depth) { DPRINTK("FIFO disconnect enabled\n");
case 8: pm2_WR(p, PM2R_FIFO_DISCON, 1);
case 24: DEFRW();
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 #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);
vsync|=PM2F_HSYNC_ACT_HIGH|PM2F_VSYNC_ACT_HIGH;
WAIT_FIFO(i, 5);
pm2_WR(i, PM2R_VIDEO_CONTROL, vsync);
switch (i->type) {
case PM2_TYPE_PERMEDIA2:
tmp = PM2F_RD_PALETTE_WIDTH_8;
if ((video & PM2F_HSYNC_MASK) == PM2F_HSYNC_ACT_LOW)
tmp |= 4; /* invert hsync */
if ((video & PM2F_VSYNC_MASK) == PM2F_VSYNC_ACT_LOW)
tmp |= 8; /* invert vsync */
pm2_RDAC_WR(i, PM2I_RD_MISC_CONTROL, tmp);
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;
}
}
static void get_screen(struct pm2fb_info* i, 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 #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) {
static void reset_config(struct pm2fb_par* 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));
pm2_WR(p, PM2R_BYPASS_WRITE_MASK, ~(0L)); pm2_WR(p, PM2R_BYPASS_WRITE_MASK, ~(0L));
pm2_WR(p, PM2R_FRAMEBUFFER_WRITE_MASK, ~(0L)); pm2_WR(p, PM2R_FRAMEBUFFER_WRITE_MASK, ~(0L));
pm2_WR(p, PM2R_FIFO_CONTROL, 0); pm2_WR(p, PM2R_FIFO_CONTROL, 0);
...@@ -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)); switch (par->type) {
DPRINTK("scanning PCI bus for known chipsets...\n"); case PM2_TYPE_PERMEDIA2:
pm2_mnp(clk, &m, &n, &p);
while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { WAIT_FIFO(par, 8);
for (i = 0; pm2pci_cards[i].vendor; i++) pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A3, 0);
if (pm2pci_cards[i].vendor == dev->vendor && DEFW();
pm2pci_cards[i].device == dev->device) { pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A1, m);
pci->dev = dev; pm2_RDAC_WR(par, PM2I_RD_PIXEL_CLOCK_A2, n);
p->type = pm2pci_cards[i].type; DEFW();
DPRINTK("... found %s\n", pm2pci_cards[i].name); 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();
break; for (i = 256;
} i && !(pm2_RD(par, PM2R_RD_INDEXED_DATA) & PM2F_PLL_LOCKED);
if (!pci->dev) { i--)
DPRINTK("no PCI board found.\n"); ;
return 0; break;
} case PM2_TYPE_PERMEDIA2V:
DPRINTK("PCI board @%08lx %08lx %08lx rom %08lx\n", pm2v_mnp(clk/2, &m, &n, &p);
pci->dev->resource[0].start, WAIT_FIFO(par, 8);
pci->dev->resource[1].start, pm2_WR(par, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CLK0_PRESCALE >> 8);
pci->dev->resource[2].start, pm2v_RDAC_WR(par, PM2VI_RD_CLK0_PRESCALE, m);
pci->dev->resource[PCI_ROM_RESOURCE].start); pm2v_RDAC_WR(par, PM2VI_RD_CLK0_FEEDBACK, n);
#ifdef __sparc__ pm2v_RDAC_WR(par, PM2VI_RD_CLK0_POSTSCALE, p);
p->regions.rg_base= pci->dev->resource[0].start; pm2_WR(par, PM2VR_RD_INDEX_HIGH, 0);
p->regions.fb_base= pci->dev->resource[1].start; break;
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;
case PM2F_MEM_BANKS_2:
p->regions.fb_size=0x400000;
break;
case PM2F_MEM_BANKS_3:
p->regions.fb_size=0x600000;
break;
case PM2F_MEM_BANKS_4:
p->regions.fb_size=0x800000;
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);
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 */
/*************************************************************************** vsync = video;
* 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; break;
case VESA_HSYNC_SUSPEND: case PM2_TYPE_PERMEDIA2V:
video=video&~(PM2F_HSYNC_MASK| tmp = 0;
PM2F_BLANK_LOW); if ((video & PM2F_HSYNC_MASK) == PM2F_HSYNC_ACT_LOW)
break; tmp |= 1; /* invert hsync */
case VESA_VSYNC_SUSPEND: if ((video & PM2F_VSYNC_MASK) == PM2F_VSYNC_ACT_LOW)
video=video&~(PM2F_VSYNC_MASK| tmp |= 4; /* invert vsync */
PM2F_BLANK_LOW); pm2v_RDAC_WR(p, PM2VI_RD_SYNC_CONTROL, tmp);
break; pm2v_RDAC_WR(p, PM2VI_RD_MISC_CONTROL, 1);
case VESA_POWERDOWN: break;
video=video&~(PM2F_VSYNC_MASK|
PM2F_HSYNC_MASK|
PM2F_BLANK_LOW);
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
static void pm2fb_clear8(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=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,
int bottom_only) {
u32 c;
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 = {
.setup = fbcon_cfb8_setup,
.bmove = pm2fb_pp_bmove,
#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,
int bottom_only) {
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->yres > var->yres_virtual) {
.setup = fbcon_cfb16_setup, DPRINTK("virtual y resolution < physical y resolution not possible\n");
.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
/*
* fast fill for 24bpp works only when red==green==blue
*/
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);
}
static void pm2fb_clear_margins24(struct vc_data* conp, struct display* p,
int bottom_only) {
struct pm2fb_info* i=(struct pm2fb_info* )p->fb_info;
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 = {
.setup = fbcon_cfb24_setup,
.bmove = pm2fb_bmove,
.clear = pm2fb_clear24,
.putc = fbcon_cfb24_putc,
.putcs = fbcon_cfb24_putcs,
.revc = fbcon_cfb24_revc,
.cursor = pm2fb_cursor,
.set_font = pm2fb_set_font,
.clear_margins =pm2fb_clear_margins24,
.fontwidthmask =FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
};
#endif /* FBCON_HAS_CFB24 */
#ifdef FBCON_HAS_CFB32
static void pm2fb_clear32(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=((u32 *)p->dispsw_data)[attr_bgcol_ec(p, conp)];
pm2fb_block_op((struct pm2fb_info* )p->fb_info, 0, 0, 0, sx, sy,
width, height, c);
}
static void pm2fb_clear_margins32(struct vc_data* conp, struct display* p, if (var->xoffset) {
int bottom_only) { DPRINTK("xoffset not supported\n");
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 = {
.setup = fbcon_cfb32_setup,
.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;
}
#ifdef PM2FB_MASTER_DEBUG
static void pm2fb_display_var(const struct fb_var_screeninfo* var) {
printk( KERN_DEBUG
"- struct fb_var_screeninfo ---------------------------------------------------\n");
printk( KERN_DEBUG
"resolution: %ux%ux%u (virtual %ux%u+%u+%u)\n",
var->xres, var->yres, var->bits_per_pixel,
var->xres_virtual, var->yres_virtual,
var->xoffset, var->yoffset);
printk( KERN_DEBUG
"color: %c%c "
"R(%u,%u,%u), G(%u,%u,%u), B(%u,%u,%u), T(%u,%u,%u)\n",
var->grayscale?'G':'C', var->nonstd?'N':'S',
var->red.offset, var->red.length, var->red.msb_right,
var->green.offset, var->green.length, var->green.msb_right,
var->blue.offset, var->blue.length, var->blue.msb_right,
var->transp.offset, var->transp.length,
var->transp.msb_right);
printk( KERN_DEBUG
"timings: %ups (%u,%u)-(%u,%u)+%u+%u\n",
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;
int data64;
memset(&p, 0, sizeof(struct pm2fb_par));
if (var->accel_flags & FB_ACCELF_TEXT)
p.flags |= PM2FF_ACCEL;
p.width=(var->xres_virtual+7)&~7;
p.height=var->yres_virtual;
p.depth=(var->bits_per_pixel+7)&~7;
p.depth=p.depth>32?32:p.depth;
data64=p.depth>8 || i->type==PM2_TYPE_PERMEDIA2V;
xres=(var->xres+31)&~31;
if (p.width<xres+var->xoffset)
p.width=xres+var->xoffset;
if (p.height<var->yres+var->yoffset)
p.height=var->yres+var->yoffset;
if (!partprod(xres)) {
DPRINTK("width not supported: %u\n", xres);
return -EINVAL; return -EINVAL;
} }
if (p.width>2047) {
DPRINTK("virtual width not supported: %u\n", p.width); if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
DPRINTK("interlace not supported\n");
return -EINVAL; return -EINVAL;
} }
if (var->yres<200) {
DPRINTK("height not supported: %u\n", var->xres = (var->xres + 15) & ~15; /* could sometimes be 8 */
(u32 )var->yres); lpitch = var->xres * ((var->bits_per_pixel + 7)>>3);
if (var->xres < 320 || var->xres > 1600) {
DPRINTK("width not supported: %u\n", var->xres);
return -EINVAL; return -EINVAL;
} }
if (p.height<200 || p.height>2047) {
DPRINTK("virtual height not supported: %u\n", p.height); if (var->yres < 200 || var->yres > 1200) {
DPRINTK("height not supported: %u\n", var->yres);
return -EINVAL; return -EINVAL;
} }
if (p.depth>32) {
DPRINTK("depth not supported: %u\n", p.depth); 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; return -EINVAL;
} }
if (p.width*p.height*p.depth/8>i->regions.fb_size) {
DPRINTK("no memory for screen (%ux%ux%u)\n", if (PICOS2KHZ(var->pixclock) > PM2_MAX_PIXCLOCK) {
p.width, p.height, p.depth); DPRINTK("pixclock too high (%ldKHz)\n", PICOS2KHZ(var->pixclock));
return -EINVAL; return -EINVAL;
} }
p.pixclock=PICOS2KHZ(var->pixclock);
if (p.pixclock>PM2_MAX_PIXCLOCK) { switch(var->bits_per_pixel) {
DPRINTK("pixclock too high (%uKHz)\n", p.pixclock); case 8:
var->red.length = var->green.length = var->blue.length = 8;
break;
case 16:
var->red.offset = 11;
var->red.length = 5;
var->green.offset = 5;
var->green.length = 6;
var->blue.offset = 0;
var->blue.length = 5;
break;
case 24:
var->red.offset = 16;
var->green.offset = 8;
var->blue.offset = 0;
var->red.length = var->green.length = var->blue.length = 8;
case 32:
var->red.offset = 16;
var->green.offset = 8;
var->blue.offset = 0;
var->red.length = var->green.length = var->blue.length = 8;
break;
}
var->height = var->width = -1;
var->accel_flags = 0; /* Can't mmap if this is on */
DPRINTK("Checking graphics mode at %dx%d depth %d\n",
var->xres, var->yres, var->bits_per_pixel);
return 0;
}
/**
* pm2fb_set_par - Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
*
* Using the fb_var_screeninfo in fb_info we set the resolution of the
* this particular framebuffer.
*/
static int pm2fb_set_par(struct fb_info *info)
{
struct pm2fb_par *par = (struct pm2fb_par *) info->par;
u32 pixclock;
u32 width, height, depth;
u32 hsstart, hsend, hbend, htotal;
u32 vsstart, vsend, vbend, vtotal;
u32 stride;
u32 base;
u32 video = 0;
u32 clrmode = PM2F_RD_COLOR_MODE_RGB;
u32 txtmap = 0;
u32 pixsize = 0;
u32 clrformat = 0;
u32 xres;
int data64;
reset_config(par);
clear_palette(par);
width = (info->var.xres_virtual + 7) & ~7;
height = info->var.yres_virtual;
depth = (info->var.bits_per_pixel + 7) & ~7;
depth = (depth > 32) ? 32 : depth;
data64 = depth > 8 || par->type == PM2_TYPE_PERMEDIA2V;
xres = (info->var.xres + 31) & ~31;
pixclock = PICOS2KHZ(info->var.pixclock);
if (pixclock > PM2_MAX_PIXCLOCK) {
DPRINTK("pixclock too high (%uKHz)\n", 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,
void* par, struct fb_info_gen* info) {
int result;
result=pm2fb_wrapped_decode_var(var, par, info);
pm2fb_display_var(var);
return result;
}
#endif
static void pm2fb_par2var(struct fb_var_screeninfo* v,
const struct pm2fb_par* p) {
u32 base;
memset(v, 0, sizeof(struct fb_var_screeninfo));
if (p->flags & PM2FF_ACCEL)
v->accel_flags |= FB_ACCELF_TEXT;
v->xres_virtual=p->width;
v->yres_virtual=p->height;
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:
v->red.length=v->green.length=v->blue.length=8;
v->xres=v->xres<<2;
v->right_margin=v->right_margin<<2;
v->hsync_len=v->hsync_len<<2;
v->left_margin=v->left_margin<<2;
break;
case 16:
v->red.offset=11;
v->red.length=5;
v->green.offset=5;
v->green.length=6;
v->blue.length=5;
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;
case 32:
v->transp.offset=24;
v->red.offset=16;
v->green.offset=8;
v->red.length=v->green.length=v->blue.length=
v->transp.length=8;
break;
case 24:
v->blue.offset=16;
v->green.offset=8;
v->red.length=v->green.length=v->blue.length=8;
v->xres=(v->xres<<2)/3;
v->right_margin=(v->right_margin<<2)/3;
v->hsync_len=(v->hsync_len<<2)/3;
v->left_margin=(v->left_margin<<2)/3;
break;
}
base=from3264(p->base, p->depth, 1);
v->xoffset=base%v->xres;
v->yoffset=base/v->xres;
v->height=v->width=-1;
v->pixclock=KHZ2PICOS(p->pixclock);
if ((p->video & PM2F_HSYNC_MASK)==PM2F_HSYNC_ACT_HIGH)
v->sync|=FB_SYNC_HOR_HIGH_ACT;
if ((p->video & PM2F_VSYNC_MASK)==PM2F_VSYNC_ACT_HIGH)
v->sync|=FB_SYNC_VERT_HIGH_ACT;
if (p->video & PM2F_LINE_DOUBLE)
v->vmode=FB_VMODE_DOUBLE;
}
static int pm2fb_encode_var(struct fb_var_screeninfo* var,
const void* par, struct fb_info_gen* info) {
pm2fb_par2var(var, (struct pm2fb_par* )par);
return 0;
}
static void set_user_mode(struct pm2fb_info* i) {
memcpy(&i->current_par, &pm2fb_options.user_mode,
sizeof(i->current_par));
if (pm2fb_options.flags & OPTF_YPAN) {
i->current_par.height=i->regions.fb_size/
(i->current_par.width*i->current_par.depth/8);
i->current_par.height=MIN(i->current_par.height,2047);
i->current_par.height=MAX(i->current_par.height,
pm2fb_options.user_mode.height);
}
}
static void pm2fb_get_par(void* par, struct fb_info_gen* info) { info->fix.visual =
struct pm2fb_info* i=(struct pm2fb_info* )info; (depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
info->fix.line_length =
info->var.xres * ((info->var.bits_per_pixel + 7) >> 3);
info->cmap.len = 256;
if (!i->current_par_valid) { /*
set_user_mode(i); * Settings calculated. Now write them out.
pm2fb_set_par(&i->current_par, info); */
if (par->type == PM2_TYPE_PERMEDIA2V) {
WAIT_FIFO(par, 1);
pm2_WR(par, PM2VR_RD_INDEX_HIGH, 0);
} }
get_screen(i, (struct pm2fb_par* )par);
} set_aperture(par);
static void pm2fb_set_par(const void* par, struct fb_info_gen* info) { DEFRW();
struct pm2fb_info* i=(struct pm2fb_info* )info; WAIT_FIFO(par, 19);
struct pm2fb_par* p=(struct pm2fb_par* )par; pm2_RDAC_WR(par, PM2I_RD_COLOR_KEY_CONTROL,
( depth == 8 ) ? 0 : PM2F_COLOR_KEY_TEST_OFF);
if (screen_is_valid(i)) { switch (depth) {
i->current_par.base=p->base; case 8:
if (!memcmp(p, &i->current_par, sizeof(struct pm2fb_par))) { pm2_WR(par, PM2R_FB_READ_PIXEL, 0);
WAIT_FIFO(i, 1); clrformat = 0x0e;
pm2_WR(i, PM2R_SCREEN_BASE, p->base); break;
return; case 16:
} pm2_WR(par, PM2R_FB_READ_PIXEL, 1);
clrmode |= PM2F_RD_TRUECOLOR | 0x06;
txtmap = PM2F_TEXTEL_SIZE_16;
pixsize = 1;
clrformat = 0x70;
break;
case 32:
pm2_WR(par, PM2R_FB_READ_PIXEL, 2);
clrmode |= PM2F_RD_TRUECOLOR | 0x08;
txtmap = PM2F_TEXTEL_SIZE_32;
pixsize = 2;
clrformat = 0x20;
break;
case 24:
pm2_WR(par, 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(par, PM2R_FB_WRITE_MODE, PM2F_FB_WRITE_ENABLE);
wait_pm2(i); pm2_WR(par, PM2R_FB_READ_MODE, partprod(xres));
reset_units(i); pm2_WR(par, PM2R_LB_READ_MODE, partprod(xres));
set_screen(i, p); pm2_WR(par, PM2R_TEXTURE_MAP_FORMAT, txtmap | partprod(xres));
i->current_par=*p; pm2_WR(par, PM2R_H_TOTAL, htotal);
i->current_par_valid=1; pm2_WR(par, PM2R_HS_START, hsstart);
#ifdef PM2FB_HW_CURSOR pm2_WR(par, PM2R_HS_END, hsend);
if (i->cursor) { pm2_WR(par, PM2R_HG_END, hbend);
pm2v_set_cursor_color(i, cursor_color_map, pm2_WR(par, PM2R_HB_END, hbend);
cursor_color_map, cursor_color_map); pm2_WR(par, PM2R_V_TOTAL, vtotal);
pm2v_set_cursor_shape(i); 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);
DEFW();
pm2_WR(par, PM2R_WINDOW_ORIGIN, 0);
pm2_WR(par, PM2R_SCREEN_SIZE, (height << 16) | width);
pm2_WR(par, PM2R_SCISSOR_MODE, PM2F_SCREEN_SCISSOR_ENABLE);
DEFW();
pm2_WR(par, PM2R_SCREEN_BASE, base);
DEFW();
set_video(par, video);
WAIT_FIFO(par, 4);
switch (par->type) {
case PM2_TYPE_PERMEDIA2:
pm2_RDAC_WR(par, PM2I_RD_COLOR_MODE,
PM2F_RD_COLOR_MODE_RGB | PM2F_RD_GUI_ACTIVE | clrmode);
break;
case PM2_TYPE_PERMEDIA2V:
pm2v_RDAC_WR(par, PM2VI_RD_PIXEL_SIZE, pixsize);
pm2v_RDAC_WR(par, PM2VI_RD_COLOR_FORMAT, clrformat);
break;
} }
#endif set_pixclock(par, pixclock);
} return 0;
}
/**
* pm2fb_setcolreg - Sets a color register.
* @regno: boolean, 0 copy local, 1 get_user() function
* @red: frame buffer colormap structure
* @green: The green value which can be up to 16 bits wide
* @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.
* @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 int pm2fb_getcolreg(unsigned regno, if (regno >= info->cmap.len) /* no. of hw registers */
unsigned* red, unsigned* green, unsigned* blue, return 1;
unsigned* transp, struct fb_info* info) { /*
struct pm2fb_info* i=(struct pm2fb_info* )info; * Program hardware... do anything you want with transp
*/
if (regno<256) { /* grayscale works only partially under directcolor */
*red=i->palette[regno].red<<8|i->palette[regno].red; if (info->var.grayscale) {
*green=i->palette[regno].green<<8|i->palette[regno].green; /* grayscale = 0.30*R + 0.59*G + 0.11*B */
*blue=i->palette[regno].blue<<8|i->palette[regno].blue; red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
*transp=i->palette[regno].transp<<8|i->palette[regno].transp; }
/* Directcolor:
* var->{color}.offset contains start of bitfield
* var->{color}.length contains length of bitfield
* {hardwarespecific} contains width of DAC
* cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset)
* RAMDAC[X] is programmed to (red, green, blue)
*
* Pseudocolor:
* uses offset = 0 && length = DAC register width.
* 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;
} }
return regno>255; #undef CNVT_TOHW
} /* Truecolor has hardware independent palette */
if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
u32 v;
static int pm2fb_setcolreg(unsigned regno, if (regno >= 16)
unsigned red, unsigned green, unsigned blue, return 1;
unsigned transp, struct fb_info* info) {
struct pm2fb_info* i=(struct pm2fb_info* )info;
if (regno<16) { v = (red << info->var.red.offset) |
switch (i->current_par.depth) { (green << info->var.green.offset) |
#ifdef FBCON_HAS_CFB8 (blue << info->var.blue.offset) |
case 8: (transp << info->var.transp.offset);
break;
#endif
#ifdef FBCON_HAS_CFB16
case 16:
i->cmap.cmap16[regno]=
((u32 )red & 0xf800) |
(((u32 )green & 0xfc00)>>5) |
(((u32 )blue & 0xf800)>>11);
break;
#endif
#ifdef FBCON_HAS_CFB24
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:
i->cmap.cmap32[regno]=
(((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;
}
}
if (regno<256) {
i->palette[regno].red=red >> 8;
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, switch (info->var.bits_per_pixel) {
struct fb_info_gen* info) {
struct pm2fb_info* i=(struct pm2fb_info* )info;
struct pm2fb_par* p=(struct pm2fb_par* )par;
unsigned long flags;
local_irq_save(flags);
#ifdef __alpha__
disp->screen_base=i->regions.v_fb + dense_mem(i->regions.v_fb);
#else
disp->screen_base=i->regions.v_fb;
#endif
switch (p->depth) {
#ifdef FBCON_HAS_CFB8
case 8: case 8:
if (p->flags & PM2FF_ACCEL) /* Yes some hand held devices have this. */
disp->dispsw=&pm2_cfb8; ((u8*)(info->pseudo_palette))[regno] = v;
else break;
disp->dispsw=&fbcon_cfb8; case 16:
break; ((u16*)(info->pseudo_palette))[regno] = v;
#endif
#ifdef FBCON_HAS_CFB16
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
#ifdef FBCON_HAS_CFB24
case 24: case 24:
if (p->flags & PM2FF_ACCEL) case 32:
disp->dispsw=&pm2_cfb24; ((u32*)(info->pseudo_palette))[regno] = v;
else
disp->dispsw=&fbcon_cfb24;
disp->dispsw_data=i->cmap.cmap24;
break;
#endif
#ifdef FBCON_HAS_CFB32
case 32:
if (p->flags & PM2FF_ACCEL)
disp->dispsw=&pm2_cfb32;
else
disp->dispsw=&fbcon_cfb32;
disp->dispsw_data=i->cmap.cmap32;
break;
#endif
default:
disp->dispsw=&fbcon_dummy;
break; break;
}
return 0;
} }
local_irq_restore(flags); /* ... */
return 0;
} }
#ifdef PM2FB_HW_CURSOR /**
/*************************************************************************** * pm2fb_pan_display - Pans the display.
* Hardware cursor support * @var: frame buffer variable screen structure
***************************************************************************/ * @info: frame buffer structure that represents a single frame buffer
*
static u8 cursor_bits_lookup[16] = { * Pan (or wrap, depending on the `vmode' field) the display using the
0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54, * `xoffset' and `yoffset' fields of the `var' structure.
0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55 * If the values don't fit, return -EINVAL.
}; *
* Returns negative errno on error, or zero on success.
static u8 cursor_mask_lookup[16] = { *
0x00, 0x80, 0x20, 0xa0, 0x08, 0x88, 0x28, 0xa8, */
0x02, 0x82, 0x22, 0xa2, 0x0a, 0x8a, 0x2a, 0xaa static int pm2fb_pan_display(struct fb_var_screeninfo *var,
}; struct fb_info *info)
static void pm2v_set_cursor_color(struct pm2fb_info *fb, u8 *red, u8 *green, u8 *blue)
{ {
struct pm2_cursor *c = fb->cursor; struct pm2fb_par *p = (struct pm2fb_par *) info->par;
int i; u32 base;
u32 depth;
for (i = 0; i < 2; i++) { u32 xres;
c->color[3*i] = red[i];
c->color[3*i+1] = green[i];
c->color[3*i+2] = blue[i];
}
WAIT_FIFO(fb, 14); xres = (var->xres + 31) & ~31;
pm2_WR(fb, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CURSOR_PALETTE >> 8); depth = (var->bits_per_pixel + 7) & ~7;
for (i = 0; i < 6; i++) depth = (depth > 32) ? 32 : depth;
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PALETTE+i, c->color[i]); base = to3264(var->yoffset * xres + var->xoffset, depth, 1);
pm2_WR(fb, PM2VR_RD_INDEX_HIGH, 0); WAIT_FIFO(p, 1);
pm2_WR(p, PM2R_SCREEN_BASE, base);
return 0;
} }
static void pm2v_set_cursor_shape(struct pm2fb_info *fb) /**
* pm2fb_blank - Blanks the display.
* @blank_mode: the blank mode we want.
* @info: frame buffer structure that represents a single frame buffer
*
* Blank the screen if blank_mode != 0, else unblank. Return 0 if
* blanking succeeded, != 0 if un-/blanking failed due to e.g. a
* video mode which doesn't support it. Implements VESA suspend
* and powerdown modes on hardware that supports disabling hsync/vsync:
* blank_mode == 2: suspend vsync
* blank_mode == 3: suspend hsync
* blank_mode == 4: powerdown
*
* Returns negative errno on error, or zero on success.
*
*/
static int pm2fb_blank(int blank_mode, struct fb_info *info)
{ {
struct pm2_cursor *c = fb->cursor; struct pm2fb_par *par = (struct pm2fb_par *) info->par;
u8 m, b; u32 video = par->video;
int i, x, y;
switch (blank_mode) {
WAIT_FIFO(fb, 1); case 0: /* Screen: On; HSync: On, VSync: On */
pm2_WR(fb, PM2VR_RD_INDEX_HIGH, PM2VI_RD_CURSOR_PATTERN >> 8); break;
for (y = 0, i = 0; y < c->size.y; y++) { case 1: /* Screen: Off; HSync: On, VSync: On */
WAIT_FIFO(fb, 32); video &= ~PM2F_VIDEO_ENABLE;
for (x = 0; x < c->size.x >> 3; x++) { break;
m = c->mask[x][y]; case 2: /* Screen: Off; HSync: On, VSync: Off */
b = c->bits[x][y]; video &= ~(PM2F_VIDEO_ENABLE | PM2F_VSYNC_MASK | PM2F_BLANK_LOW );
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i, break;
cursor_mask_lookup[m >> 4] | case 3: /* Screen: Off; HSync: Off, VSync: On */
cursor_bits_lookup[(b & m) >> 4]); video &= ~(PM2F_VIDEO_ENABLE | PM2F_HSYNC_MASK | PM2F_BLANK_LOW );
pm2v_RDAC_WR(fb, PM2VI_RD_CURSOR_PATTERN + i + 1, break;
cursor_mask_lookup[m & 0x0f] | case 4: /* Screen: Off; HSync: Off, VSync: Off */
cursor_bits_lookup[(b & m) & 0x0f]); video &= ~(PM2F_VIDEO_ENABLE | PM2F_VSYNC_MASK | PM2F_HSYNC_MASK|
i+=2; PM2F_BLANK_LOW);
} break;
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); set_video(par, video);
pm2_WR(fb, PM2VR_RD_INDEX_HIGH, 0); return 0;
} }
static void pm2v_set_cursor(struct pm2fb_info *fb, int on) /* ------------ Hardware Independent Functions ------------ */
{
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) /*
{ * Frame buffer operations
struct pm2fb_info *fb = (struct pm2fb_info *)dev_addr; */
if (!fb->cursor->enable) static struct fb_ops pm2fb_ops = {
goto out; .owner = THIS_MODULE,
.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,
};
if (fb->cursor->vbl_cnt && --fb->cursor->vbl_cnt == 0) { /*
fb->cursor->on ^= 1; * PCI stuff
pm2v_set_cursor(fb, fb->cursor->on); */
fb->cursor->vbl_cnt = fb->cursor->blink_rate;
}
out:
fb->cursor->timer->expires = jiffies + (HZ / 50);
add_timer(fb->cursor->timer);
}
static void pm2fb_cursor(struct display *p, int mode, int x, int y) /**
* 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 pm2fb_info *fb = (struct pm2fb_info *)p->fb_info; struct pm2fb_par *default_par;
struct pm2_cursor *c = fb->cursor; struct fb_info *info;
int size, err;
if (!c) return; u32 pci_mem_config;
int err_retval = -ENXIO;
x *= fontwidth(p);
y *= fontheight(p); err = pci_enable_device(pdev);
if (c->pos.x == x && c->pos.y == y && (mode == CM_ERASE) == !c->enable) if ( err ) {
return; printk(KERN_WARNING "pm2fb: Can't enable pdev: %d\n", err);
return err;
c->enable = 0; }
if (c->on)
pm2v_set_cursor(fb, 0); size = sizeof(struct fb_info) + sizeof(struct pm2fb_par) + 256 * sizeof(u32);
c->pos.x = x;
c->pos.y = y; info = framebuffer_alloc(size, &pdev->dev);
if ( !info )
switch (mode) { return -ENOMEM;
case CM_ERASE: memset(info, 0, size);
c->on = 0;
default_par = info->par;
switch (pdev->device) {
case PCI_DEVICE_ID_TI_TVP4020:
strcpy(pm2fb_fix.id, "TVP4020");
default_par->type = PM2_TYPE_PERMEDIA2;
break; break;
case PCI_DEVICE_ID_3DLABS_PERMEDIA2:
case CM_DRAW: strcpy(pm2fb_fix.id, "Permedia2");
case CM_MOVE: default_par->type = PM2_TYPE_PERMEDIA2;
if (c->on) break;
pm2v_set_cursor(fb, 1); case PCI_DEVICE_ID_3DLABS_PERMEDIA2V:
else strcpy(pm2fb_fix.id, "Permedia2v");
c->vbl_cnt = CURSOR_DRAW_DELAY; default_par->type = PM2_TYPE_PERMEDIA2V;
c->enable = 1;
break; break;
} }
}
static struct pm2_cursor * __init pm2_init_cursor(struct pm2fb_info *fb) pm2fb_fix.mmio_start = pci_resource_start(pdev, 0);
{ pm2fb_fix.mmio_len = PM2_REGS_SIZE;
struct pm2_cursor *cursor;
if (fb->type != PM2_TYPE_PERMEDIA2V)
return 0; /* FIXME: Support hw cursor everywhere */
cursor = kmalloc(sizeof(struct pm2_cursor), GFP_ATOMIC);
if (!cursor)
return 0;
memset(cursor, 0, sizeof(*cursor));
cursor->timer = kmalloc(sizeof(*cursor->timer), GFP_KERNEL); #ifdef PM2FB_BE_APERTURE
if (!cursor->timer) { pm2fb_fix.mmio_start += PM2_REGS_SIZE;
kfree(cursor); #endif
return 0;
/* Registers - request region and map it. */
if ( !request_mem_region(pm2fb_fix.mmio_start, pm2fb_fix.mmio_len,
"pm2fb regbase") ) {
printk(KERN_WARNING "pm2fb: Can't reserve regbase.\n");
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;
}
/* Now work out how big lfb is going to be. */
pci_mem_config = RD32(default_par->v_regs, PM2R_MEM_CONFIG);
switch(pci_mem_config & PM2F_MEM_CONFIG_RAM_MASK) {
case PM2F_MEM_BANKS_1:
default_par->fb_size=0x200000;
break;
case PM2F_MEM_BANKS_2:
default_par->fb_size=0x400000;
break;
case PM2F_MEM_BANKS_3:
default_par->fb_size=0x600000;
break;
case PM2F_MEM_BANKS_4:
default_par->fb_size=0x800000;
break;
} }
memset(cursor->timer, 0, sizeof(*cursor->timer)); default_par->memclock = CVPPC_MEMCLOCK;
pm2fb_fix.smem_start = pci_resource_start(pdev, 1);
cursor->blink_rate = DEFAULT_CURSOR_BLINK_RATE; pm2fb_fix.smem_len = default_par->fb_size;
/* Linear frame buffer - request region and map it. */
if ( !request_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len,
"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;
}
info->fbops = &pm2fb_ops;
info->fix = pm2fb_fix;
info->par = default_par;
info->pseudo_palette = (void *)(default_par + 1);
info->flags = FBINFO_FLAG_DEFAULT;
#ifndef MODULE
if (!mode)
mode = "640x480@60";
err = fb_find_mode(&info->var, info, mode, NULL, 0, NULL, 8);
if (!err || err == 4)
#endif
info->var = pm2fb_var;
if (curblink) { if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
init_timer(cursor->timer); goto err_exit_all;
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; if (register_framebuffer(info) < 0)
} goto err_exit_both;
static int pm2fb_set_font(struct display *d, int width, int height) printk(KERN_INFO "fb%d: %s frame buffer device, memory = %dK.\n",
{ info->node, info->fix.id, default_par->fb_size / 1024);
struct pm2fb_info *fb = (struct pm2fb_info *)d->fb_info;
struct pm2_cursor *c = fb->cursor;
int i, j;
if (c) {
if (!width || !height) {
width = 8;
height = 16;
}
c->hot.x = 0; /*
c->hot.y = 0; * Our driver data
c->size.x = width; */
c->size.y = height; pci_set_drvdata(pdev, info);
memset(c->bits, 0xff, sizeof(c->bits)); return 0;
memset(c->mask, 0, sizeof(c->mask));
for (i = 0, j = width; j >= 0; j -= 8, i++) { err_exit_all:
c->mask[i][height-2] = (j >= 8) ? 0xff : (0xff << (8 - j)); fb_dealloc_cmap(&info->cmap);
c->mask[i][height-1] = (j >= 8) ? 0xff : (0xff << (8 - j)); 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;
unregister_framebuffer(info);
iounmap((void*) fix->smem_start);
release_mem_region(fix->smem_start, fix->smem_len);
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 },
#ifdef MODULE { PCI_VENDOR_ID_3DLABS, PCI_DEVICE_ID_3DLABS_PERMEDIA2,
static void pm2fb_cleanup(void) { PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16,
struct pm2fb_info* i=&fb_info; 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, }
};
unregister_framebuffer((struct fb_info* )info); static struct pci_driver pm2fb_driver = {
pm2fb_reset(i); .name = "pm2fb",
.id_table = pm2fb_id_table,
UNMAP(i->regions.v_fb, i->regions.fb_size); .probe = pm2fb_probe,
release_mem_region(i->regions.p_fb, i->regions.fb_size); .remove = __devexit_p(pm2fb_remove),
};
UNMAP(i->regions.v_regs, PM2_REGS_SIZE);
release_mem_region(i->regions.p_regs, PM2_REGS_SIZE);
if (board_table[i->board].cleanup)
board_table[i->board].cleanup(i);
}
#endif /* MODULE */
int __init pm2fb_init(void) { MODULE_DEVICE_TABLE(pci, pm2fb_id_table);
MOD_INC_USE_COUNT;
memset(&fb_info, 0, sizeof(fb_info));
if (!pm2fb_conf(&fb_info)) { /*
MOD_DEC_USE_COUNT; * Initialization
return -ENXIO; */
}
/* Pick up user_var settings if set. */ int __init pm2fb_init(void)
if ((pm2fb_options.flags & OPTF_USER_VAR) && {
pm2fb_decode_var(&user_var, &pm2fb_options.user_mode, return pci_module_init(&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; * Cleanup
*/
for (i=0; user_mode[i].name[0] && static void __exit pm2fb_exit(void)
strcmp(options, user_mode[i].name); i++); {
if (user_mode[i].name[0]) { pci_unregister_driver(&pm2fb_driver);
memcpy(&pm2fb_options.user_mode, &user_mode[i].par,
sizeof(pm2fb_options.user_mode));
pm2fb_options.flags|=OPTF_USER;
}
} }
static void __init pm2fb_font_setup(char* options) { /*
strlcpy(pm2fb_options.font, options, sizeof(pm2fb_options.font)); * Setup
} */
static void __init pm2fb_var_setup(char* options) { /**
char* next; * Parse user speficied options.
*
pm2fb_par2var(&user_var, &pm2fb_options.user_mode); * This is, comma-separated options following `video=pm2fb:'.
*/
while (options) { int __init pm2fb_setup(char *options)
if ((next=strchr(options, ';'))) {
*(next++)='\0'; char* this_opt;
if (!strncmp(options, "bpp:", 4))
user_var.bits_per_pixel= if (!options || !*options)
simple_strtoul(options+4, NULL, 0); return 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) { while ((this_opt = strsep(&options, ",")) != NULL) {
char* next; if (!*this_opt)
continue;
while (options) { if(!strcmp(this_opt, "lowhsync")) {
if ((next=strchr(options, ','))) lowhsync = 1;
*(next++)='\0'; } else if(!strcmp(this_opt, "lowvsync")) {
if (!strncmp(options, "font:", 5)) lowvsync = 1;
pm2fb_font_setup(options+5); } else {
else if (!strncmp(options, "mode:", 5)) mode = this_opt;
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) {
if (mode) #ifdef MODULE
pm2fb_mode_setup(mode); module_init(pm2fb_init);
return pm2fb_init(); #endif
} module_exit(pm2fb_exit);
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