/*
* linux/drivers/video/pm3fb.c -- 3DLabs Permedia3 frame buffer device
*
* Copyright (C) 2001 Romain Dolbeau <romain@dolbeau.org>.
*
* Ported to 2.6 kernel on 1 May 2007 by Krzysztof Helt <krzysztof.h1@wp.pl>
* based on pm2fb.c
*
* Based on code written by:
* Sven Luther, <luther@dpt-info.u-strasbg.fr>
* Alan Hourihane, <alanh@fairlite.demon.co.uk>
* Russell King, <rmk@arm.linux.org.uk>
* Based on linux/drivers/video/skeletonfb.c:
* Copyright (C) 1997 Geert Uytterhoeven
* Based on linux/driver/video/pm2fb.c:
* Copyright (C) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT)
* Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com)
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <video/pm3fb.h>
#if !defined(CONFIG_PCI)
#error "Only generic PCI cards supported."
#endif
#undef PM3FB_MASTER_DEBUG
#ifdef PM3FB_MASTER_DEBUG
#define DPRINTK(a,b...) printk(KERN_DEBUG "pm3fb: %s: " a, __FUNCTION__ , ## b)
#else
#define DPRINTK(a,b...)
#endif
/*
* Driver data
*/
static char *mode_option __devinitdata;
/*
* If your driver supports multiple boards, you should make the
* below data types arrays, or allocate them dynamically (using kmalloc()).
*/
/*
* This structure defines the hardware state of the graphics card. Normally
* you place this in a header file in linux/include/video. This file usually
* also includes register information. That allows other driver subsystems
* and userland applications the ability to use the same header file to
* avoid duplicate work and easy porting of software.
*/
struct pm3_par {
unsigned char __iomem *v_regs;/* virtual address of p_regs */
u32 video; /* video flags before blanking */
u32 base; /* screen base (xoffset+yoffset) in 128 bits unit */
u32 palette[16];
};
/*
* Here we define the default structs fb_fix_screeninfo and fb_var_screeninfo
* if we don't use modedb. If we do use modedb see pm3fb_init how to use it
* to get a fb_var_screeninfo. Otherwise define a default var as well.
*/
static struct fb_fix_screeninfo pm3fb_fix __devinitdata = {
.id = "Permedia3",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_PSEUDOCOLOR,
.xpanstep = 1,
.ypanstep = 1,
.ywrapstep = 0,
.accel = FB_ACCEL_NONE,
};
/*
* Utility functions
*/
static inline u32 PM3_READ_REG(struct pm3_par *par, s32 off)
{
return fb_readl(par->v_regs + off);
}
static inline void PM3_WRITE_REG(struct pm3_par *par, s32 off, u32 v)
{
fb_writel(v, par->v_regs + off);
}
static inline void PM3_WAIT(struct pm3_par *par, u32 n)
{
while (PM3_READ_REG(par, PM3InFIFOSpace) < n);
}
static inline void PM3_SLOW_WRITE_REG(struct pm3_par *par, s32 off, u32 v)
{
if (par->v_regs) {
mb();
PM3_WAIT(par, 1);
wmb();
PM3_WRITE_REG(par, off, v);
}
}
static inline void PM3_SET_INDEX(struct pm3_par *par, unsigned index)
{
PM3_SLOW_WRITE_REG(par, PM3RD_IndexHigh, (index >> 8) & 0xff);
PM3_SLOW_WRITE_REG(par, PM3RD_IndexLow, index & 0xff);
}
static inline void PM3_WRITE_DAC_REG(struct pm3_par *par, unsigned r, u8 v)
{
PM3_SET_INDEX(par, r);
wmb();
PM3_WRITE_REG(par, PM3RD_IndexedData, v);
}
static inline void pm3fb_set_color(struct pm3_par *par, unsigned char regno,
unsigned char r, unsigned char g, unsigned char b)
{
PM3_SLOW_WRITE_REG(par, PM3RD_PaletteWriteAddress, regno);
PM3_SLOW_WRITE_REG(par, PM3RD_PaletteData, r);
PM3_SLOW_WRITE_REG(par, PM3RD_PaletteData, g);
PM3_SLOW_WRITE_REG(par, PM3RD_PaletteData, b);
}
static void pm3fb_clear_colormap(struct pm3_par *par,
unsigned char r, unsigned char g, unsigned char b)
{
int i;
for (i = 0; i < 256 ; i++) /* fill color map with white */
pm3fb_set_color(par, i, r, g, b);
}
/* Calculating various clock parameter */
static void pm3fb_calculate_clock(unsigned long reqclock,
unsigned char *prescale,
unsigned char *feedback,
unsigned char *postscale)
{
int f, pre, post;
unsigned long freq;
long freqerr = 1000;
long currerr;
for (f = 1; f < 256; f++) {
for (pre = 1; pre < 256; pre++) {
for (post = 0; post < 5;