
The new pci code is moved to port/pci.[hc] and shared by all ports. Each port has its own PCI controller implementation, providing the pcicfgrw*() functions for low level pci config space access. The locking for pcicfgrw*() is now done by the caller (only port/pci.c). Device drivers now need to include "../port/pci.h" in addition to "io.h". The new code now checks bridge windows and membars, while enumerating the bus, giving the pc driver a chance to re-assign them. This is needed because some UEFI implementations fail to assign the bars for some devices, so we need to do it outselfs. (See pcireservemem()). While working on this, it was discovered that the pci code assimed the smallest I/O bar size is 16 (pcibarsize()), which is wrong. I/O bars can be as small as 4 bytes. Bit 1 in an I/O bar is also reserved and should be masked off, making the port mask: port = bar & ~3;
517 lines
9.6 KiB
C
517 lines
9.6 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/pci.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;
|
|
|
|
/* 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;
|
|
|
|
if((pci->mem[0].bar & 1) != 0 || pci->mem[0].size < size
|
|
|| (pci->mem[1].bar & 1) != 0 || pci->mem[1].size < 16*1024)
|
|
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);
|
|
|
|
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, &cursor);
|
|
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,
|
|
};
|