plan9fox/sys/src/9/pc/vgamga4xx.c
cinap_lenrek 2a1b43ad98 vga: make kernel vga drivers more stupid
previously, we had to maintain 3 sets of pci vid/did's:

1) in /lib/vgadb for detection
2) in the userspace driver in aux/vga
3) in the kernel mode driver

this change makes the kernel mode driver more dumb in
the cases where possible. we let userspace do the pci
enumeration and if needed, it can set the pci address
of the vga card. kernel mode drivers can assume to get
the right pci device passed in scr->pci for enable()
and linear() functions and just do very basic sanity
checking before mapping framebuffer and mmio regions.

vgalinearpciid() was removed as userspace is responsible
to pick pci device.

theres a new vgactl message "pcidev" where userspace
can set the bus address. we initialize scr->pci in
vgareset() to the first pci graphics card found. this
should cover cases when an old aux/vga binary is used
that doesnt use the new pcidev message.

userspace drivers will now use the pci device that got
a match from /lib/vgadb and skip ther own enumeration.
this way, vga cards can be made to work by simply adding
an entry in vgadb with no need to modify userspace or
kernelspace drivers. this is not always possible if
the driver derives information from the specific card
model.
2013-01-02 01:19:51 +01:00

512 lines
9.4 KiB
C

