/*
*
* 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 writting this driver)
*
* FreeVBE/AF (Matrox), "Shawn Hargreaves" <shawn@talula.demon.co.uk>
*
*/
/* make checkconfig does not check included files... */
#include <linuxcomp.h>
#include <linux/config.h>
#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 <asm/uaccess.h>
#ifdef CONFIG_PPC_PMAC
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
,
0, {0,0,0,0,0}
};
/* --------------------------------------------------------------------- */
static void update_crtc2
(WPMINFO
unsigned int pos
) {
struct matroxfb_dh_fb_info
* info
= ACCESS_FBINFO
(crtc2.
info);
/* Make sure that displays are compatible */
if (info
&& (info
->fbcon.
var.
bits_per_pixel == ACCESS_FBINFO
(fbcon
).
var.
bits_per_pixel)
&& (info
->fbcon.
var.
xres_virtual == ACCESS_FBINFO
(fbcon
).
var.
xres_virtual)
&& (info
->fbcon.
var.
green.
length == ACCESS_FBINFO
(fbcon
).
var.
green.
length)
) {
switch (ACCESS_FBINFO
(fbcon
).
var.
bits_per_pixel) {
case 16:
case 32:
pos
= pos
* 8;
if (info
->interlaced
) {
mga_outl
(0x3C2C, pos
);
mga_outl
(0x3C28, pos
+ ACCESS_FBINFO
(fbcon
).
var.
xres_virtual * ACCESS_FBINFO
(fbcon
).
var.
bits_per_pixel / 8);
} else {
mga_outl
(0x3C28, pos
);
}
break;
}
}
}
static void matroxfb_crtc1_panpos
(WPMINFO2
) {
if (ACCESS_FBINFO
(crtc1.
panpos) >= 0) {
unsigned long flags
;
int panpos
;
matroxfb_DAC_lock_irqsave
(flags
);
panpos
= ACCESS_FBINFO
(crtc1.
panpos);
if (panpos
>= 0) {
unsigned int extvga_reg
;
ACCESS_FBINFO
(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
, struct pt_regs
*fp
)
{
u_int32_t status
;
int handled
= 0;
MINFO_FROM
(dev_id
);
status
= mga_inl
(M_STATUS
);
if (status
& 0x20) {
mga_outl
(M_ICLEAR
, 0x20);
ACCESS_FBINFO
(crtc1.
vsync.
cnt)++;
matroxfb_crtc1_panpos
(PMINFO2
);
//wake_up_interruptible(&ACCESS_FBINFO(crtc1.vsync.wait));
handled
= 1;
}
if (status
& 0x200) {
mga_outl
(M_ICLEAR
, 0x200);
ACCESS_FBINFO
(crtc2.
vsync.
cnt)++;
//wake_up_interruptible(&ACCESS_FBINFO(crtc2.vsync.wait));
handled
= 1;
}
return IRQ_RETVAL
(handled
);
}
int matroxfb_enable_irq
(WPMINFO
int reenable
) {
u_int32_t bm
;
if (ACCESS_FBINFO
(devflags.
accelerator) == FB_ACCEL_MATROX_MGAG400
)
bm
= 0x220;
else
bm
= 0x020;
if (!test_and_set_bit
(0, &ACCESS_FBINFO
(irq_flags
))) {
if (request_irq
(ACCESS_FBINFO
(pcidev
)->irq
, matrox_irq
,
SA_SHIRQ
, "matroxfb", MINFO
)) {
clear_bit
(0, &ACCESS_FBINFO
(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", (int)ien
);
mga_outl
(M_IEN
, ien
| bm
);
}
}
return 0;
}
static void matroxfb_disable_irq
(WPMINFO2
) {
if (test_and_clear_bit
(0, &ACCESS_FBINFO
(irq_flags
))) {
/* Flush pending pan-at-vbl request... */
matroxfb_crtc1_panpos
(PMINFO2
);
if (ACCESS_FBINFO
(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
(ACCESS_FBINFO
(pcidev
)->irq
, MINFO
);
}
}
int matroxfb_wait_for_sync
(WPMINFO u_int32_t crtc
) {
wait_queue_t __wait
;
struct matrox_vsync
*vs
;
unsigned int cnt
;
int ret
;
switch (crtc
) {
case 0:
vs
= &ACCESS_FBINFO
(crtc1.
vsync);
break;
case 1:
if (ACCESS_FBINFO
(devflags.
accelerator) != FB_ACCEL_MATROX_MGAG400
) {
return -ENODEV
;
}
vs
= &ACCESS_FBINFO
(crtc2.
vsync);
break;
default:
return -ENODEV
;
}
ret
= matroxfb_enable_irq
(PMINFO
0);
if (ret
) {
return ret
;
}
//init_waitqueue_entry(&__wait, current);
cnt
= vs
->cnt
;
ret
= 1;//ret = wait_event_interruptible_timeout(vs->wait, cnt != vs->cnt, HZ/10);
if (ret
< 0) {
return ret
;
}
if (ret
== 0) {
matroxfb_enable_irq
(PMINFO
1);
return -ETIMEDOUT
;
}
return 0;
}
/* --------------------------------------------------------------------- */
static void matrox_pan_var
(WPMINFO
struct fb_var_screeninfo
*var
) {
unsigned int pos
;
unsigned short p0
, p1
, p2
;
#ifdef CONFIG_FB_MATROX_32MB
unsigned int p3
;
#endif
int vbl
;
unsigned long flags
;
CRITFLAGS
DBG
(__FUNCTION__
)
if (ACCESS_FBINFO
(dead
))
return;
ACCESS_FBINFO
(fbcon
).
var.
xoffset = var
->xoffset
;
ACCESS_FBINFO
(fbcon
).
var.
yoffset = var
->yoffset
;
pos
= (ACCESS_FBINFO
(fbcon
).
var.
yoffset * ACCESS_FBINFO
(fbcon
).
var.
xres_virtual + ACCESS_FBINFO
(fbcon
).
var.
xoffset) * ACCESS_FBINFO
(curr.
final_bppShift) / 32;
pos
+= ACCESS_FBINFO
(curr.
ydstorg.
chunks);
p0
= ACCESS_FBINFO
(hw
).
CRTC[0x0D] = pos
& 0xFF;
p1
= ACCESS_FBINFO
(hw
).
CRTC[0x0C] = (pos
& 0xFF00) >> 8;
p2
= ACCESS_FBINFO
(hw
).
CRTCEXT[0] = (ACCESS_FBINFO
(hw
).
CRTCEXT[0] & 0xB0) | ((pos
>> 16) & 0x0F) | ((pos
>> 14) & 0x40);
#ifdef CONFIG_FB_MATROX_32MB
p3
= ACCESS_FBINFO
(hw
).
CRTCEXT[8] = pos
>> 21;
#endif
/* FB_ACTIVATE_VBL and we can acquire interrupts? Honor FB_ACTIVATE_VBL then... */
vbl
= (var
->activate
& FB_ACTIVATE_VBL
) && (matroxfb_enable_irq
(PMINFO
0) == 0);
CRITBEGIN
matroxfb_DAC_lock_irqsave
(flags
);
mga_setr
(M_CRTC_INDEX
, 0x0D, p0
);
mga_setr
(M_CRTC_INDEX
, 0x0C, p1
);
#ifdef CONFIG_FB_MATROX_32MB
if (ACCESS_FBINFO
(devflags.
support32MB))
mga_setr
(M_EXTVGA_INDEX
, 0x08, p3
);
#endif
if (vbl
) {
ACCESS_FBINFO
(crtc1.
panpos) = p2
;
} else {
/* Abort any pending change */
ACCESS_FBINFO
(crtc1.
panpos) = -1;
mga_setr
(M_EXTVGA_INDEX
, 0x00, p2
);
}
matroxfb_DAC_unlock_irqrestore
(flags
);
update_crtc2
(PMINFO pos
);
CRITEND
}
static void matroxfb_remove
(WPMINFO
int dummy
) {
/* Currently we are holding big kernel lock on all dead & usecount updates.
* Destroy everything after all users release it. Especially do not unregister
* framebuffer and iounmap memory, neither fbmem nor fbcon-cfb* does not check
* for device unplugged when in use.
* In future we should point mmio.vbase & video.vbase somewhere where we can
* write data without causing too much damage...
*/
ACCESS_FBINFO
(dead
) = 1;
if (ACCESS_FBINFO
(usecount
)) {
/* destroy it later */
return;
}
matroxfb_unregister_device
(MINFO
);
unregister_framebuffer
(&ACCESS_FBINFO
(fbcon
));
matroxfb_g450_shutdown
(PMINFO2
);
#ifdef CONFIG_MTRR
if (ACCESS_FBINFO
(mtrr.
vram_valid))
mtrr_del
(ACCESS_FBINFO
(mtrr.
vram), ACCESS_FBINFO
(video.
base), ACCESS_FBINFO
(video.
len));
#endif
mga_iounmap
(ACCESS_FBINFO
(mmio.
vbase));
mga_iounmap
(ACCESS_FBINFO
(video.
vbase));
release_mem_region
(ACCESS_FBINFO
(video.
base), ACCESS_FBINFO
(video.
len_maximum));
release_mem_region
(ACCESS_FBINFO
(mmio.
base), 16384);
#ifdef CONFIG_FB_MATROX_MULTIHEAD
kfree
(minfo
);
#endif
}
/*
* Open/Release the frame buffer device
*/
static int matroxfb_open
(struct fb_info
*info
, int user
)
{
MINFO_FROM_INFO
(info
);
DBG_LOOP
(__FUNCTION__
)
if (ACCESS_FBINFO
(dead
)) {
return -ENXIO
;
}
ACCESS_FBINFO
(usecount
)++;
if (user
) {
ACCESS_FBINFO
(userusecount
)++;
}
return(0);
}
static int matroxfb_release
(struct fb_info
*info
, int user
)
{
MINFO_FROM_INFO
(info
);
DBG_LOOP
(__FUNCTION__
)
if (user
) {
if (0 == --ACCESS_FBINFO
(userusecount
)) {
matroxfb_disable_irq
(PMINFO2
);
}
}
if (!(--ACCESS_FBINFO
(usecount
)) && ACCESS_FBINFO
(dead
)) {
matroxfb_remove
(PMINFO
0);
}
return(0);
}
static int matroxfb_pan_display
(struct fb_var_screeninfo
*var
,
struct fb_info
* info
) {
MINFO_FROM_INFO
(info
);
DBG
(__FUNCTION__
)
matrox_pan_var
(PMINFO var
);
return 0;
}
static int matroxfb_get_final_bppShift
(CPMINFO
int bpp
) {
int bppshft2
;
DBG
(__FUNCTION__
)
bppshft2
= bpp
;
if (!bppshft2
) {
return 8;
}
if (isInterleave
(MINFO
))
bppshft2
>>= 1;
if (ACCESS_FBINFO
(devflags.
video64bits))
bppshft2
>>= 1;
return bppshft2
;
}
static int matroxfb_test_and_set_rounding
(CPMINFO
int xres
, int bpp
) {
int over
;
int rounding
;
DBG
(__FUNCTION__
)
switch (bpp
) {
case 0: return xres
;
case 4: rounding
= 128;
break;
case 8: rounding
= 64; /* doc says 64; 32 is OK for G400 */
break;
case 16: rounding
= 32;
break;
case 24: rounding
= 64; /* doc says 64; 32 is OK for G400 */
break;
default: rounding
= 16;
/* on G400, 16 really does not work */
if (ACCESS_FBINFO
(devflags.
accelerator) == FB_ACCEL_MATROX_MGAG400
)
rounding
= 32;
break;
}
if (isInterleave
(MINFO
)) {
rounding
*= 2;
}
over
= xres
% rounding
;
if (over
)
xres
+= rounding
-over
;
return xres
;
}
static int matroxfb_pitch_adjust
(CPMINFO
int xres
, int bpp
) {
const int* width
;
int xres_new
;
DBG
(__FUNCTION__
)
if (!bpp
) return xres
;
width
= ACCESS_FBINFO
(capable.
vxres);
if (ACCESS_FBINFO
(devflags.
precise_width)) {
while (*width
) {
if ((*width
>= xres
) && (matroxfb_test_and_set_rounding
(PMINFO
*width
, bpp
) == *width
)) {
break;
}
width
++;
}
xres_new
= *width
;
} else {
xres_new
= matroxfb_test_and_set_rounding
(PMINFO xres
, bpp
);
}
if (!xres_new
) return 0;
if (xres
!= xres_new
) {
printk
(KERN_INFO
"matroxfb: cannot set xres to %d, rounded up to %d\n", xres
, xres_new
);
}
return xres_new
;
}
static int matroxfb_get_cmap_len
(struct fb_var_screeninfo
*var
) {
DBG
(__FUNCTION__
)
switch (var
->bits_per_pixel
) {
case 4:
return 16; /* pseudocolor... 16 entries HW palette */
case 8:
return 256; /* pseudocolor... 256 entries HW palette */
case 16:
return 16; /* directcolor... 16 entries SW palette */
/* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */
case 24:
return 16; /* directcolor... 16 entries SW palette */
/* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */
case 32:
return 16; /* directcolor... 16 entries SW palette */
/* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */
}
return 16; /* return something reasonable... or panic()? */
}
static int matroxfb_decode_var
(CPMINFO
struct fb_var_screeninfo
*var
, int *visual
, int *video_cmap_len
, unsigned int* ydstorg
) {
struct RGBT
{
unsigned char bpp
;
struct {
unsigned char offset
,
length
;
} red
,
green
,
blue
,
transp
;
signed char visual
;
};
static const struct RGBT table
[]= {
{ 8,{ 0,8},{0,8},{0,8},{ 0,0},MX_VISUAL_PSEUDOCOLOR
},
{15,{10,5},{5,5},{0,5},{15,1},MX_VISUAL_DIRECTCOLOR
},
{16,{11,5},{5,6},{0,5},{ 0,0},MX_VISUAL_DIRECTCOLOR
},
{24,{16,8},{8,8},{0,8},{ 0,0},MX_VISUAL_DIRECTCOLOR
},
{32,{16,8},{8,8},{0,8},{24,8},MX_VISUAL_DIRECTCOLOR
}
};
struct RGBT
const *rgbt
;
unsigned int bpp
= var
->bits_per_pixel
;
unsigned int vramlen
;
unsigned int memlen
;
DBG
(__FUNCTION__
)
switch (bpp
) {
case 4: if (!ACCESS_FBINFO
(capable.
cfb4)) return -EINVAL
;
break;
case 8: break;
case 16: break;
case 24: break;
case 32: break;
default: return -EINVAL
;
}
*ydstorg
= 0;
vramlen
= ACCESS_FBINFO
(video.
len_usable);
if (var
->yres_virtual
< var
->yres
)
var
->yres_virtual
= var
->yres
;
if (var
->xres_virtual
< var
->xres
)
var
->xres_virtual
= var
->xres
;
var
->xres_virtual
= matroxfb_pitch_adjust
(PMINFO var
->xres_virtual
, bpp
);
memlen
= var
->xres_virtual
* bpp
* var
->yres_virtual
/ 8;
if (memlen
> vramlen
) {
var
->yres_virtual
= vramlen
* 8 / (var
->xres_virtual
* bpp
);
memlen
= var
->xres_virtual
* bpp
* var
->yres_virtual
/ 8;
}
/* There is hardware bug that no line can cross 4MB boundary */
/* give up for CFB24, it is impossible to easy workaround it */
/* for other try to do something */
if (!ACCESS_FBINFO
(capable.
cross4MB) && (memlen
> 0x400000)) {
if (bpp
== 24) {
/* sorry */
} else {
unsigned int linelen
;
unsigned int m1
= linelen
= var
->xres_virtual
* bpp
/ 8;
unsigned int m2
= PAGE_SIZE
; /* or 128 if you do not need PAGE ALIGNED address */
unsigned int max_yres
;
while (m1
) {
int t
;
while (m2
>= m1
) m2
-= m1
;
t
= m1
;
m1
= m2
;
m2
= t
;
}
m2
= linelen
* PAGE_SIZE
/ m2
;
*ydstorg
= m2
= 0x400000 % m2
;
max_yres
= (vramlen
- m2
) / linelen
;
if (var
->yres_virtual
> max_yres
)
var
->yres_virtual
= max_yres
;
}
}
/* YDSTLEN contains only signed 16bit value */
if (var
->yres_virtual
> 32767)
var
->yres_virtual
= 32767;
/* we must round yres/xres down, we already rounded y/xres_virtual up
if it was possible. We should return -EINVAL, but I disagree */
if (var
->yres_virtual
< var
->yres
)
var
->yres
= var
->yres_virtual
;
if (var
->xres_virtual
< var
->xres
)
var
->xres
= var
->xres_virtual
;
if (var
->xoffset
+ var
->xres
> var
->xres_virtual
)
var
->xoffset
= var
->xres_virtual
- var
->xres
;
if (var
->yoffset
+ var
->yres
> var
->yres_virtual
)
var
->yoffset
= var
->yres_virtual
- var
->yres
;
if (bpp
== 16 && var
->green.
length == 5) {
bpp
--; /* an artifical value - 15 */
}
for (rgbt
= table
; rgbt
->bpp
< bpp
; rgbt
++);
#define SETCLR(clr)\
var->clr.offset = rgbt->clr.offset;\
var->clr.length = rgbt->clr.length
SETCLR
(red
);
SETCLR
(green
);
SETCLR
(blue
);
SETCLR
(transp
);
#undef SETCLR
*visual
= rgbt
->visual
;
if (bpp
> 8)
dprintk
("matroxfb: truecolor: "
"size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
var
->transp.
length, var
->red.
length, var
->green.
length, var
->blue.
length,
var
->transp.
offset, var
->red.
offset, var
->green.
offset, var
->blue.
offset);
*video_cmap_len
= matroxfb_get_cmap_len
(var
);
dprintk
(KERN_INFO
"requested %d*%d/%dbpp (%d*%d)\n", var
->xres
, var
->yres
, var
->bits_per_pixel
,
var
->xres_virtual
, var
->yres_virtual
);
return 0;
}
static int matroxfb_setcolreg
(unsigned regno
, unsigned red
, unsigned green
,
unsigned blue
, unsigned transp
,
struct fb_info
*fb_info
)
{
#ifdef CONFIG_FB_MATROX_MULTIHEAD
struct matrox_fb_info
* minfo
= container_of
(fb_info
, struct matrox_fb_info
, fbcon
);
#endif
DBG
(__FUNCTION__
)
/*
* Set a single color register. The values supplied are
* already rounded down to the hardware's capabilities
* (according to the entries in the `var' structure). Return
* != 0 for invalid regno.
*/
if (regno
>= ACCESS_FBINFO
(curr.
cmap_len))
return 1;
if (ACCESS_FBINFO
(fbcon
).
var.
grayscale) {
/* gray = 0.30*R + 0.59*G + 0.11*B */
red
= green
= blue
= (red
* 77 + green
* 151 + blue
* 28) >> 8;
}
red
= CNVT_TOHW
(red
, ACCESS_FBINFO
(fbcon
).
var.
red.
length);
green
= CNVT_TOHW
(green
, ACCESS_FBINFO
(fbcon
).
var.
green.
length);
blue
= CNVT_TOHW
(blue
, ACCESS_FBINFO
(fbcon
).
var.
blue.
length);
transp
= CNVT_TOHW
(transp
, ACCESS_FBINFO
(fbcon
).
var.
transp.
length);
switch (ACCESS_FBINFO
(fbcon
).
var.
bits_per_pixel) {
case 4:
case 8:
mga_outb
(M_DAC_REG
, regno
);
mga_outb
(M_DAC_VAL
, red
);
mga_outb
(M_DAC_VAL
, green
);
mga_outb
(M_DAC_VAL
, blue
);
break;
case 16:
{
u_int16_t col
=
(red
<< ACCESS_FBINFO
(fbcon
).
var.
red.
offset) |
(green
<< ACCESS_FBINFO
(fbcon
).
var.
green.
offset) |
(blue
<< ACCESS_FBINFO
(fbcon
).
var.
blue.
offset) |
(transp
<< ACCESS_FBINFO
(fbcon
).
var.
transp.
offset); /* for 1:5:5:5 */
ACCESS_FBINFO
(cmap
[regno
]) = col
| (col
<< 16);
}
break;
case 24:
case 32:
ACCESS_FBINFO
(cmap
[regno
]) =
(red
<< ACCESS_FBINFO
(fbcon
).
var.
red.
offset) |
(green
<< ACCESS_FBINFO
(fbcon
).
var.
green.
offset) |
(blue
<< ACCESS_FBINFO
(fbcon
).
var.
blue.
offset) |
(transp
<< ACCESS_FBINFO
(fbcon
).
var.
transp.
offset); /* 8:8:8:8 */
break;
}
return 0;
}
static void matroxfb_init_fix
(WPMINFO2
)
{
struct fb_fix_screeninfo
*fix
= &ACCESS_FBINFO
(fbcon
).
fix;
DBG
(__FUNCTION__
)
strcpy(fix
->id
,"MATROX");
fix
->xpanstep
= 8; /* 8 for 8bpp, 4 for 16bpp, 2 for 32bpp */
fix
->ypanstep
= 1;
fix
->ywrapstep
= 0;
fix
->mmio_start
= ACCESS_FBINFO
(mmio.
base);
fix
->mmio_len
= ACCESS_FBINFO
(mmio.
len);
fix
->accel
= ACCESS_FBINFO
(devflags.
accelerator);
}
static void matroxfb_update_fix
(WPMINFO2
)
{
struct fb_fix_screeninfo
*fix
= &ACCESS_FBINFO
(fbcon
).
fix;
DBG
(__FUNCTION__
)
fix
->smem_start
= ACCESS_FBINFO
(video.
base) + ACCESS_FBINFO
(curr.
ydstorg.
bytes);
fix
->smem_len
= ACCESS_FBINFO
(video.
len_usable) - ACCESS_FBINFO
(curr.
ydstorg.
bytes);
}
static int matroxfb_check_var
(struct fb_var_screeninfo
*var
, struct fb_info
*info
)
{
int err
;
int visual
;
int cmap_len
;
unsigned int ydstorg
;
MINFO_FROM_INFO
(info
);
if (ACCESS_FBINFO
(dead
)) {
return -ENXIO
;
}
if ((err
= matroxfb_decode_var
(PMINFO var
, &visual
, &cmap_len
, &ydstorg
)) != 0)
return err
;
return 0;
}
static int matroxfb_set_par
(struct fb_info
*info
)
{
int err
;
int visual
;
int cmap_len
;
unsigned int ydstorg
;
struct fb_var_screeninfo
*var
;
MINFO_FROM_INFO
(info
);
DBG
(__FUNCTION__
)
if (ACCESS_FBINFO
(dead
)) {
return -ENXIO
;
}
var
= &info
->var
;
if ((err
= matroxfb_decode_var
(PMINFO var
, &visual
, &cmap_len
, &ydstorg
)) != 0)
return err
;
ACCESS_FBINFO
(fbcon.
screen_base) = vaddr_va
(ACCESS_FBINFO
(video.
vbase)) + ydstorg
;
matroxfb_update_fix
(PMINFO2
);
ACCESS_FBINFO
(fbcon
).
fix.
visual = visual
;
ACCESS_FBINFO
(fbcon
).
fix.
type = FB_TYPE_PACKED_PIXELS
;
ACCESS_FBINFO
(fbcon
).
fix.
type_aux = 0;
ACCESS_FBINFO
(fbcon
).
fix.
line_length = (var
->xres_virtual
* var
->bits_per_pixel
) >> 3;
{
unsigned int pos
;
ACCESS_FBINFO
(curr.
cmap_len) = cmap_len
;
ydstorg
+= ACCESS_FBINFO
(devflags.
ydstorg);
ACCESS_FBINFO
(curr.
ydstorg.
bytes) = ydstorg
;
ACCESS_FBINFO
(curr.
ydstorg.
chunks) = ydstorg
>> (isInterleave
(MINFO
)?3:2);
if (var
->bits_per_pixel
== 4)
ACCESS_FBINFO
(curr.
ydstorg.
pixels) = ydstorg
;
else
ACCESS_FBINFO
(curr.
ydstorg.
pixels) = (ydstorg
* 8) / var
->bits_per_pixel
;
ACCESS_FBINFO
(curr.
final_bppShift) = matroxfb_get_final_bppShift
(PMINFO var
->bits_per_pixel
);
{ struct my_timming mt
;
struct matrox_hw_state
* hw
;
int out
;
matroxfb_var2my
(var
, &mt
);
mt.
crtc = MATROXFB_SRC_CRTC1
;
/* CRTC1 delays */
switch (var
->bits_per_pixel
) {
case 0: mt.
delay = 31 + 0; break;
case 16: mt.
delay = 21 + 8; break;
case 24: mt.
delay = 17 + 8; break;
case 32: mt.
delay = 16 + 8; break;
default: mt.
delay = 31 + 8; break;
}
hw
= &ACCESS_FBINFO
(hw
);
//down_read(&ACCESS_FBINFO(altout).lock);
for (out
= 0; out
< MATROXFB_MAX_OUTPUTS
; out
++) {
if (ACCESS_FBINFO
(outputs
[out
]).
src == MATROXFB_SRC_CRTC1
&&
ACCESS_FBINFO
(outputs
[out
]).
output->compute
) {
ACCESS_FBINFO
(outputs
[out
]).
output->compute
(ACCESS_FBINFO
(outputs
[out
]).
data, &mt
);
}
}
//up_read(&ACCESS_FBINFO(altout).lock);
ACCESS_FBINFO
(crtc1
).
pixclock = mt.
pixclock;
ACCESS_FBINFO
(crtc1
).
mnp = mt.
mnp;
ACCESS_FBINFO
(hw_switch
->init
(PMINFO
&mt
));
pos
= (var
->yoffset
* var
->xres_virtual
+ var
->xoffset
) * ACCESS_FBINFO
(curr.
final_bppShift) / 32;
pos
+= ACCESS_FBINFO
(curr.
ydstorg.
chunks);
hw
->CRTC
[0x0D] = pos
& 0xFF;
hw
->CRTC
[0x0C] = (pos
& 0xFF00) >> 8;
hw
->CRTCEXT
[0] = (hw
->CRTCEXT
[0] & 0xF0) | ((pos
>> 16) & 0x0F) | ((pos
>> 14) & 0x40);
hw
->CRTCEXT
[8] = pos
>> 21;
ACCESS_FBINFO
(hw_switch
->restore
(PMINFO2
));
update_crtc2
(PMINFO pos
);
//down_read(&ACCESS_FBINFO(altout).lock);
for (out
= 0; out
< MATROXFB_MAX_OUTPUTS
; out
++) {
if (ACCESS_FBINFO
(outputs
[out
]).
src == MATROXFB_SRC_CRTC1
&&
ACCESS_FBINFO
(outputs
[out
]).
output->program
) {
ACCESS_FBINFO
(outputs
[out
]).
output->program
(ACCESS_FBINFO
(outputs
[out
]).
data);
}
}
for (out
= 0; out
< MATROXFB_MAX_OUTPUTS
; out
++) {
if (ACCESS_FBINFO
(outputs
[out
]).
src == MATROXFB_SRC_CRTC1
&&
ACCESS_FBINFO
(outputs
[out
]).
output->start
) {
ACCESS_FBINFO
(outputs
[out
]).
output->start
(ACCESS_FBINFO
(outputs
[out
]).
data);
}
}
//up_read(&ACCESS_FBINFO(altout).lock);
matrox_cfbX_init
(PMINFO2
);
}
}
return 0;
}
static int matroxfb_get_vblank
(WPMINFO
struct fb_vblank
*vblank
)
{
unsigned int sts1
;
matroxfb_enable_irq
(PMINFO
0);
memset(vblank
, 0, sizeof(*vblank
));
vblank
->flags
= FB_VBLANK_HAVE_VCOUNT
| FB_VBLANK_HAVE_VSYNC
|
FB_VBLANK_HAVE_VBLANK
| FB_VBLANK_HAVE_HBLANK
;
sts1
= mga_inb
(M_INSTS1
);
vblank
->vcount
= mga_inl
(M_VCOUNT
);
/* BTW, on my PIII/450 with G400, reading M_INSTS1
byte makes this call about 12% slower (1.70 vs. 2.05 us
per ioctl()) */
if (sts1
& 1)
vblank
->flags
|= FB_VBLANK_HBLANKING
;
if (sts1
& 8)
vblank
->flags
|= FB_VBLANK_VSYNCING
;
if (vblank
->vcount
>= ACCESS_FBINFO
(fbcon
).
var.
yres)
vblank
->flags
|= FB_VBLANK_VBLANKING
;
if (test_bit
(0, &ACCESS_FBINFO
(irq_flags
))) {
vblank
->flags
|= FB_VBLANK_HAVE_COUNT
;
/* Only one writer, aligned int value...
it should work without lock and without atomic_t */
vblank
->count
= ACCESS_FBINFO
(crtc1
).
vsync.
cnt;
}
return 0;
}
static struct matrox_altout panellink_output
= {
.
name = "Panellink output",
};
static int matroxfb_ioctl
(struct inode
*inode
, struct file
*file
,
unsigned int cmd
, unsigned long arg
,
struct fb_info
*info
)
{
MINFO_FROM_INFO
(info
);
DBG
(__FUNCTION__
)
if (ACCESS_FBINFO
(dead
)) {
return -ENXIO
;
}
switch (cmd
) {
case FBIOGET_VBLANK
:
{
struct fb_vblank vblank
;
int err
;
err
= matroxfb_get_vblank
(PMINFO
&vblank
);
if (err
)
return err
;
if (copy_to_user
((struct fb_vblank
*)arg
, &vblank
, sizeof(vblank
)))
return -EFAULT
;
return 0;
}
case FBIO_WAITFORVSYNC
:
{
u_int32_t crt
;
crt
= *(u_int32_t
*)arg
;
return matroxfb_wait_for_sync
(PMINFO crt
);
}
case MATROXFB_SET_OUTPUT_MODE
:
{
struct matroxioc_output_mode mom
;
struct matrox_altout
*oproc
;
int val
;
if (copy_from_user
(&mom
, (struct matroxioc_output_mode
*)arg
, sizeof(mom
)))
return -EFAULT
;
if (mom.
output >= MATROXFB_MAX_OUTPUTS
)
return -ENXIO
;
//down_read(&ACCESS_FBINFO(altout.lock));
oproc
= ACCESS_FBINFO
(outputs
[mom.
output]).
output;
if (!oproc
) {
val
= -ENXIO
;
} else if (!oproc
->verifymode
) {
if (mom.
mode == MATROXFB_OUTPUT_MODE_MONITOR
) {
val
= 0;
} else {
val
= -EINVAL
;
}
} else {
val
= oproc
->verifymode
(ACCESS_FBINFO
(outputs
[mom.
output]).
data, mom.
mode);
}
if (!val
) {
if (ACCESS_FBINFO
(outputs
[mom.
output]).
mode != mom.
mode) {
ACCESS_FBINFO
(outputs
[mom.
output]).
mode = mom.
mode;
val
= 1;
}
}
//up_read(&ACCESS_FBINFO(altout.lock));
if (val
!= 1)
return val
;
switch (ACCESS_FBINFO
(outputs
[mom.
output]).
src) {
case MATROXFB_SRC_CRTC1
:
matroxfb_set_par
(info
);
break;
case MATROXFB_SRC_CRTC2
:
{
struct matroxfb_dh_fb_info
* crtc2
;
//down_read(&ACCESS_FBINFO(crtc2.lock));
crtc2
= ACCESS_FBINFO
(crtc2.
info);
if (crtc2
)
crtc2
->fbcon.
fbops->fb_set_par
(&crtc2
->fbcon
);
//up_read(&ACCESS_FBINFO(crtc2.lock));
}
break;
}
return 0;
}
case MATROXFB_GET_OUTPUT_MODE
:
{
struct matroxioc_output_mode mom
;
struct matrox_altout
*oproc
;
int val
;
if (copy_from_user
(&mom
, (struct matroxioc_output_mode
*)arg
, sizeof(mom
)))
return -EFAULT
;
if (mom.
output >= MATROXFB_MAX_OUTPUTS
)
return -ENXIO
;
//down_read(&ACCESS_FBINFO(altout.lock));
oproc
= ACCESS_FBINFO
(outputs
[mom.
output]).
output;
if (!oproc
) {
val
= -ENXIO
;
} else {
mom.
mode = ACCESS_FBINFO
(outputs
[mom.
output]).
mode;
val
= 0;
}
//up_read(&ACCESS_FBINFO(altout.lock));
if (val
)
return val
;
if (copy_to_user
((struct matroxioc_output_mode
*)arg
, &mom
, sizeof(mom
)))
return -EFAULT
;
return 0;
}
case MATROXFB_SET_OUTPUT_CONNECTION
:
{
u_int32_t tmp
;
int i
;
int changes
;
if (copy_from_user
(&tmp
, (u_int32_t
*)arg
, sizeof(tmp
)))
return -EFAULT
;
for (i
= 0; i
< 32; i
++) {
if (tmp
& (1 << i
)) {
if (i
>= MATROXFB_MAX_OUTPUTS
)
return -ENXIO
;
if (!ACCESS_FBINFO
(outputs
[i
]).
output)
return -ENXIO
;
switch (ACCESS_FBINFO
(outputs
[i
]).
src) {
case MATROXFB_SRC_NONE
:
case MATROXFB_SRC_CRTC1
:
break;
default:
return -EBUSY
;
}
}
}
if (ACCESS_FBINFO
(devflags.
panellink)) {
if (tmp
& MATROXFB_OUTPUT_CONN_DFP
) {
if (tmp
& MATROXFB_OUTPUT_CONN_SECONDARY
)
return -EINVAL
;
for (i
= 0; i
< MATROXFB_MAX_OUTPUTS
; i
++) {
if (ACCESS_FBINFO
(outputs
[i
]).
src == MATROXFB_SRC_CRTC2
) {
return -EBUSY
;
}
}
}
}
changes
= 0;
for (i
= 0; i
< MATROXFB_MAX_OUTPUTS
; i
++) {
if (tmp
& (1 << i
)) {
if (ACCESS_FBINFO
(outputs
[i
]).
src != MATROXFB_SRC_CRTC1
) {
changes
= 1;
ACCESS_FBINFO
(outputs
[i
]).
src = MATROXFB_SRC_CRTC1
;
}
} else if (ACCESS_FBINFO
(outputs
[i
]).
src == MATROXFB_SRC_CRTC1
) {
changes
= 1;
ACCESS_FBINFO
(outputs
[i
]).
src = MATROXFB_SRC_NONE
;
}
}
if (!changes
)
return 0;
matroxfb_set_par
(info
);
return 0;
}
case MATROXFB_GET_OUTPUT_CONNECTION
:
{
u_int32_t conn
= 0;
int i
;
for (i
= 0; i
< MATROXFB_MAX_OUTPUTS
; i
++) {
if (ACCESS_FBINFO
(outputs
[i
]).
src == MATROXFB_SRC_CRTC1
) {
conn
|= 1 << i
;
}
}
if (put_user
(conn
, (u_int32_t
*)arg
))
return -EFAULT
;
return 0;
}
case MATROXFB_GET_AVAILABLE_OUTPUTS
:
{
u_int32_t conn
= 0;
int i
;
for (i
= 0; i
< MATROXFB_MAX_OUTPUTS
; i
++) {
if (ACCESS_FBINFO
(outputs
[i
]).
output) {
switch (ACCESS_FBINFO
(outputs
[i
]).
src) {
case MATROXFB_SRC_NONE
:
case MATROXFB_SRC_CRTC1
:
conn
|= 1 << i
;
break;
}
}
}
if (ACCESS_FBINFO
(devflags.
panellink)) {
if (conn
& MATROXFB_OUTPUT_CONN_DFP
)
conn
&= ~MATROXFB_OUTPUT_CONN_SECONDARY
;
if (conn
& MATROXFB_OUTPUT_CONN_SECONDARY
)
conn
&= ~MATROXFB_OUTPUT_CONN_DFP
;
}
if (put_user
(conn
, (u_int32_t
*)arg
))
return -EFAULT
;
return 0;
}
case MATROXFB_GET_ALL_OUTPUTS
:
{
u_int32_t conn
= 0;
int i
;
for (i
= 0; i
< MATROXFB_MAX_OUTPUTS
; i
++) {
if (ACCESS_FBINFO
(outputs
[i
]).
output) {
conn
|= 1 << i
;
}
}
if (put_user
(conn
, (u_int32_t
*)arg
))
return -EFAULT
;
return 0;
}
case VIDIOC_QUERYCAP
:
{
struct v4l2_capability r
;
memset(&r
, 0, sizeof(r
));
strcpy(r.
driver, "matroxfb");
strcpy(r.
card, "Matrox");
sprintf26
(r.
bus_info, "PCI:%s", pci_name
(ACCESS_FBINFO
(pcidev
)));
r.
version = KERNEL_VERSION
(1,0,0);
r.
capabilities = V4L2_CAP_VIDEO_OUTPUT
;
if (copy_to_user
((void*)arg
, &r
, sizeof(r
)))
return -EFAULT
;
return 0;
}
case VIDIOC_QUERYCTRL
:
{
struct v4l2_queryctrl qctrl
;
int err
;
if (copy_from_user
(&qctrl
, (struct v4l2_queryctrl
*)arg
, sizeof(qctrl
)))
return -EFAULT
;
//down_read(&ACCESS_FBINFO(altout).lock);
if (!ACCESS_FBINFO
(outputs
[1]).
output) {
err
= -ENXIO
;
} else if (ACCESS_FBINFO
(outputs
[1]).
output->getqueryctrl
) {
err
= ACCESS_FBINFO
(outputs
[1]).
output->getqueryctrl
(ACCESS_FBINFO
(outputs
[1]).
data, &qctrl
);
} else {
err
= -EINVAL
;
}
//up_read(&ACCESS_FBINFO(altout).lock);
if (err
>= 0 &&
copy_to_user
((struct v4l2_queryctrl
*)arg
, &qctrl
, sizeof(qctrl
)))
return -EFAULT
;
return err
;
}
case VIDIOC_G_CTRL
:
{
struct v4l2_control ctrl
;
int err
;
if (copy_from_user
(&ctrl
, (struct v4l2_control
*)arg
, sizeof(ctrl
)))
return -EFAULT
;
//down_read(&ACCESS_FBINFO(altout).lock);
if (!ACCESS_FBINFO
(outputs
[1]).
output) {
err
= -ENXIO
;
} else if (ACCESS_FBINFO
(outputs
[1]).
output->getctrl
) {
err
= ACCESS_FBINFO
(outputs
[1]).
output->getctrl
(ACCESS_FBINFO
(outputs
[1]).
data, &ctrl
);
} else {
err
= -EINVAL
;
}
//up_read(&ACCESS_FBINFO(altout).lock);
if (err
>= 0 &&
copy_to_user
((struct v4l2_control
*)arg
, &ctrl
, sizeof(ctrl
)))
return -EFAULT
;
return err
;
}
case VIDIOC_S_CTRL
:
{
struct v4l2_control ctrl
;
int err
;
if (copy_from_user
(&ctrl
, (struct v4l2_control
*)arg
, sizeof(ctrl
)))
return -EFAULT
;
//down_read(&ACCESS_FBINFO(altout).lock);
if (!ACCESS_FBINFO
(outputs
[1]).
output) {
err
= -ENXIO
;
} else if (ACCESS_FBINFO
(outputs
[1]).
output->setctrl
) {
err
= ACCESS_FBINFO
(outputs
[1]).
output->setctrl
(ACCESS_FBINFO
(outputs
[1]).
data, &ctrl
);
} else {
err
= -EINVAL
;
}
//up_read(&ACCESS_FBINFO(altout).lock);
return err
;
}
}
return -ENOTTY
;
}
/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
static int matroxfb_blank
(int blank
, struct fb_info
*info
)
{
int seq
;
int crtc
;
CRITFLAGS
MINFO_FROM_INFO
(info
);
DBG
(__FUNCTION__
)
if (ACCESS_FBINFO
(dead
))
return 1;
switch (blank
) {
case 1: seq
= 0x20; crtc
= 0x00; break; /* works ??? */
case 2: seq
= 0x20; crtc
= 0x10; break;
case 3: seq
= 0x20; crtc
= 0x20; break;
case 4: seq
= 0x20; crtc
= 0x30; break;
default: seq
= 0x00; crtc
= 0x00; break;
}
CRITBEGIN
mga_outb
(M_SEQ_INDEX
, 1);
mga_outb
(M_SEQ_DATA
, (mga_inb
(M_SEQ_DATA
) & ~
0x20) | seq
);
mga_outb
(M_EXTVGA_INDEX
, 1);
mga_outb
(M_EXTVGA_DATA
, (mga_inb
(M_EXTVGA_DATA
) & ~
0x30) | crtc
);
CRITEND
return 0;
}
static struct fb_ops matroxfb_ops
= {
.
owner = THIS_MODULE
,
.
fb_open = matroxfb_open
,
.
fb_release = matroxfb_release
,
.
fb_check_var = matroxfb_check_var
,
.
fb_set_par = matroxfb_set_par
,
.
fb_setcolreg = matroxfb_setcolreg
,
.
fb_pan_display =matroxfb_pan_display
,
.
fb_blank = matroxfb_blank
,
.
fb_ioctl = matroxfb_ioctl
,
/* .fb_fillrect = <set by matrox_cfbX_init>, */
/* .fb_copyarea = <set by matrox_cfbX_init>, */
/* .fb_imageblit = <set by matrox_cfbX_init>, */
/* .fb_cursor = <set by matrox_cfbX_init>, */
};
#define RSDepth(X) (((X) >> 8) & 0x0F)
#define RS8bpp 0x1
#define RS15bpp 0x2
#define RS16bpp 0x3
#define RS32bpp 0x4
#define RS4bpp 0x5
#define RS24bpp 0x6
#define RSText 0x7
#define RSText8 0x8
/* 9-F */
static struct { struct fb_bitfield red
, green
, blue
, transp
; int bits_per_pixel
; } colors
[] = {
{ { 0, 8, 0}, { 0, 8, 0}, { 0, 8, 0}, { 0, 0, 0}, 8 },
{ { 10, 5, 0}, { 5, 5, 0}, { 0, 5, 0}, { 15, 1, 0}, 16 },
{ { 11, 5, 0}, { 5, 6, 0}, { 0, 5, 0}, { 0, 0, 0}, 16 },
{ { 16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, { 24, 8, 0}, 32 },
{ { 0, 8, 0}, { 0, 8, 0}, { 0, 8, 0}, { 0, 0, 0}, 4 },
{ { 16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, { 0, 0, 0}, 24 },
{ { 0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, { 0, 0, 0}, 0 }, /* textmode with (default) VGA8x16 */
{ { 0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, { 0, 0, 0}, 0 }, /* textmode hardwired to VGA8x8 */
};
/* initialized by setup, see explanation at end of file (search for MODULE_PARM_DESC) */
static unsigned int mem
; /* "matrox:mem:xxxxxM" */
static int option_precise_width
= 1; /* cannot be changed, option_precise_width==0 must imply noaccel */
static int inv24
; /* "matrox:inv24" */
static int cross4MB
= -1; /* "matrox:cross4MB" */
static int disabled
; /* "matrox:disabled" */
static int noaccel
; /* "matrox:noaccel" */
static int nopan
; /* "matrox:nopan" */
static int no_pci_retry
; /* "matrox:nopciretry" */
static int novga
; /* "matrox:novga" */
static int nobios
; /* "matrox:nobios" */
static int noinit
= 1; /* "matrox:init" */
static int inverse
; /* "matrox:inverse" */
static int sgram
; /* "matrox:sgram" */
#ifdef CONFIG_MTRR
static int mtrr
= 1; /* "matrox:nomtrr" */
#endif
static int grayscale
; /* "matrox:grayscale" */
static int dev
= -1; /* "matrox:dev:xxxxx" */
static unsigned int vesa
= ~
0; /* "matrox:vesa:xxxxx" */
static int depth
= -1; /* "matrox:depth:xxxxx" */
static unsigned int xres
; /* "matrox:xres:xxxxx" */
static unsigned int yres
; /* "matrox:yres:xxxxx" */
static unsigned int upper
= ~
0; /* "matrox:upper:xxxxx" */
static unsigned int lower
= ~
0; /* "matrox:lower:xxxxx" */
static unsigned int vslen
; /* "matrox:vslen:xxxxx" */
static unsigned int left
= ~
0; /* "matrox:left:xxxxx" */
static unsigned int right
= ~
0; /* "matrox:right:xxxxx" */
static unsigned int hslen
; /* "matrox:hslen:xxxxx" */
static unsigned int pixclock
; /* "matrox:pixclock:xxxxx" */
static int sync
= -1; /* "matrox:sync:xxxxx" */
static unsigned int fv
; /* "matrox:fv:xxxxx" */
static unsigned int fh
; /* "matrox:fh:xxxxxk" */
static unsigned int maxclk
; /* "matrox:maxclk:xxxxM" */
static int dfp
; /* "matrox:dfp */
static int dfp_type
= -1; /* "matrox:dfp:xxx */
static int memtype
= -1; /* "matrox:memtype:xxx" */
#ifndef MODULE
static char videomode
[64]; /* "matrox:mode:xxxxx" or "matrox:xxxxx" */
#endif
static int matroxfb_getmemory
(WPMINFO
unsigned int maxSize
, unsigned int *realSize
){
vaddr_t vm
;
unsigned int offs
;
unsigned int offs2
;
unsigned char store
;
unsigned char bytes
[32];
unsigned char* tmp
;
DBG
(__FUNCTION__
)
vm
= ACCESS_FBINFO
(video.
vbase);
maxSize
&= ~
0x1FFFFF; /* must be X*2MB (really it must be 2 or X*4MB) */
/* at least 2MB */
if (maxSize
< 0x0200000) return 0;
if (maxSize
> 0x2000000) maxSize
= 0x2000000;
mga_outb
(M_EXTVGA_INDEX
, 0x03);
mga_outb
(M_EXTVGA_DATA
, mga_inb
(M_EXTVGA_DATA
) | 0x80);
store
= mga_readb
(vm
, 0x1234);
tmp
= bytes
;
for (offs
= 0x100000; offs
< maxSize
; offs
+= 0x200000)
*tmp
++ = mga_readb
(vm
, offs
);
for (offs
= 0x100000; offs
< maxSize
; offs
+= 0x200000)
mga_writeb
(vm
, offs
, 0x02);
if (ACCESS_FBINFO
(features.
accel.
has_cacheflush))
mga_outb
(M_CACHEFLUSH
, 0x00);
else
mga_writeb
(vm
, 0x1234, 0x99);
for (offs
= 0x100000; offs
< maxSize
; offs
+= 0x200000) {
if (mga_readb
(vm
, offs
) != 0x02)
break;
mga_writeb
(vm
, offs
, mga_readb
(vm
, offs
) - 0x02);
if (mga_readb
(vm
, offs
))
break;
}
tmp
= bytes
;
for (offs2
= 0x100000; offs2
< maxSize
; offs2
+= 0x200000)
mga_writeb
(vm
, offs2
, *tmp
++);
mga_writeb
(vm
, 0x1234, store
);
mga_outb
(M_EXTVGA_INDEX
, 0x03);
mga_outb
(M_EXTVGA_DATA
, mga_inb
(M_EXTVGA_DATA
) & ~
0x80);
*realSize
= offs
- 0x100000;
#ifdef CONFIG_FB_MATROX_MILLENIUM
ACCESS_FBINFO
(interleave
) = !(!isMillenium
(MINFO
) || ((offs
- 0x100000) & 0x3FFFFF));
#endif
return 1;
}
struct video_board
{
int maxvram
;
int maxdisplayable
;
int accelID
;
struct matrox_switch
* lowlevel
;
};
#ifdef CONFIG_FB_MATROX_MILLENIUM
static struct video_board vbMillennium
= {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGA2064W
, &matrox_millennium
};
static struct video_board vbMillennium2
= {0x1000000, 0x0800000, FB_ACCEL_MATROX_MGA2164W
, &matrox_millennium
};
static struct video_board vbMillennium2A
= {0x1000000, 0x0800000, FB_ACCEL_MATROX_MGA2164W_AGP
, &matrox_millennium
};
#endif /* CONFIG_FB_MATROX_MILLENIUM */
#ifdef CONFIG_FB_MATROX_MYSTIQUE
static struct video_board vbMystique
= {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGA1064SG
, &matrox_mystique
};
#endif /* CONFIG_FB_MATROX_MYSTIQUE */
#ifdef CONFIG_FB_MATROX_G100
static struct video_board vbG100
= {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGAG100
, &matrox_G100
};
static struct video_board vbG200
= {0x1000000, 0x1000000, FB_ACCEL_MATROX_MGAG200
, &matrox_G100
};
#ifdef CONFIG_FB_MATROX_32MB
/* from doc it looks like that accelerator can draw only to low 16MB :-( Direct accesses & displaying are OK for
whole 32MB */
static struct video_board vbG400
= {0x2000000, 0x1000000, FB_ACCEL_MATROX_MGAG400
, &matrox_G100
};
#else
static struct video_board vbG400
= {0x2000000, 0x1000000, FB_ACCEL_MATROX_MGAG400
, &matrox_G100
};
#endif
#endif
#define DEVF_VIDEO64BIT 0x0001
#define DEVF_SWAPS 0x0002
#define DEVF_SRCORG 0x0004
#define DEVF_DUALHEAD 0x0008
#define DEVF_CROSS4MB 0x0010
#define DEVF_TEXT4B 0x0020
/* #define DEVF_recycled 0x0040 */
/* #define DEVF_recycled 0x0080 */
#define DEVF_SUPPORT32MB 0x0100
#define DEVF_ANY_VXRES 0x0200
#define DEVF_TEXT16B 0x0400
#define DEVF_CRTC2 0x0800
#define DEVF_MAVEN_CAPABLE 0x1000
#define DEVF_PANELLINK_CAPABLE 0x2000
#define DEVF_G450DAC 0x4000
#define DEVF_GCORE (DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB)
#define DEVF_G2CORE (DEVF_GCORE | DEVF_ANY_VXRES | DEVF_MAVEN_CAPABLE | DEVF_PANELLINK_CAPABLE | DEVF_SRCORG | DEVF_DUALHEAD)
#define DEVF_G100 (DEVF_GCORE) /* no doc, no vxres... */
#define DEVF_G200 (DEVF_G2CORE)
#define DEVF_G400 (DEVF_G2CORE | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2)
/* if you'll find how to drive DFP... */
#define DEVF_G450 (DEVF_GCORE | DEVF_ANY_VXRES | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2 | DEVF_G450DAC | DEVF_SRCORG | DEVF_DUALHEAD)
#define DEVF_G550 (DEVF_G450)
static struct board
{
unsigned short vendor
, device
, rev
, svid
, sid
;
unsigned int flags
;
unsigned int maxclk
;
enum mga_chip chip
;
struct video_board
* base
;
const char* name
;
} dev_list
[] = {
#ifdef CONFIG_FB_MATROX_MILLENIUM
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_MIL
, 0xFF,
0, 0,
DEVF_TEXT4B
,
230000,
MGA_2064
,
&vbMillennium
,
"Millennium (PCI)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_MIL_2
, 0xFF,
0, 0,
DEVF_SWAPS
,
220000,
MGA_2164
,
&vbMillennium2
,
"Millennium II (PCI)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_MIL_2_AGP
, 0xFF,
0, 0,
DEVF_SWAPS
,
250000,
MGA_2164
,
&vbMillennium2A
,
"Millennium II (AGP)"},
#endif
#ifdef CONFIG_FB_MATROX_MYSTIQUE
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_MYS
, 0x02,
0, 0,
DEVF_VIDEO64BIT
| DEVF_CROSS4MB
,
180000,
MGA_1064
,
&vbMystique
,
"Mystique (PCI)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_MYS
, 0xFF,
0, 0,
DEVF_VIDEO64BIT
| DEVF_SWAPS
| DEVF_CROSS4MB
,
220000,
MGA_1164
,
&vbMystique
,
"Mystique 220 (PCI)"},
#endif
#ifdef CONFIG_FB_MATROX_G100
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G100_MM
, 0xFF,
0, 0,
DEVF_G100
,
230000,
MGA_G100
,
&vbG100
,
"MGA-G100 (PCI)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G100_AGP
, 0xFF,
0, 0,
DEVF_G100
,
230000,
MGA_G100
,
&vbG100
,
"MGA-G100 (AGP)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G200_PCI
, 0xFF,
0, 0,
DEVF_G200
,
250000,
MGA_G200
,
&vbG200
,
"MGA-G200 (PCI)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G200_AGP
, 0xFF,
PCI_SS_VENDOR_ID_MATROX
, PCI_SS_ID_MATROX_GENERIC
,
DEVF_G200
,
220000,
MGA_G200
,
&vbG200
,
"MGA-G200 (AGP)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G200_AGP
, 0xFF,
PCI_SS_VENDOR_ID_MATROX
, PCI_SS_ID_MATROX_MYSTIQUE_G200_AGP
,
DEVF_G200
,
230000,
MGA_G200
,
&vbG200
,
"Mystique G200 (AGP)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G200_AGP
, 0xFF,
PCI_SS_VENDOR_ID_MATROX
, PCI_SS_ID_MATROX_MILLENIUM_G200_AGP
,
DEVF_G200
,
250000,
MGA_G200
,
&vbG200
,
"Millennium G200 (AGP)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G200_AGP
, 0xFF,
PCI_SS_VENDOR_ID_MATROX
, PCI_SS_ID_MATROX_MARVEL_G200_AGP
,
DEVF_G200
,
230000,
MGA_G200
,
&vbG200
,
"Marvel G200 (AGP)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G200_AGP
, 0xFF,
PCI_SS_VENDOR_ID_SIEMENS_NIXDORF
, PCI_SS_ID_SIEMENS_MGA_G200_AGP
,
DEVF_G200
,
230000,
MGA_G200
,
&vbG200
,
"MGA-G200 (AGP)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G200_AGP
, 0xFF,
0, 0,
DEVF_G200
,
230000,
MGA_G200
,
&vbG200
,
"G200 (AGP)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G400
, 0x80,
PCI_SS_VENDOR_ID_MATROX
, PCI_SS_ID_MATROX_MILLENNIUM_G400_MAX_AGP
,
DEVF_G400
,
360000,
MGA_G400
,
&vbG400
,
"Millennium G400 MAX (AGP)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G400
, 0x80,
0, 0,
DEVF_G400
,
300000,
MGA_G400
,
&vbG400
,
"G400 (AGP)"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G400
, 0xFF,
0, 0,
DEVF_G450
,
360000,
MGA_G450
,
&vbG400
,
"G450"},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G550
, 0xFF,
0, 0,
DEVF_G550
,
360000,
MGA_G550
,
&vbG400
,
"G550"},
#endif
{0, 0, 0xFF,
0, 0,
0,
0,
0,
NULL
,
NULL
}};
#ifndef MODULE
static struct fb_videomode defaultmode
= {
/* 640x480 @ 60Hz, 31.5 kHz */
NULL
, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
0, FB_VMODE_NONINTERLACED
};
#endif /* !MODULE */
static int hotplug
= 0;
static int initMatrox2
(WPMINFO
struct board
* b
){
unsigned long ctrlptr_phys
= 0;
unsigned long video_base_phys
= 0;
unsigned int memsize
;
int err
;
DBG
(__FUNCTION__
)
/* set default values... */
vesafb_defined.
accel_flags = FB_ACCELF_TEXT
;
ACCESS_FBINFO
(hw_switch
) = b
->base
->lowlevel
;
ACCESS_FBINFO
(devflags.
accelerator) = b
->base
->accelID
;
ACCESS_FBINFO
(max_pixel_clock
) = b
->maxclk
;
printk
(KERN_INFO
"matroxfb: Matrox %s detected\n", b
->name
);
ACCESS_FBINFO
(capable.
plnwt) = 1;
ACCESS_FBINFO
(chip
) = b
->chip
;
ACCESS_FBINFO
(capable.
srcorg) = b
->flags
& DEVF_SRCORG
;
ACCESS_FBINFO
(devflags.
video64bits) = b
->flags
& DEVF_VIDEO64BIT
;
if (b
->flags
& DEVF_TEXT4B
) {
ACCESS_FBINFO
(devflags.
vgastep) = 4;
ACCESS_FBINFO
(devflags.
textmode) = 4;
ACCESS_FBINFO
(devflags.
text_type_aux) = FB_AUX_TEXT_MGA_STEP16
;
} else if (b
->flags
& DEVF_TEXT16B
) {
ACCESS_FBINFO
(devflags.
vgastep) = 16;
ACCESS_FBINFO
(devflags.
textmode) = 1;
ACCESS_FBINFO
(devflags.
text_type_aux) = FB_AUX_TEXT_MGA_STEP16
;
} else {
ACCESS_FBINFO
(devflags.
vgastep) = 8;
ACCESS_FBINFO
(devflags.
textmode) = 1;
ACCESS_FBINFO
(devflags.
text_type_aux) = FB_AUX_TEXT_MGA_STEP8
;
}
#ifdef CONFIG_FB_MATROX_32MB
ACCESS_FBINFO
(devflags.
support32MB) = (b
->flags
& DEVF_SUPPORT32MB
) != 0;
#endif
ACCESS_FBINFO
(devflags.
precise_width) = !(b
->flags
& DEVF_ANY_VXRES
);
ACCESS_FBINFO
(devflags.
crtc2) = (b
->flags
& DEVF_CRTC2
) != 0;
ACCESS_FBINFO
(devflags.
maven_capable) = (b
->flags
& DEVF_MAVEN_CAPABLE
) != 0;
ACCESS_FBINFO
(devflags.
dualhead) = (b
->flags
& DEVF_DUALHEAD
) != 0;
if (b
->flags
& DEVF_PANELLINK_CAPABLE
) {
ACCESS_FBINFO
(outputs
[2]).
data = MINFO
;
ACCESS_FBINFO
(outputs
[2]).
output = &panellink_output
;
if (dfp
)
ACCESS_FBINFO
(outputs
[2]).
src = MATROXFB_SRC_CRTC1
;
else
ACCESS_FBINFO
(outputs
[2]).
src = MATROXFB_SRC_NONE
;
ACCESS_FBINFO
(outputs
[2]).
mode = MATROXFB_OUTPUT_MODE_MONITOR
;
ACCESS_FBINFO
(devflags.
panellink) = 1;
}
ACCESS_FBINFO
(devflags.
dfp_type) = dfp_type
;
ACCESS_FBINFO
(devflags.
g450dac) = (b
->flags
& DEVF_G450DAC
) != 0;
ACCESS_FBINFO
(devflags.
textstep) = ACCESS_FBINFO
(devflags.
vgastep) * ACCESS_FBINFO
(devflags.
textmode);
ACCESS_FBINFO
(devflags.
textvram) = 65536 / ACCESS_FBINFO
(devflags.
textmode);
if (ACCESS_FBINFO
(capable.
cross4MB) < 0)
ACCESS_FBINFO
(capable.
cross4MB) = b
->flags
& DEVF_CROSS4MB
;
if (b
->flags
& DEVF_SWAPS
) {
ctrlptr_phys
= pci_resource_start
(ACCESS_FBINFO
(pcidev
), 1);
video_base_phys
= pci_resource_start
(ACCESS_FBINFO
(pcidev
), 0);
ACCESS_FBINFO
(devflags.
fbResource) = PCI_BASE_ADDRESS_0
;
} else {
ctrlptr_phys
= pci_resource_start
(ACCESS_FBINFO
(pcidev
), 0);
video_base_phys
= pci_resource_start
(ACCESS_FBINFO
(pcidev
), 1);
ACCESS_FBINFO
(devflags.
fbResource) = PCI_BASE_ADDRESS_1
;
}
err
= -EINVAL
;
if (!ctrlptr_phys
) {
printk
(KERN_ERR
"matroxfb: control registers are not available, matroxfb disabled\n");
goto fail
;
}
if (!video_base_phys
) {
printk
(KERN_ERR
"matroxfb: video RAM is not available in PCI address space, matroxfb disabled\n");
goto fail
;
}
memsize
= b
->base
->maxvram
;
if (!request_mem_region
(ctrlptr_phys
, 16384, "matroxfb MMIO")) {
goto fail
;
}
if (!request_mem_region
(video_base_phys
, memsize
, "matroxfb FB")) {
goto failCtrlMR
;
}
ACCESS_FBINFO
(video.
len_maximum) = memsize
;
/* convert mem (autodetect k, M) */
if (mem
< 1024) mem
*= 1024;
if (mem
< 0x00100000) mem
*= 1024;
if (mem
&& (mem
< memsize
))
memsize
= mem
;
err
= -ENOMEM
;
if (mga_ioremap
(ctrlptr_phys
, 16384, MGA_IOREMAP_MMIO
, &ACCESS_FBINFO
(mmio.
vbase))) {
printk
(KERN_ERR
"matroxfb: cannot ioremap(%lX, 16384), matroxfb disabled\n", ctrlptr_phys
);
goto failVideoMR
;
}
ACCESS_FBINFO
(mmio.
base) = ctrlptr_phys
;
ACCESS_FBINFO
(mmio.
len) = 16384;
ACCESS_FBINFO
(video.
base) = video_base_phys
;
if (mga_ioremap
(video_base_phys
, memsize
, MGA_IOREMAP_FB
, &ACCESS_FBINFO
(video.
vbase))) {
printk
(KERN_ERR
"matroxfb: cannot ioremap(%lX, %d), matroxfb disabled\n",
video_base_phys
, memsize
);
goto failCtrlIO
;
}
{
u_int32_t cmd
;
u_int32_t mga_option
;
pci_read_config_dword
(ACCESS_FBINFO
(pcidev
), PCI_OPTION_REG
, (void *)&mga_option
);
pci_read_config_dword
(ACCESS_FBINFO
(pcidev
), PCI_COMMAND
, (void *)&cmd
);
mga_option
&= 0x7FFFFFFF; /* clear BIG_ENDIAN */
mga_option
|= MX_OPTION_BSWAP
;
/* disable palette snooping */
cmd
&= ~PCI_COMMAND_VGA_PALETTE
;
if (pci_find_device
(PCI_VENDOR_ID_INTEL
, PCI_DEVICE_ID_INTEL_82437
, NULL
)) {
if (!(mga_option
& 0x20000000) && !ACCESS_FBINFO
(devflags.
nopciretry)) {
printk
(KERN_WARNING
"matroxfb: Disabling PCI retries due to i82437 present\n");
}
mga_option
|= 0x20000000;
ACCESS_FBINFO
(devflags.
nopciretry) = 1;
}
pci_write_config_dword
(ACCESS_FBINFO
(pcidev
), PCI_COMMAND
, cmd
);
pci_write_config_dword
(ACCESS_FBINFO
(pcidev
), PCI_OPTION_REG
, mga_option
);
ACCESS_FBINFO
(hw
).
MXoptionReg = mga_option
;
/* select non-DMA memory for PCI_MGA_DATA, otherwise dump of PCI cfg space can lock PCI bus */
/* maybe preinit() candidate, but it is same... for all devices... at this time... */
pci_write_config_dword
(ACCESS_FBINFO
(pcidev
), PCI_MGA_INDEX
, 0x00003C00);
}
err
= -ENXIO
;
matroxfb_read_pins
(PMINFO2
);
if (ACCESS_FBINFO
(hw_switch
)->preinit
(PMINFO2
)) {
goto failVideoIO
;
}
err
= -ENOMEM
;
if (!matroxfb_getmemory
(PMINFO memsize
, &ACCESS_FBINFO
(video.
len)) || !ACCESS_FBINFO
(video.
len)) {
printk
(KERN_ERR
"matroxfb: cannot determine memory size\n");
goto failVideoIO
;
}
ACCESS_FBINFO
(devflags.
ydstorg) = 0;
ACCESS_FBINFO
(fbcon.
currcon) = -1;
ACCESS_FBINFO
(video.
base) = video_base_phys
;
ACCESS_FBINFO
(video.
len_usable) = ACCESS_FBINFO
(video.
len);
if (ACCESS_FBINFO
(video.
len_usable) > b
->base
->maxdisplayable
)
ACCESS_FBINFO
(video.
len_usable) = b
->base
->maxdisplayable
;
#ifdef CONFIG_MTRR
if (mtrr
) {
ACCESS_FBINFO
(mtrr.
vram) = mtrr_add
(video_base_phys
, ACCESS_FBINFO
(video.
len), MTRR_TYPE_WRCOMB
, 1);
ACCESS_FBINFO
(mtrr.
vram_valid) = 1;
printk
(KERN_INFO
"matroxfb: MTRR's turned on\n");
}
#endif /* CONFIG_MTRR */
if (!ACCESS_FBINFO
(devflags.
novga))
request_region
(0x3C0, 32, "matrox");
matroxfb_g450_connect
(PMINFO2
);
ACCESS_FBINFO
(hw_switch
->reset
(PMINFO2
));
ACCESS_FBINFO
(fbcon.
monspecs.
hfmin) = 0;
ACCESS_FBINFO
(fbcon.
monspecs.
hfmax) = fh
;
ACCESS_FBINFO
(fbcon.
monspecs.
vfmin) = 0;
ACCESS_FBINFO
(fbcon.
monspecs.
vfmax) = fv
;
ACCESS_FBINFO
(fbcon.
monspecs.
dpms) = 0; /* TBD */
/* static settings */
vesafb_defined.
red = colors
[depth
-1].
red;
vesafb_defined.
green = colors
[depth
-1].
green;
vesafb_defined.
blue = colors
[depth
-1].
blue;
vesafb_defined.
bits_per_pixel = colors
[depth
-1].
bits_per_pixel;
vesafb_defined.
grayscale = grayscale
;
vesafb_defined.
vmode = 0;
if (noaccel
)
vesafb_defined.
accel_flags &= ~FB_ACCELF_TEXT
;
ACCESS_FBINFO
(fbops
) = matroxfb_ops
;
ACCESS_FBINFO
(fbcon.
fbops) = &ACCESS_FBINFO
(fbops
);
ACCESS_FBINFO
(fbcon.
pseudo_palette) = ACCESS_FBINFO
(cmap
);
/* after __init time we are like module... no logo */
ACCESS_FBINFO
(fbcon.
flags) = hotplug
? FBINFO_FLAG_MODULE
: FBINFO_FLAG_DEFAULT
;
ACCESS_FBINFO
(video.
len_usable) &= PAGE_MASK
;
fb_alloc_cmap
(&ACCESS_FBINFO
(fbcon.
cmap), 256, 1);
#ifndef MODULE
/* mode database is marked __init!!! */
if (!hotplug
) {
fb_find_mode
(&vesafb_defined
, &ACCESS_FBINFO
(fbcon
), videomode
[0]?videomode
:NULL
,
NULL
, 0, &defaultmode
, vesafb_defined.
bits_per_pixel);
}
#endif /* !MODULE */
/* mode modifiers */
if (hslen
)
vesafb_defined.
hsync_len = hslen
;
if (vslen
)
vesafb_defined.
vsync_len = vslen
;
if (left
!= ~
0)
vesafb_defined.
left_margin = left
;
if (right
!= ~
0)
vesafb_defined.
right_margin = right
;
if (upper
!= ~
0)
vesafb_defined.
upper_margin = upper
;
if (lower
!= ~
0)
vesafb_defined.
lower_margin = lower
;
if (xres
)
vesafb_defined.
xres = xres
;
if (yres
)
vesafb_defined.
yres = yres
;
if (sync
!= -1)
vesafb_defined.
sync = sync
;
else if (vesafb_defined.
sync == ~
0) {
vesafb_defined.
sync = 0;
if (yres
< 400)
vesafb_defined.
sync |= FB_SYNC_HOR_HIGH_ACT
;
else if (yres
< 480)
vesafb_defined.
sync |= FB_SYNC_VERT_HIGH_ACT
;
}
/* fv, fh, maxclk limits was specified */
{
unsigned int tmp
;
if (fv
) {
tmp
= fv
* (vesafb_defined.
upper_margin + vesafb_defined.
yres
+ vesafb_defined.
lower_margin + vesafb_defined.
vsync_len);
if ((tmp
< fh
) || (fh
== 0)) fh
= tmp
;
}
if (fh
) {
tmp
= fh
* (vesafb_defined.
left_margin + vesafb_defined.
xres
+ vesafb_defined.
right_margin + vesafb_defined.
hsync_len);
if ((tmp
< maxclk
) || (maxclk
== 0)) maxclk
= tmp
;
}
tmp
= (maxclk
+ 499) / 500;
if (tmp
) {
tmp
= (2000000000 + tmp
) / tmp
;
if (tmp
> pixclock
) pixclock
= tmp
;
}
}
if (pixclock
) {
if (pixclock
< 2000) /* > 500MHz */
pixclock
= 4000; /* 250MHz */
if (pixclock
> 1000000)
pixclock
= 1000000; /* 1MHz */
vesafb_defined.
pixclock = pixclock
;
}
/* FIXME: Where to move this?! */
#if defined(CONFIG_PPC_PMAC)
#ifndef MODULE
if (_machine
== _MACH_Pmac
) {
struct fb_var_screeninfo var
;
if (default_vmode
<= 0 || default_vmode
> VMODE_MAX
)
default_vmode
= VMODE_640_480_60
;
#ifdef CONFIG_NVRAM
if (default_cmode
== CMODE_NVRAM
)
default_cmode
= nvram_read_byte
(NV_CMODE
);
#endif
if (default_cmode
< CMODE_8
|| default_cmode
> CMODE_32
)
default_cmode
= CMODE_8
;
if (!mac_vmode_to_var
(default_vmode
, default_cmode
, &var
)) {
var.
accel_flags = vesafb_defined.
accel_flags;
var.
xoffset = var.
yoffset = 0;
/* Note: mac_vmode_to_var() does not set all parameters */
vesafb_defined
= var
;
}
}
#endif /* !MODULE */
#endif /* CONFIG_PPC_PMAC */
vesafb_defined.
xres_virtual = vesafb_defined.
xres;
if (nopan
) {
vesafb_defined.
yres_virtual = vesafb_defined.
yres;
} else {
vesafb_defined.
yres_virtual = 65536; /* large enough to be INF, but small enough
to yres_virtual * xres_virtual < 2^32 */
}
matroxfb_init_fix
(PMINFO2
);
err
= -EINVAL
;
printk
(KERN_INFO
"matroxfb: %dx%dx%dbpp (virtual: %dx%d)\n",
vesafb_defined.
xres, vesafb_defined.
yres, vesafb_defined.
bits_per_pixel,
vesafb_defined.
xres_virtual, vesafb_defined.
yres_virtual);
printk
(KERN_INFO
"matroxfb: framebuffer at 0x%lX, mapped to 0x%p, size %d\n",
ACCESS_FBINFO
(video.
base), vaddr_va
(ACCESS_FBINFO
(video.
vbase)), ACCESS_FBINFO
(video.
len));
/* We do not have to set currcon to 0... register_framebuffer do it for us on first console
* and we do not want currcon == 0 for subsequent framebuffers */
if (register_framebuffer
(&ACCESS_FBINFO
(fbcon
)) < 0) {
goto failVideoIO
;
}
printk
("fb%d: %s frame buffer device\n",
ACCESS_FBINFO
(fbcon.
node), ACCESS_FBINFO
(fbcon.
fix.
id));
if (ACCESS_FBINFO
(fbcon.
currcon) < 0) {
/* there is no console on this fb... but we have to initialize hardware
* until someone tells me what is proper thing to do */
printk
(KERN_INFO
"fb%d: initializing hardware\n",
ACCESS_FBINFO
(fbcon.
node));
fb_set_var
(&ACCESS_FBINFO
(fbcon
), &vesafb_defined
);
}
return 0;
failVideoIO
:;
matroxfb_g450_shutdown
(PMINFO2
);
mga_iounmap
(ACCESS_FBINFO
(video.
vbase));
failCtrlIO
:;
mga_iounmap
(ACCESS_FBINFO
(mmio.
vbase));
failVideoMR
:;
release_mem_region
(video_base_phys
, ACCESS_FBINFO
(video.
len_maximum));
failCtrlMR
:;
release_mem_region
(ctrlptr_phys
, 16384);
fail
:;
return err
;
}
LIST_HEAD
(matroxfb_list
);
LIST_HEAD
(matroxfb_driver_list
);
#define matroxfb_l(x) list_entry(x, struct matrox_fb_info, next_fb)
#define matroxfb_driver_l(x) list_entry(x, struct matroxfb_driver, node)
int matroxfb_register_driver
(struct matroxfb_driver
* drv
) {
struct matrox_fb_info
* minfo
;
list_add
(&drv
->node
, &matroxfb_driver_list
);
for (minfo
= matroxfb_l
(matroxfb_list.
next);
minfo
!= matroxfb_l
(&matroxfb_list
);
minfo
= matroxfb_l
(minfo
->next_fb.
next)) {
void* p
;
if (minfo
->drivers_count
== MATROXFB_MAX_FB_DRIVERS
)
continue;
p
= drv
->probe
(minfo
);
if (p
) {
minfo
->drivers_data
[minfo
->drivers_count
] = p
;
minfo
->drivers
[minfo
->drivers_count
++] = drv
;
}
}
return 0;
}
void matroxfb_unregister_driver
(struct matroxfb_driver
* drv
) {
struct matrox_fb_info
* minfo
;
list_del
(&drv
->node
);
for (minfo
= matroxfb_l
(matroxfb_list.
next);
minfo
!= matroxfb_l
(&matroxfb_list
);
minfo
= matroxfb_l
(minfo
->next_fb.
next)) {
int i
;
for (i
= 0; i
< minfo
->drivers_count
; ) {
if (minfo
->drivers
[i
] == drv
) {
if (drv
&& drv
->remove)
drv
->remove(minfo
, minfo
->drivers_data
[i
]);
minfo
->drivers
[i
] = minfo
->drivers
[--minfo
->drivers_count
];
minfo
->drivers_data
[i
] = minfo
->drivers_data
[minfo
->drivers_count
];
} else
i
++;
}
}
}
static void matroxfb_register_device
(struct matrox_fb_info
* minfo
) {
struct matroxfb_driver
* drv
;
int i
= 0;
list_add
(&ACCESS_FBINFO
(next_fb
), &matroxfb_list
);
for (drv
= matroxfb_driver_l
(matroxfb_driver_list.
next);
drv
!= matroxfb_driver_l
(&matroxfb_driver_list
);
drv
= matroxfb_driver_l
(drv
->node.
next)) {
if (drv
&& drv
->probe
) {
void *p
= drv
->probe
(minfo
);
if (p
) {
minfo
->drivers_data
[i
] = p
;
minfo
->drivers
[i
++] = drv
;
if (i
== MATROXFB_MAX_FB_DRIVERS
)
break;
}
}
}
minfo
->drivers_count
= i
;
}
static void matroxfb_unregister_device
(struct matrox_fb_info
* minfo
) {
int i
;
list_del
(&ACCESS_FBINFO
(next_fb
));
for (i
= 0; i
< minfo
->drivers_count
; i
++) {
struct matroxfb_driver
* drv
= minfo
->drivers
[i
];
if (drv
&& drv
->remove)
drv
->remove(minfo
, minfo
->drivers_data
[i
]);
}
}
static int matroxfb_probe
(struct pci_dev
* pdev
, const struct pci_device_id
* dummy
) {
struct board
* b
;
u_int8_t rev
;
u_int16_t svid
;
u_int16_t sid
;
struct matrox_fb_info
* minfo
;
int err
;
u_int32_t cmd
;
#ifndef CONFIG_FB_MATROX_MULTIHEAD
static int registered
= 0;
#endif
DBG
(__FUNCTION__
)
pci_read_config_byte
(pdev
, PCI_REVISION_ID
, &rev
);
svid
= pdev
->subsystem_vendor
;
sid
= pdev
->subsystem_device
;
for (b
= dev_list
; b
->vendor
; b
++) {
if ((b
->vendor
!= pdev
->vendor
) || (b
->device
!= pdev
->device
) || (b
->rev
< rev
)) continue;
if (b
->svid
)
if ((b
->svid
!= svid
) || (b
->sid
!= sid
)) continue;
break;
}
/* not match... */
if (!b
->vendor
)
return -1;
if (dev
> 0) {
/* not requested one... */
dev
--;
return -1;
}
pci_read_config_dword
(pdev
, PCI_COMMAND
, (void *)&cmd
);
if (pci_enable_device
(pdev
)) {
return -1;
}
#ifdef CONFIG_FB_MATROX_MULTIHEAD
minfo
= (struct matrox_fb_info
*)kmalloc
(sizeof(*minfo
), GFP_KERNEL
);
if (!minfo
)
return -1;
#else
if (registered
) /* singlehead driver... */
return -1;
minfo
= &matroxfb_global_mxinfo
;
#endif
memset(MINFO
, 0, sizeof(*MINFO
));
ACCESS_FBINFO
(pcidev
) = pdev
;
ACCESS_FBINFO
(dead
) = 0;
ACCESS_FBINFO
(usecount
) = 0;
ACCESS_FBINFO
(userusecount
) = 0;
pci_set_drvdata
(pdev
, MINFO
);
/* DEVFLAGS */
ACCESS_FBINFO
(devflags.
memtype) = memtype
;
if (memtype
!= -1)
noinit
= 0;
if (cmd
& PCI_COMMAND_MEMORY
) {
ACCESS_FBINFO
(devflags.
novga) = novga
;
ACCESS_FBINFO
(devflags.
nobios) = nobios
;
ACCESS_FBINFO
(devflags.
noinit) = noinit
;
/* subsequent heads always needs initialization and must not enable BIOS */
novga
= 1;
nobios
= 1;
noinit
= 0;
} else {
ACCESS_FBINFO
(devflags.
novga) = 1;
ACCESS_FBINFO
(devflags.
nobios) = 1;
ACCESS_FBINFO
(devflags.
noinit) = 0;
}
ACCESS_FBINFO
(devflags.
nopciretry) = no_pci_retry
;
ACCESS_FBINFO
(devflags.
mga_24bpp_fix) = inv24
;
ACCESS_FBINFO
(devflags.
precise_width) = option_precise_width
;
ACCESS_FBINFO
(devflags.
sgram) = sgram
;
ACCESS_FBINFO
(capable.
cross4MB) = cross4MB
;
spin_lock_init
(&ACCESS_FBINFO
(lock.
DAC));
spin_lock_init
(&ACCESS_FBINFO
(lock.
accel));
init_rwsem
(&ACCESS_FBINFO
(crtc2.
lock));
init_rwsem
(&ACCESS_FBINFO
(altout.
lock));
ACCESS_FBINFO
(irq_flags
) = 0;
//init_waitqueue_head(&ACCESS_FBINFO(crtc1.vsync.wait));
//init_waitqueue_head(&ACCESS_FBINFO(crtc2.vsync.wait));
ACCESS_FBINFO
(crtc1.
panpos) = -1;
err
= initMatrox2
(PMINFO b
);
if (!err
) {
#ifndef CONFIG_FB_MATROX_MULTIHEAD
registered
= 1;
#endif
matroxfb_register_device
(MINFO
);
return 0;
}
#ifdef CONFIG_FB_MATROX_MULTIHEAD
kfree
(minfo
);
#endif
return -1;
}
static void pci_remove_matrox
(struct pci_dev
* pdev
) {
struct matrox_fb_info
* minfo
;
minfo
= pci_get_drvdata
(pdev
);
matroxfb_remove
(PMINFO
1);
}
static struct pci_device_id matroxfb_devices
[] = {
#ifdef CONFIG_FB_MATROX_MILLENIUM
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_MIL
,
PCI_ANY_ID
, PCI_ANY_ID
, 0, 0, 0},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_MIL_2
,
PCI_ANY_ID
, PCI_ANY_ID
, 0, 0, 0},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_MIL_2_AGP
,
PCI_ANY_ID
, PCI_ANY_ID
, 0, 0, 0},
#endif
#ifdef CONFIG_FB_MATROX_MYSTIQUE
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_MYS
,
PCI_ANY_ID
, PCI_ANY_ID
, 0, 0, 0},
#endif
#ifdef CONFIG_FB_MATROX_G100
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G100_MM
,
PCI_ANY_ID
, PCI_ANY_ID
, 0, 0, 0},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G100_AGP
,
PCI_ANY_ID
, PCI_ANY_ID
, 0, 0, 0},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G200_PCI
,
PCI_ANY_ID
, PCI_ANY_ID
, 0, 0, 0},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G200_AGP
,
PCI_ANY_ID
, PCI_ANY_ID
, 0, 0, 0},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G400
,
PCI_ANY_ID
, PCI_ANY_ID
, 0, 0, 0},
{PCI_VENDOR_ID_MATROX
, PCI_DEVICE_ID_MATROX_G550
,
PCI_ANY_ID
, PCI_ANY_ID
, 0, 0, 0},
#endif
{0, 0,
0, 0, 0, 0, 0}
};
MODULE_DEVICE_TABLE
(pci
, matroxfb_devices
);
static struct pci_driver matroxfb_driver
= {
.
name = "matroxfb",
.
id_table = matroxfb_devices
,
.
probe = matroxfb_probe
,
.
remove = pci_remove_matrox
,
};
/* **************************** init-time only **************************** */
#define RSResolution(X) ((X) & 0x0F)
#define RS640x400 1
#define RS640x480 2
#define RS800x600 3
#define RS1024x768 4
#define RS1280x1024 5
#define RS1600x1200 6
#define RS768x576 7
#define RS960x720 8
#define RS1152x864 9
#define RS1408x1056 10
#define RS640x350 11
#define RS1056x344 12 /* 132 x 43 text */
#define RS1056x400 13 /* 132 x 50 text */
#define RS1056x480 14 /* 132 x 60 text */
#define RSNoxNo 15
/* 10-FF */
static struct { int xres
, yres
, left
, right
, upper
, lower
, hslen
, vslen
, vfreq
; } timmings
[] __initdata
= {
{ 640, 400, 48, 16, 39, 8, 96, 2, 70 },
{ 640, 480, 48, 16, 33, 10, 96, 2, 60 },
{ 800, 600, 144, 24, 28, 8, 112, 6, 60 },
{ 1024, 768, 160, 32, 30, 4, 128, 4, 60 },
{ 1280, 1024, 224, 32, 32, 4, 136, 4, 60 },
{ 1600, 1200, 272, 48, 32, 5, 152, 5, 60 },
{ 768, 576, 144, 16, 28, 6, 112, 4, 60 },
{ 960, 720, 144, 24, 28, 8, 112, 4, 60 },
{ 1152, 864, 192, 32, 30, 4, 128, 4, 60 },
{ 1408, 1056, 256, 40, 32, 5, 144, 5, 60 },
{ 640, 350, 48, 16, 39, 8, 96, 2, 70 },
{ 1056, 344, 96, 24, 59, 44, 160, 2, 70 },
{ 1056, 400, 96, 24, 39, 8, 160, 2, 70 },
{ 1056, 480, 96, 24, 36, 12, 160, 3, 60 },
{ 0, 0, ~
0, ~
0, ~
0, ~
0, 0, 0, 0 }
};
#define RSCreate(X,Y) ((X) | ((Y) << 8))
static struct { unsigned int vesa
; unsigned int info
; } *RSptr
, vesamap
[] __initdata
= {
/* default must be first */
{ ~
0, RSCreate
(RSNoxNo
, RS8bpp
) },
{ 0x101, RSCreate
(RS640x480
, RS8bpp
) },
{ 0x100, RSCreate
(RS640x400
, RS8bpp
) },
{ 0x180, RSCreate
(RS768x576
, RS8bpp
) },
{ 0x103, RSCreate
(RS800x600
, RS8bpp
) },
{ 0x188, RSCreate
(RS960x720
, RS8bpp
) },
{ 0x105, RSCreate
(RS1024x768
, RS8bpp
) },
{ 0x190, RSCreate
(RS1152x864
, RS8bpp
) },
{ 0x107, RSCreate
(RS1280x1024
, RS8bpp
) },
{ 0x198, RSCreate
(RS1408x1056
, RS8bpp
) },
{ 0x11C, RSCreate
(RS1600x1200
, RS8bpp
) },
{ 0x110, RSCreate
(RS640x480
, RS15bpp
) },
{ 0x181, RSCreate
(RS768x576
, RS15bpp
) },
{ 0x113, RSCreate
(RS800x600
, RS15bpp
) },
{ 0x189, RSCreate
(RS960x720
, RS15bpp
) },
{ 0x116, RSCreate
(RS1024x768
, RS15bpp
) },
{ 0x191, RSCreate
(RS1152x864
, RS15bpp
) },
{ 0x119, RSCreate
(RS1280x1024
, RS15bpp
) },
{ 0x199, RSCreate
(RS1408x1056
, RS15bpp
) },
{ 0x11D, RSCreate
(RS1600x1200
, RS15bpp
) },
{ 0x111, RSCreate
(RS640x480
, RS16bpp
) },
{ 0x182, RSCreate
(RS768x576
, RS16bpp
) },
{ 0x114, RSCreate
(RS800x600
, RS16bpp
) },
{ 0x18A, RSCreate
(RS960x720
, RS16bpp
) },
{ 0x117, RSCreate
(RS1024x768
, RS16bpp
) },
{ 0x192, RSCreate
(RS1152x864
, RS16bpp
) },
{ 0x11A, RSCreate
(RS1280x1024
, RS16bpp
) },
{ 0x19A, RSCreate
(RS1408x1056
, RS16bpp
) },
{ 0x11E, RSCreate
(RS1600x1200
, RS16bpp
) },
{ 0x1B2, RSCreate
(RS640x480
, RS24bpp
) },
{ 0x184, RSCreate
(RS768x576
, RS24bpp
) },
{ 0x1B5, RSCreate
(RS800x600
, RS24bpp
) },
{ 0x18C, RSCreate
(RS960x720
, RS24bpp
) },
{ 0x1B8, RSCreate
(RS1024x768
, RS24bpp
) },
{ 0x194, RSCreate
(RS1152x864
, RS24bpp
) },
{ 0x1BB, RSCreate
(RS1280x1024
, RS24bpp
) },
{ 0x19C, RSCreate
(RS1408x1056
, RS24bpp
) },
{ 0x1BF, RSCreate
(RS1600x1200
, RS24bpp
) },
{ 0x112, RSCreate
(RS640x480
, RS32bpp
) },
{ 0x183, RSCreate
(RS768x576
, RS32bpp
) },
{ 0x115, RSCreate
(RS800x600
, RS32bpp
) },
{ 0x18B, RSCreate
(RS960x720
, RS32bpp
) },
{ 0x118, RSCreate
(RS1024x768
, RS32bpp
) },
{ 0x193, RSCreate
(RS1152x864
, RS32bpp
) },
{ 0x11B, RSCreate
(RS1280x1024
, RS32bpp
) },
{ 0x19B, RSCreate
(RS1408x1056
, RS32bpp
) },
{ 0x11F, RSCreate
(RS1600x1200
, RS32bpp
) },
{ 0x010, RSCreate
(RS640x350
, RS4bpp
) },
{ 0x012, RSCreate
(RS640x480
, RS4bpp
) },
{ 0x102, RSCreate
(RS800x600
, RS4bpp
) },
{ 0x104, RSCreate
(RS1024x768
, RS4bpp
) },
{ 0x106, RSCreate
(RS1280x1024
, RS4bpp
) },
{ 0, 0 }};
static void __init matroxfb_init_params
(void) {
/* fh from kHz to Hz */
if (fh
< 1000)
fh
*= 1000; /* 1kHz minimum */
/* maxclk */
if (maxclk
< 1000) maxclk
*= 1000; /* kHz -> Hz, MHz -> kHz */
if (maxclk
< 1000000) maxclk
*= 1000; /* kHz -> Hz, 1MHz minimum */
/* fix VESA number */
if (vesa
!= ~
0)
vesa
&= 0x1DFF; /* mask out clearscreen, acceleration and so on */
/* static settings */
for (RSptr
= vesamap
; RSptr
->vesa
; RSptr
++) {
if (RSptr
->vesa
== vesa
) break;
}
if (!RSptr
->vesa
) {
printk
(KERN_ERR
"Invalid vesa mode 0x%04X\n", vesa
);
RSptr
= vesamap
;
}
{
int res
= RSResolution
(RSptr
->info
)-1;
if (left
== ~
0)
left
= timmings
[res
].
left;
if (!xres
)
xres
= timmings
[res
].
xres;
if (right
== ~
0)
right
= timmings
[res
].
right;
if (!hslen
)
hslen
= timmings
[res
].
hslen;
if (upper
== ~
0)
upper
= timmings
[res
].
upper;
if (!yres
)
yres
= timmings
[res
].
yres;
if (lower
== ~
0)
lower
= timmings
[res
].
lower;
if (!vslen
)
vslen
= timmings
[res
].
vslen;
if (!(fv
||fh
||maxclk
||pixclock
))
fv
= timmings
[res
].
vfreq;
if (depth
== -1)
depth
= RSDepth
(RSptr
->info
);
}
}
static void __init matrox_init
(void) {
matroxfb_init_params
();
pci_register_driver
(&matroxfb_driver
);
dev
= -1; /* accept all new devices... */
}
/* **************************** exit-time only **************************** */
static void __exit matrox_done
(void) {
pci_unregister_driver
(&matroxfb_driver
);
}
#ifndef MODULE
/* ************************* init in-kernel code ************************** */
int __init matroxfb_setup
(char *options
) {
char *this_opt
;
DBG
(__FUNCTION__
)
if (!options
|| !*options
)
return 0;
while ((this_opt
= strsep
(&options
, ",")) != NULL
) {
if (!*this_opt
) continue;
dprintk
("matroxfb_setup: option %s\n", this_opt
);
if (!strncmp(this_opt
, "dev:", 4))
dev
= simple_strtoul
(this_opt
+4, NULL
, 0);
else if (!strncmp(this_opt
, "depth:", 6)) {
switch (simple_strtoul
(this_opt
+6, NULL
, 0)) {
case 0: depth
= RSText
; break;
case 4: depth
= RS4bpp
; break;
case 8: depth
= RS8bpp
; break;
case 15:depth
= RS15bpp
; break;
case 16:depth
= RS16bpp
; break;
case 24:depth
= RS24bpp
; break;
case 32:depth
= RS32bpp
; break;
default:
printk
(KERN_ERR
"matroxfb: unsupported color depth\n");
}
} else if (!strncmp(this_opt
, "xres:", 5))
xres
= simple_strtoul
(this_opt
+5, NULL
, 0);
else if (!strncmp(this_opt
, "yres:", 5))
yres
= simple_strtoul
(this_opt
+5, NULL
, 0);
else if (!strncmp(this_opt
, "vslen:", 6))
vslen
= simple_strtoul
(this_opt
+6, NULL
, 0);
else if (!strncmp(this_opt
, "hslen:", 6))
hslen
= simple_strtoul
(this_opt
+6, NULL
, 0);
else if (!strncmp(this_opt
, "left:", 5))
left
= simple_strtoul
(this_opt
+5, NULL
, 0);
else if (!strncmp(this_opt
, "right:", 6))
right
= simple_strtoul
(this_opt
+6, NULL
, 0);
else if (!strncmp(this_opt
, "upper:", 6))
upper
= simple_strtoul
(this_opt
+6, NULL
, 0);
else if (!strncmp(this_opt
, "lower:", 6))
lower
= simple_strtoul
(this_opt
+6, NULL
, 0);
else if (!strncmp(this_opt
, "pixclock:", 9))
pixclock
= simple_strtoul
(this_opt
+9, NULL
, 0);
else if (!strncmp(this_opt
, "sync:", 5))
sync
= simple_strtoul
(this_opt
+5, NULL
, 0);
else if (!strncmp(this_opt
, "vesa:", 5))
vesa
= simple_strtoul
(this_opt
+5, NULL
, 0);
else if (!strncmp(this_opt
, "maxclk:", 7))
maxclk
= simple_strtoul
(this_opt
+7, NULL
, 0);
else if (!strncmp(this_opt
, "fh:", 3))
fh
= simple_strtoul
(this_opt
+3, NULL
, 0);
else if (!strncmp(this_opt
, "fv:", 3))
fv
= simple_strtoul
(this_opt
+3, NULL
, 0);
else if (!strncmp(this_opt
, "mem:", 4))
mem
= simple_strtoul
(this_opt
+4, NULL
, 0);
else if (!strncmp(this_opt
, "mode:", 5))
strncpy(videomode
, this_opt
+5, sizeof(videomode
));
else if (!strncmp(this_opt
, "dfp:", 4)) {
dfp_type
= simple_strtoul
(this_opt
+4, NULL
, 0);
dfp
= 1;
}
#ifdef CONFIG_PPC_PMAC
else if (!strncmp(this_opt
, "vmode:", 6)) {
unsigned int vmode
= simple_strtoul
(this_opt
+6, NULL
, 0);
if (vmode
> 0 && vmode
<= VMODE_MAX
)
default_vmode
= vmode
;
} else if (!strncmp(this_opt
, "cmode:", 6)) {
unsigned int cmode
= simple_strtoul
(this_opt
+6, NULL
, 0);
switch (cmode
) {
case 0:
case 8:
default_cmode
= CMODE_8
;
break;
case 15:
case 16:
default_cmode
= CMODE_16
;
break;
case 24:
case 32:
default_cmode
= CMODE_32
;
break;
}
}
#endif
else if (!strcmp(this_opt
, "disabled")) /* nodisabled does not exist */
disabled
= 1;
else if (!strcmp(this_opt
, "enabled")) /* noenabled does not exist */
disabled
= 0;
else if (!strcmp(this_opt
, "sgram")) /* nosgram == sdram */
sgram
= 1;
else if (!strcmp(this_opt
, "sdram"))
sgram
= 0;
else if (!strncmp(this_opt
, "memtype:", 8))
memtype
= simple_strtoul
(this_opt
+8, NULL
, 0);
else {
int value
= 1;
if (!strncmp(this_opt
, "no", 2)) {
value
= 0;
this_opt
+= 2;
}
if (! strcmp(this_opt
, "inverse"))
inverse
= value
;
else if (!strcmp(this_opt
, "accel"))
noaccel
= !value
;
else if (!strcmp(this_opt
, "pan"))
nopan
= !value
;
else if (!strcmp(this_opt
, "pciretry"))
no_pci_retry
= !value
;
else if (!strcmp(this_opt
, "vga"))
novga
= !value
;
else if (!strcmp(this_opt
, "bios"))
nobios
= !value
;
else if (!strcmp(this_opt
, "init"))
noinit
= !value
;
#ifdef CONFIG_MTRR
else if (!strcmp(this_opt
, "mtrr"))
mtrr
= value
;
#endif
else if (!strcmp(this_opt
, "inv24"))
inv24
= value
;
else if (!strcmp(this_opt
, "cross4MB"))
cross4MB
= value
;
else if (!strcmp(this_opt
, "grayscale"))
grayscale
= value
;
else if (!strcmp(this_opt
, "dfp"))
dfp
= value
;
else {
strncpy(videomode
, this_opt
, sizeof(videomode
));
}
}
}
return 0;
}
static int __initdata initialized
= 0;
int __init matroxfb_init
(void)
{
DBG
(__FUNCTION__
)
if (disabled
)
return -ENXIO
;
if (!initialized
) {
initialized
= 1;
matrox_init
();
}
hotplug
= 1;
/* never return failure, user can hotplug matrox later... */
return 0;
}
#else
/* *************************** init module code **************************** */
MODULE_AUTHOR
("(c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
MODULE_DESCRIPTION
("Accelerated FBDev driver for Matrox Millennium/Mystique/G100/G200/G400/G450/G550");
MODULE_LICENSE
("GPL");
MODULE_PARM
(mem
, "i");
MODULE_PARM_DESC
(mem
, "Size of available memory in MB, KB or B (2,4,8,12,16MB, default=autodetect)");
MODULE_PARM
(disabled
, "i");
MODULE_PARM_DESC
(disabled
, "Disabled (0 or 1=disabled) (default=0)");
MODULE_PARM
(noaccel
, "i");
MODULE_PARM_DESC
(noaccel
, "Do not use accelerating engine (0 or 1=disabled) (default=0)");
MODULE_PARM
(nopan
, "i");
MODULE_PARM_DESC
(nopan
, "Disable pan on startup (0 or 1=disabled) (default=0)");
MODULE_PARM
(no_pci_retry
, "i");
MODULE_PARM_DESC
(no_pci_retry
, "PCI retries enabled (0 or 1=disabled) (default=0)");
MODULE_PARM
(novga
, "i");
MODULE_PARM_DESC
(novga
, "VGA I/O (0x3C0-0x3DF) disabled (0 or 1=disabled) (default=0)");
MODULE_PARM
(nobios
, "i");
MODULE_PARM_DESC
(nobios
, "Disables ROM BIOS (0 or 1=disabled) (default=do not change BIOS state)");
MODULE_PARM
(noinit
, "i");
MODULE_PARM_DESC
(noinit
, "Disables W/SG/SD-RAM and bus interface initialization (0 or 1=do not initialize) (default=0)");
MODULE_PARM
(memtype
, "i");
MODULE_PARM_DESC
(memtype
, "Memory type for G200/G400 (see Documentation/fb/matroxfb.txt for explanation) (default=3 for G200, 0 for G400)");
MODULE_PARM
(mtrr
, "i");
MODULE_PARM_DESC
(mtrr
, "This speeds up video memory accesses (0=disabled or 1) (default=1)");
MODULE_PARM
(sgram
, "i");
MODULE_PARM_DESC
(sgram
, "Indicates that G100/G200/G400 has SGRAM memory (0=SDRAM, 1=SGRAM) (default=0)");
MODULE_PARM
(inv24
, "i");
MODULE_PARM_DESC
(inv24
, "Inverts clock polarity for 24bpp and loop frequency > 100MHz (default=do not invert polarity)");
MODULE_PARM
(inverse
, "i");
MODULE_PARM_DESC
(inverse
, "Inverse (0 or 1) (default=0)");
#ifdef CONFIG_FB_MATROX_MULTIHEAD
MODULE_PARM
(dev
, "i");
MODULE_PARM_DESC
(dev
, "Multihead support, attach to device ID (0..N) (default=all working)");
#else
MODULE_PARM
(dev
, "i");
MODULE_PARM_DESC
(dev
, "Multihead support, attach to device ID (0..N) (default=first working)");
#endif
MODULE_PARM
(vesa
, "i");
MODULE_PARM_DESC
(vesa
, "Startup videomode (0x000-0x1FF) (default=0x101)");
MODULE_PARM
(xres
, "i");
MODULE_PARM_DESC
(xres
, "Horizontal resolution (px), overrides xres from vesa (default=vesa)");
MODULE_PARM
(yres
, "i");
MODULE_PARM_DESC
(yres
, "Vertical resolution (scans), overrides yres from vesa (default=vesa)");
MODULE_PARM
(upper
, "i");
MODULE_PARM_DESC
(upper
, "Upper blank space (scans), overrides upper from vesa (default=vesa)");
MODULE_PARM
(lower
, "i");
MODULE_PARM_DESC
(lower
, "Lower blank space (scans), overrides lower from vesa (default=vesa)");
MODULE_PARM
(vslen
, "i");
MODULE_PARM_DESC
(vslen
, "Vertical sync length (scans), overrides lower from vesa (default=vesa)");
MODULE_PARM
(left
, "i");
MODULE_PARM_DESC
(left
, "Left blank space (px), overrides left from vesa (default=vesa)");
MODULE_PARM
(right
, "i");
MODULE_PARM_DESC
(right
, "Right blank space (px), overrides right from vesa (default=vesa)");
MODULE_PARM
(hslen
, "i");
MODULE_PARM_DESC
(hslen
, "Horizontal sync length (px), overrides hslen from vesa (default=vesa)");
MODULE_PARM
(pixclock
, "i");
MODULE_PARM_DESC
(pixclock
, "Pixelclock (ns), overrides pixclock from vesa (default=vesa)");
MODULE_PARM
(sync
, "i");
MODULE_PARM_DESC
(sync
, "Sync polarity, overrides sync from vesa (default=vesa)");
MODULE_PARM
(depth
, "i");
MODULE_PARM_DESC
(depth
, "Color depth (0=text,8,15,16,24,32) (default=vesa)");
MODULE_PARM
(maxclk
, "i");
MODULE_PARM_DESC
(maxclk
, "Startup maximal clock, 0-999MHz, 1000-999999kHz, 1000000-INF Hz");
MODULE_PARM
(fh
, "i");
MODULE_PARM_DESC
(fh
, "Startup horizontal frequency, 0-999kHz, 1000-INF Hz");
MODULE_PARM
(fv
, "i");
MODULE_PARM_DESC
(fv
, "Startup vertical frequency, 0-INF Hz\n"
"You should specify \"fv:max_monitor_vsync,fh:max_monitor_hsync,maxclk:max_monitor_dotclock\"\n");
MODULE_PARM
(grayscale
, "i");
MODULE_PARM_DESC
(grayscale
, "Sets display into grayscale. Works perfectly with paletized videomode (4, 8bpp), some limitations apply to 16, 24 and 32bpp videomodes (default=nograyscale)");
MODULE_PARM
(cross4MB
, "i");
MODULE_PARM_DESC
(cross4MB
, "Specifies that 4MB boundary can be in middle of line. (default=autodetected)");
MODULE_PARM
(dfp
, "i");
MODULE_PARM_DESC
(dfp
, "Specifies whether to use digital flat panel interface of G200/G400 (0 or 1) (default=0)");
MODULE_PARM
(dfp_type
, "i");
MODULE_PARM_DESC
(dfp_type
, "Specifies DFP interface type (0 to 255) (default=read from hardware)");
#ifdef CONFIG_PPC_PMAC
MODULE_PARM
(vmode
, "i");
MODULE_PARM_DESC
(vmode
, "Specify the vmode mode number that should be used (640x480 default)");
MODULE_PARM
(cmode
, "i");
MODULE_PARM_DESC
(cmode
, "Specify the video depth that should be used (8bit default)");
#endif
int __init init_module
(void){
DBG
(__FUNCTION__
)
if (disabled
)
return -ENXIO
;
if (depth
== 0)
depth
= RSText
;
else if (depth
== 4)
depth
= RS4bpp
;
else if (depth
== 8)
depth
= RS8bpp
;
else if (depth
== 15)
depth
= RS15bpp
;
else if (depth
== 16)
depth
= RS16bpp
;
else if (depth
== 24)
depth
= RS24bpp
;
else if (depth
== 32)
depth
= RS32bpp
;
else if (depth
!= -1) {
printk
(KERN_ERR
"matroxfb: depth %d is not supported, using default\n", depth
);
depth
= -1;
}
matrox_init
();
/* never return failure; user can hotplug matrox later... */
return 0;
}
#endif /* MODULE */
module_exit
(matrox_done
);
EXPORT_SYMBOL
(matroxfb_register_driver
);
EXPORT_SYMBOL
(matroxfb_unregister_driver
);
EXPORT_SYMBOL
(matroxfb_wait_for_sync
);
EXPORT_SYMBOL
(matroxfb_enable_irq
);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* ---------------------------------------------------------------------------
* Local variables:
* c-basic-offset: 8
* End:
*/