/*
*
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400
*
* (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
*
* Portions Copyright (c) 2001 Matrox Graphics Inc.
*
* Version: 1.65 2002/08/14
*
* MTRR stuff: 1998 Tom Rini <trini@kernel.crashing.org>
*
* Contributors: "menion?" <menion@mindless.com>
* Betatesting, fixes, ideas
*
* "Kurt Garloff" <garloff@suse.de>
* Betatesting, fixes, ideas, videomodes, videomodes timmings
*
* "Tom Rini" <trini@kernel.crashing.org>
* MTRR stuff, PPC cleanups, betatesting, fixes, ideas
*
* "Bibek Sahu" <scorpio@dodds.net>
* Access device through readb|w|l and write b|w|l
* Extensive debugging stuff
*
* "Daniel Haun" <haund@usa.net>
* Testing, hardware cursor fixes
*
* "Scott Wood" <sawst46+@pitt.edu>
* Fixes
*
* "Gerd Knorr" <kraxel@goldbach.isdn.cs.tu-berlin.de>
* Betatesting
*
* "Kelly French" <targon@hazmat.com>
* "Fernando Herrera" <fherrera@eurielec.etsit.upm.es>
* Betatesting, bug reporting
*
* "Pablo Bianucci" <pbian@pccp.com.ar>
* Fixes, ideas, betatesting
*
* "Inaky Perez Gonzalez" <inaky@peloncho.fis.ucm.es>
* Fixes, enhandcements, ideas, betatesting
*
* "Ryuichi Oikawa" <roikawa@rr.iiij4u.or.jp>
* PPC betatesting, PPC support, backward compatibility
*
* "Paul Womar" <Paul@pwomar.demon.co.uk>
* "Owen Waller" <O.Waller@ee.qub.ac.uk>
* PPC betatesting
*
* "Thomas Pornin" <pornin@bolet.ens.fr>
* Alpha betatesting
*
* "Pieter van Leuven" <pvl@iae.nl>
* "Ulf Jaenicke-Roessler" <ujr@physik.phy.tu-dresden.de>
* G100 testing
*
* "H. Peter Arvin" <hpa@transmeta.com>
* Ideas
*
* "Cort Dougan" <cort@cs.nmt.edu>
* CHRP fixes and PReP cleanup
*
* "Mark Vojkovich" <mvojkovi@ucsd.edu>
* G400 support
*
* "Samuel Hocevar" <sam@via.ecp.fr>
* Fixes
*
* "Anton Altaparmakov" <AntonA@bigfoot.com>
* G400 MAX/non-MAX distinction
*
* "Ken Aaker" <kdaaker@rchland.vnet.ibm.com>
* memtype extension (needed for GXT130P RS/6000 adapter)
*
* "Uns Lider" <unslider@miranda.org>
* G100 PLNWT fixes
*
* "Denis Zaitsev" <zzz@cd-club.ru>
* Fixes
*
* "Mike Pieper" <mike@pieper-family.de>
* TVOut enhandcements, V4L2 control interface.
*
* "Diego Biurrun" <diego@biurrun.de>
* DFP testing
*
* (following author is not in any relation with this code, but his code
* is included in this driver)
*
* Based on framebuffer driver for VBE 2.0 compliant graphic boards
* (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
*
* (following author is not in any relation with this code, but his ideas
* were used when writing this driver)
*
* FreeVBE/AF (Matrox), "Shawn Hargreaves" <shawn@talula.demon.co.uk>
*
*/
#include <linux/version.h>
#include "matroxfb_base.h"
#include "matroxfb_misc.h"
#include "matroxfb_accel.h"
#include "matroxfb_DAC1064.h"
#include "matroxfb_Ti3026.h"
#include "matroxfb_maven.h"
#include "matroxfb_crtc2.h"
#include "matroxfb_g450.h"
#include <linux/matroxfb.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#ifdef CONFIG_PPC_PMAC
#include <asm/machdep.h>
unsigned char nvram_read_byte(int);
static int default_vmode = VMODE_NVRAM;
static int default_cmode = CMODE_NVRAM;
#endif
static void matroxfb_unregister_device(struct matrox_fb_info* minfo);
/* --------------------------------------------------------------------- */
/*
* card parameters
*/
/* --------------------------------------------------------------------- */
static struct fb_var_screeninfo vesafb_defined = {
640,480,640,480,/* W,H, W, H (virtual) load xres,xres_virtual*/
0,0, /* virtual -> visible no offset */
8, /* depth -> load bits_per_pixel */
0, /* greyscale ? */
{0,0,0}, /* R */
{0,0,0}, /* G */
{0,0,0}, /* B */
{0,0,0}, /* transparency */
0, /* standard pixel format */
FB_ACTIVATE_NOW,
-1,-1,
FB_ACCELF_TEXT, /* accel flags */
39721L,48L,16L,33L,10L,
96L,2L,~0, /* No sync info */
FB_VMODE_NONINTERLACED,
};
/* --------------------------------------------------------------------- */
static void update_crtc2(struct matrox_fb_info *minfo, unsigned int pos)
{
struct matroxfb_dh_fb_info *info = minfo->crtc2.info;
/* Make sure that displays are compatible */
if (info && (info->fbcon.var.bits_per_pixel == minfo->fbcon.var.bits_per_pixel)
&& (info->fbcon.var.xres_virtual == minfo->fbcon.var.xres_virtual)
&& (info->fbcon.var.green.length == minfo->fbcon.var.green.length)
) {
switch (minfo->fbcon.var.bits_per_pixel) {
case 16:
case 32:
pos = pos * 8;
if (info->interlaced) {
mga_outl(0x3C2C, pos);
mga_outl(0x3C28, pos + minfo->fbcon.var.xres_virtual * minfo->fbcon.var.bits_per_pixel / 8);
} else {
mga_outl(0x3C28, pos);
}
break;
}
}
}
static void matroxfb_crtc1_panpos(struct matrox_fb_info *minfo)
{
if (minfo->crtc1.panpos >= 0) {
unsigned long flags;
int panpos;
matroxfb_DAC_lock_irqsave(flags);
panpos = minfo->crtc1.panpos;
if (panpos >= 0) {
unsigned int extvga_reg;
minfo->crtc1.panpos = -1; /* No update pending anymore */
extvga_reg = mga_inb(M_EXTVGA_INDEX);
mga_setr(M_EXTVGA_INDEX, 0x00, panpos);
if (extvga_reg != 0x00) {
mga_outb(M_EXTVGA_INDEX, extvga_reg);
}
}
matroxfb_DAC_unlock_irqrestore(flags);
}
}
static irqreturn_t matrox_irq(int irq, void *dev_id)
{
u_int32_t status;
int handled = 0;
struct matrox_fb_info *minfo = dev_id;
status = mga_inl(M_STATUS);
if (status & 0x20) {
mga_outl(M_ICLEAR, 0x20);
minfo->crtc1.vsync.cnt++;
matroxfb_crtc1_panpos(minfo);
wake_up_interruptible(&minfo->crtc1.vsync.wait);
handled = 1;
}
if (status & 0x200) {
mga_outl(M_ICLEAR, 0x200);
minfo->crtc2.vsync.cnt++;
wake_up_interruptible(&minfo->crtc2.vsync.wait);
handled = 1;
}
return IRQ_RETVAL(handled);
}
int matroxfb_enable_irq(struct matrox_fb_info *minfo, int reenable)
{
u_int32_t bm;
if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400)
bm = 0x220;
else
bm = 0x020;
if (!test_and_set_bit(0, &minfo->irq_flags)) {
if (request_irq(minfo->pcidev->irq, matrox_irq,
IRQF_SHARED, "matroxfb", minfo)) {
clear_bit(0, &minfo->irq_flags);
return -EINVAL;
}
/* Clear any pending field interrupts */
mga_outl(M_ICLEAR, bm);
mga_outl(M_IEN, mga_inl(M_IEN) | bm);
} else if (reenable) {
u_int32_t ien;
ien = mga_inl(M_IEN);
if ((ien & bm) != bm) {
printk(KERN_DEBUG "matroxfb: someone disabled IRQ [%08X]\n", ien);
mga_outl(M_IEN, ien | bm);
}
}
return 0;
}
static void matroxfb_disable_irq(struct matrox_fb_info *minfo)
{
if (test_and_clear_bit(0, &minfo->irq_flags)) {
/* Flush pending pan-at-vbl request... */
matroxfb_crtc1_panpos(minfo);
if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400)
mga_outl(M_IEN, mga_inl(M_IEN) & ~0x220);
else
mga_outl(M_IEN, mga_inl(M_IEN) & ~0x20);
free_irq(minfo->pcidev->irq, minfo);
}
}
int matroxfb_wait_for_sync(struct matrox_fb_info *minfo, u_int32_t crtc)
{
struct matrox_vsync *vs;
unsigned int cnt;
int ret;
switch (crtc) {
case 0:
vs = &minfo->crtc1.vsync;
break;
case 1:
if (minfo->devflags.accelerator != FB_ACCEL_MATROX_M