/*
* Matrox G200, G400 and G450.
* Written by Philippe Anel <xigh@free.fr>
*
* 2006-08-07 : Minor fix to allow the G200 cards to work fine. YDSTORG is now initialized.
* : Also support for 16 and 24 bit modes is added.
* : by Leonardo Valencia <leoval@anixcorp.com>
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#define Image IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"
enum {
MATROX = 0x102B,
MGA550 = 0x2527,
MGA4xx = 0x0525,
MGA200 = 0x0521,
FCOL = 0x1c24,
FXRIGHT = 0x1cac,
FXLEFT = 0x1ca8,
YDST = 0x1c90,
YLEN = 0x1c5c,
DWGCTL = 0x1c00,
DWG_TRAP = 0x04,
DWG_BITBLT = 0x08,
DWG_ILOAD = 0x09,
DWG_LINEAR = 0x0080,
DWG_SOLID = 0x0800,
DWG_ARZERO = 0x1000,
DWG_SGNZERO = 0x2000,
DWG_SHIFTZERO = 0x4000,
DWG_REPLACE = 0x000C0000,
DWG_BFCOL = 0x04000000,
SRCORG = 0x2cb4,
PITCH = 0x1c8c,
DSTORG = 0x2cb8,
YDSTORG = 0x1c94,
PLNWRT = 0x1c1c,
ZORG = 0x1c0c,
MACCESS = 0x1c04,
STATUS = 0x1e14,
FXBNDRY = 0x1C84,
CXBNDRY = 0x1C80,
YTOP = 0x1C98,
YBOT = 0x1C9C,
YDSTLEN = 0x1C88,
AR0 = 0x1C60,
AR1 = 0x1C64,
AR2 = 0x1C68,
AR3 = 0x1C6C,
AR4 = 0x1C70,
AR5 = 0x1C74,
SGN = 0x1C58,
SGN_LEFT = 1,
SGN_UP = 4,
GO = 0x0100,
FIFOSTATUS = 0x1E10,
CACHEFLUSH = 0x1FFF,
CRTCEXTIDX = 0x1FDE, /* CRTC Extension Index */
CRTCEXTDATA = 0x1FDF, /* CRTC Extension Data */
FILL_OPERAND = 0x800c7804,
};
static void
mgawrite8(VGAscr *scr, int index, uchar val)
{
((uchar*)scr->mmio)[index] = val;
}
static uchar
mgaread8(VGAscr *scr, int index)
{
return ((uchar*)scr->mmio)[index];
}
static uchar
crtcextset(VGAscr *scr, int index, uchar set, uchar clr)
{
uchar tmp;
mgawrite8(scr, CRTCEXTIDX, index);
tmp = mgaread8(scr, CRTCEXTDATA);
mgawrite8(scr, CRTCEXTIDX, index);
mgawrite8(scr, CRTCEXTDATA, (tmp & ~clr) | set);
return tmp;
}
static void
mga4xxenable(VGAscr* scr)
{
Pcidev *pci;
int size;
int i, n, k;
uchar *p;
uchar x[16];
uchar crtcext3;
if(scr->mmio)
return;
pci = scr->pci;
if(pci == nil)
return;
scr->mmio = vmap(pci->mem[1].bar&~0x0F, 16*1024);
if(scr->mmio == nil)
return;
addvgaseg("mga4xxmmio", pci->mem[1].bar&~0x0F, pci->mem[1].size);
/* need to map frame buffer here too, so vga can find memory size */
if(pci->did == MGA4xx || pci->did == MGA550)
size = 32*MB;
else
size = 8*MB;
vgalinearaddr(scr, pci->mem[0].bar&~0x0F, size);
if(scr->paddr){
/* Find out how much memory is here, some multiple of 2 MB */
/* First Set MGA Mode ... */
crtcext3 = crtcextset(scr, 3, 0x80, 0x00);
p = scr->vaddr;
n = (size / MB) / 2;
for(i = 0; i < n; i++){
k = (2*i+1)*MB;
p[k] = 0;
p[k] = i+1;
*((uchar*)scr->mmio + CACHEFLUSH) = 0;
x[i] = p[k];
}
for(i = 1; i < n; i++)
if(x[i] != i+1)
break;
scr->apsize = 2*i*MB; /* sketchy */
addvgaseg("mga4xxscreen", scr->paddr, scr->apsize);
crtcextset(scr, 3, crtcext3, 0xff);
}
}
enum{
Index = 0x00, /* Index */
Data = 0x0A, /* Data */
Cxlsb = 0x0C, /* Cursor X LSB */
Cxmsb = 0x0D, /* Cursor X MSB */
Cylsb = 0x0E, /* Cursor Y LSB */
Cymsb = 0x0F, /* Cursor Y MSB */
Icuradrl = 0x04, /* Cursor Base Address Low */
Icuradrh = 0x05, /* Cursor Base Address High */
Icctl = 0x06, /* Indirect Cursor Control */
};
static void
dac4xxdisable(VGAscr *scr)
{
uchar *dac4xx;
if(scr->mmio == 0)
return;
dac4xx = (uchar*)scr->mmio+0x3C00;
*(dac4xx+Index) = Icctl;
*(dac4xx+Data) = 0x00;
}
static void
dac4xxload(VGAscr *scr, Cursor *curs)
{
int y;
uchar *p;
uchar *dac4xx;
if(scr->mmio == 0)
return;
dac4xx = (uchar*)scr->mmio+0x3C00;
dac4xxdisable(scr);
p = (uchar*)scr->storage;
for(y = 0; y < 64; y++){
*p++ = 0; *p++ = 0; *p++ = 0;
*p++ = 0; *p++ = 0; *p++ = 0;
if(y < 16){
*p++ = curs->set[1+y*2];
*p++ = curs->set[y*2];
} else{
*p++ = 0; *p++ = 0;
}
*p++ = 0; *p++ = 0; *p++ = 0;
*p++ = 0; *p++ = 0; *p++ = 0;
if(y < 16){
*p++ = curs->set[1+y*2]|curs->clr[1+2*y];
*p++ = curs->set[y*2]|curs->clr[2*y];
} else{
*p++ = 0; *p++ = 0;
}
}
scr->offset.x = 64 + curs->offset.x;
scr->offset.y = 64 + curs->offset.y;
*(dac4xx+Index) = Icctl;
*(dac4xx+Data) = 0x03;
}
static int
dac4xxmove(VGAscr *scr, Point p)
{
int x, y;
uchar *dac4xx;
if(scr->mmio == 0)
return 1;
dac4xx = (uchar*)scr->mmio + 0x3C00;
x = p.x + scr->offset.x;
y = p.y + scr->offset.y;
*(dac4xx+Cxlsb) = x & 0xFF;
*(dac4xx+Cxmsb) = (x>>8) & 0x0F;
*(dac4xx+Cylsb) = y & 0xFF;
*(dac4xx+Cymsb) = (y>>8) & 0x0F;
return 0;
}
static void
dac4xxenable(VGAscr *scr)
{
uchar *dac4xx;
ulong storage;
if(scr->mmio == 0)
return;
dac4xx = (uchar*)scr->mmio+0x3C00;
dac4xxdisable(scr);
storage = (scr->apsize-4096)&~0x3ff;
*(dac4xx+Index) = Icuradrl;
*(dac4xx+Data) = 0xff & (storage >> 10);
*(dac4xx+Index) = Icuradrh;
*(dac4xx+Data) = 0xff & (storage >> 18);
scr->storage = (ulong)scr->vaddr + storage;
/* Show X11-Like Cursor */
*(dac4xx+Index) = Icctl;
*(dac4xx+Data) = 0x03;
/* Cursor Color 0 : White */
*(dac4xx+Index) = 0x08;
*(dac4xx+Data) = 0xff;
*(dac4xx+Index) = 0x09;
*(dac4xx+Data) = 0xff;
*(dac4xx+Index) = 0x0a;
*(dac4xx+Data) = 0xff;
/* Cursor Color 1 : Black */
*(dac4xx+Index) = 0x0c;
*(dac4xx+Data) = 0x00;
*(dac4xx+Index) = 0x0d;
*(dac4xx+Data) = 0x00;
*(dac4xx+Index) = 0x0e;
*(dac4xx+Data) = 0x00;
/* Cursor Color 2 : Red */
*(dac4xx+Index) = 0x10;
*(dac4xx+Data) = 0xff;
*(dac4xx+Index) = 0x11;
*(dac4xx+Data) = 0x00;
*(dac4xx+Index) = 0x12;
*(dac4xx+Data) = 0x00;
/*
* Load, locate and enable the
* 64x64 cursor in X11 mode.
*/
dac4xxload(scr, &arrow);
dac4xxmove(scr, ZP);
}
static void
mga4xxblank(VGAscr *scr, int blank)
{
char *cp;
uchar *mga;
uchar seq1, crtcext1;
/* blank = 0 -> turn screen on */
/* blank = 1 -> turn screen off */
if(scr->mmio == 0)
return;
mga = (uchar*)scr->mmio;
if(blank == 0){
seq1 = 0x00;
crtcext1 = 0x00;
} else {
seq1 = 0x20;
crtcext1 = 0x10; /* Default value ... : standby */
cp = getconf("*dpms");
if(cp){
if(cistrcmp(cp, "standby") == 0)
crtcext1 = 0x10;
else if(cistrcmp(cp, "suspend") == 0)
crtcext1 = 0x20;
else if(cistrcmp(cp, "off") == 0)
crtcext1 = 0x30;
}
}
*(mga + 0x1fc4) = 1;
seq1 |= *(mga + 0x1fc5) & ~0x20;
*(mga + 0x1fc5) = seq1;
*(mga + 0x1fde) = 1;
crtcext1 |= *(mga + 0x1fdf) & ~0x30;
*(mga + 0x1fdf) = crtcext1;
}
static void
mgawrite32(uchar *mga, ulong reg, ulong val)
{
*((ulong*)(&mga[reg])) = val;
}
static ulong
mgaread32(uchar *mga, ulong reg)
{
return *((ulong*)(&mga[reg]));
}
static void
mga_fifo(uchar *mga, uchar n)
{
ulong t;
#define Timeout 100
for (t = 0; t < Timeout; t++)
if ((mgaread32(mga, FIFOSTATUS) & 0xff) >= n)
break;
if (t >= Timeout)
print("mga4xx: fifo timeout");
}
static int
mga4xxfill(VGAscr *scr, Rectangle r, ulong color)
{
uchar *mga;
if(scr->mmio == 0)
return 0;
mga = (uchar*)scr->mmio;
mga_fifo(mga, 7);
mgawrite32(mga, DWGCTL, 0);
mgawrite32(mga, FCOL, color);
mgawrite32(mga, FXLEFT, r.min.x);
mgawrite32(mga, FXRIGHT, r.max.x);
mgawrite32(mga, YDST, r.min.y);
mgawrite32(mga, YLEN, Dy(r));
mgawrite32(mga, DWGCTL + GO, FILL_OPERAND);
while(mgaread32(mga, STATUS) & 0x00010000)
;
return 1;
}
static int
mga4xxscroll(VGAscr *scr, Rectangle dr, Rectangle sr)
{
uchar * mga;
int pitch;
int width, height;
ulong start, end, sgn;
Point sp, dp;
if(scr->mmio == 0)
return 0;
mga = (uchar*)scr->mmio;
assert(Dx(sr) == Dx(dr) && Dy(sr) == Dy(dr));
sp = sr.min;
dp = dr.min;
if(eqpt(sp, dp))
return 1;
pitch = Dx(scr->gscreen->r);
width = Dx(sr);
height = Dy(sr);
sgn = 0;
if(dp.y > sp.y && dp.y < sp.y + height){
sp.y += height - 1;
dp.y += height - 1;
sgn |= SGN_UP;
}
width--;
start = end = sp.x + (sp.y * pitch);
if(dp.x > sp.x && dp.x < sp.x + width){
start += width;
sgn |= SGN_LEFT;
}
else
end += width;
mga_fifo(mga, 8);
mgawrite32(mga, DWGCTL, 0);
mgawrite32(mga, SGN, sgn);
mgawrite32(mga, AR5, sgn & SGN_UP ? -pitch : pitch);
mgawrite32(mga, AR0, end);
mgawrite32(mga, AR3, start);
mgawrite32(mga, FXBNDRY, ((dp.x + width) << 16) | dp.x);
mgawrite32(mga, YDSTLEN, (dp.y << 16) | height);
mgawrite32(mga, DWGCTL + GO, DWG_BITBLT | DWG_SHIFTZERO | DWG_BFCOL | DWG_REPLACE);
while(mgaread32(mga, STATUS) & 0x00010000)
;
return 1;
}
static void
mga4xxdrawinit(VGAscr *scr)
{
uchar *mga;
if(scr->mmio == 0)
return;
mga = (uchar*)scr->mmio;
mgawrite32(mga, SRCORG, 0);
mgawrite32(mga, DSTORG, 0);
mgawrite32(mga, YDSTORG, 0);
mgawrite32(mga, ZORG, 0);
mgawrite32(mga, PLNWRT, ~0);
mgawrite32(mga, FCOL, 0xffff0000);
mgawrite32(mga, CXBNDRY, 0xFFFF0000);
mgawrite32(mga, YTOP, 0);
mgawrite32(mga, YBOT, 0x01FFFFFF);
mgawrite32(mga, PITCH, Dx(scr->gscreen->r) & ((1 << 13) - 1));
switch(scr->gscreen->depth){
case 8:
mgawrite32(mga, MACCESS, 0);
break;
case 16:
mgawrite32(mga, MACCESS, 1);
break;
case 24:
mgawrite32(mga, MACCESS, 3);
break;
case 32:
mgawrite32(mga, MACCESS, 2);
break;
default:
return; /* depth not supported ! */
}
scr->fill = mga4xxfill;
scr->scroll = mga4xxscroll;
scr->blank = mga4xxblank;
}
VGAdev vgamga4xxdev = {
"mga4xx",
mga4xxenable, /* enable */
0, /* disable */
0, /* page */
0, /* linear */
mga4xxdrawinit,
};
VGAcur vgamga4xxcur = {
"mga4xxhwgc",
dac4xxenable,
dac4xxdisable,
dac4xxload,
dac4xxmove,
};