plan9fox/sys/src/9/pc/vganeomagic.c
cinap_lenrek 4f85115526 kernel: massive pci code rewrite
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;
2020-09-13 20:33:17 +02:00

513 lines
10 KiB
C

#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"
typedef struct CursorNM CursorNM;
struct CursorNM {
int enable;
int x;
int y;
int colour1;
int colour2;
int addr;
};
static void
neomagicenable(VGAscr* scr)
{
Pcidev *p;
int bar, curoff, vmsize;
uvlong ioaddr;
ulong iosize;
/*
* scr->mmio holds the virtual address of the cursor registers
* in the MMIO space. This may need to change for older chips
* which have the MMIO space offset in the framebuffer region.
*
* scr->io holds the offset into mmio of the CursorNM struct.
*/
if(scr->mmio)
return;
p = scr->pci;
if(p == nil || p->vid != 0x10C8)
return;
bar = 1;
switch(p->did){
case 0x0003: /* MagicGraph 128ZV */
bar = 0;
if(p->mem[bar].bar & 1)
return;
ioaddr = (p->mem[bar].bar & ~0x0F) + 0x200000;
iosize = 0x200000;
curoff = 0x100;
vmsize = 1152*1024;
goto Map;
case 0x0083: /* MagicGraph 128ZV+ */
curoff = 0x100;
vmsize = 1152*1024;
break;
case 0x0004: /* MagicGraph 128XD */
curoff = 0x100;
vmsize = 2048*1024;
break;
case 0x0005: /* MagicMedia 256AV */
curoff = 0x1000;
vmsize = 2560*1024;
break;
case 0x0006: /* MagicMedia 256ZX */
curoff = 0x1000;
vmsize = 4096*1024;
break;
case 0x0016: /* MagicMedia 256XL+ */
curoff = 0x1000;
/* Vaio VESA BIOS says 6080, but then hwgc doesn't work */
vmsize = 4096*1024;
break;
default:
return;
}
if(p->mem[bar].bar & 1)
return;
ioaddr = p->mem[bar].bar & ~0x0F;
iosize = p->mem[bar].size;
Map:
scr->mmio = vmap(ioaddr, iosize);
if(scr->mmio == nil)
return;
addvgaseg("neomagicmmio", ioaddr, iosize);
/*
* Find a place for the cursor data in display memory.
* 2 cursor images might be needed, 1KB each so use the
* last 2KB of the framebuffer.
*/
scr->storage = vmsize-2*1024;
scr->io = curoff;
vgalinearpci(scr);
if(scr->paddr)
addvgaseg("neomagicscreen", scr->paddr, scr->apsize);
}
static void
neomagiccurdisable(VGAscr* scr)
{
CursorNM *cursornm;
if(scr->mmio == 0)
return;
cursornm = (void*)((char*)scr->mmio + scr->io);
cursornm->enable = 0;
}
static void
neomagicinitcursor(VGAscr* scr, int xo, int yo, int index)
{
uchar *p;
uint p0, p1;
int x, y;
p = (uchar*)scr->vaddr;
p += scr->storage + index*1024;
for(y = yo; y < 16; y++){
p0 = scr->set[2*y];
p1 = scr->set[2*y+1];
if(xo){
p0 = (p0<<xo)|(p1>>(8-xo));
p1 <<= xo;
}
*p++ = p0;
*p++ = p1;
for(x = 16; x < 64; x += 8)
*p++ = 0x00;
p0 = scr->clr[2*y]|scr->set[2*y];
p1 = scr->clr[2*y+1]|scr->set[2*y+1];
if(xo){
p0 = (p0<<xo)|(p1>>(8-xo));
p1 <<= xo;
}
*p++ = p0;
*p++ = p1;
for(x = 16; x < 64; x += 8)
*p++ = 0x00;
}
while(y < 64+yo){
for(x = 0; x < 64; x += 8){
*p++ = 0x00;
*p++ = 0x00;
}
y++;
}
}
static void
neomagiccurload(VGAscr* scr, Cursor* curs)
{
CursorNM *cursornm;
if(scr->mmio == 0)
return;
cursornm = (void*)((char*)scr->mmio + scr->io);
cursornm->enable = 0;
memmove(&scr->Cursor, curs, sizeof(Cursor));
neomagicinitcursor(scr, 0, 0, 0);
cursornm->enable = 1;
}
static int
neomagiccurmove(VGAscr* scr, Point p)
{
CursorNM *cursornm;
int addr, index, x, xo, y, yo;
if(scr->mmio == 0)
return 1;
cursornm = (void*)((char*)scr->mmio + scr->io);
index = 0;
if((x = p.x+scr->offset.x) < 0){
xo = -x;
x = 0;
}
else
xo = 0;
if((y = p.y+scr->offset.y) < 0){
yo = -y;
y = 0;
}
else
yo = 0;
if(xo || yo){
index = 1;
neomagicinitcursor(scr, xo, yo, index);
}
addr = ((scr->storage+(1024*index))>>10) & 0xFFF;
addr = ((addr & 0x00F)<<8)|((addr>>4) & 0xFF);
if(cursornm->addr != addr)
cursornm->addr = addr;
cursornm->x = x;
cursornm->y = y;
return 0;
}
static void
neomagiccurenable(VGAscr* scr)
{
CursorNM *cursornm;
neomagicenable(scr);
if(scr->mmio == 0)
return;
cursornm = (void*)((char*)scr->mmio + scr->io);
cursornm->enable = 0;
/*
* Cursor colours.
*/
cursornm->colour1 = (Pblack<<16)|(Pblack<<8)|Pblack;
cursornm->colour2 = (Pwhite<<16)|(Pwhite<<8)|Pwhite;
/*
* Load, locate and enable the 64x64 cursor.
*/
neomagiccurload(scr, &cursor);
neomagiccurmove(scr, ZP);
cursornm->enable = 1;
}
static int neomagicbltflags;
/* registers */
enum {
BltStat = 0,
BltCntl = 1,
XPColor = 2,
FGColor = 3,
BGColor = 4,
Pitch = 5,
ClipLT = 6,
ClipRB = 7,
SrcBitOff = 8,
SrcStartOff = 9,
DstStartOff = 11,
XYExt = 12,
PageCntl = 20,
PageBase,
PostBase,
PostPtr,
DataPtr,
};
/* flags */
enum {
NEO_BS0_BLT_BUSY = 0x00000001,
NEO_BS0_FIFO_AVAIL = 0x00000002,
NEO_BS0_FIFO_PEND = 0x00000004,
NEO_BC0_DST_Y_DEC = 0x00000001,
NEO_BC0_X_DEC = 0x00000002,
NEO_BC0_SRC_TRANS = 0x00000004,
NEO_BC0_SRC_IS_FG = 0x00000008,
NEO_BC0_SRC_Y_DEC = 0x00000010,
NEO_BC0_FILL_PAT = 0x00000020,
NEO_BC0_SRC_MONO = 0x00000040,
NEO_BC0_SYS_TO_VID = 0x00000080,
NEO_BC1_DEPTH8 = 0x00000100,
NEO_BC1_DEPTH16 = 0x00000200,
NEO_BC1_DEPTH24 = 0x00000300,
NEO_BC1_X_320 = 0x00000400,
NEO_BC1_X_640 = 0x00000800,
NEO_BC1_X_800 = 0x00000c00,
NEO_BC1_X_1024 = 0x00001000,
NEO_BC1_X_1152 = 0x00001400,
NEO_BC1_X_1280 = 0x00001800,
NEO_BC1_X_1600 = 0x00001c00,
NEO_BC1_DST_TRANS = 0x00002000,
NEO_BC1_MSTR_BLT = 0x00004000,
NEO_BC1_FILTER_Z = 0x00008000,
NEO_BC2_WR_TR_DST = 0x00800000,
NEO_BC3_SRC_XY_ADDR = 0x01000000,
NEO_BC3_DST_XY_ADDR = 0x02000000,
NEO_BC3_CLIP_ON = 0x04000000,
NEO_BC3_FIFO_EN = 0x08000000,
NEO_BC3_BLT_ON_ADDR = 0x10000000,
NEO_BC3_SKIP_MAPPING = 0x80000000,
NEO_MODE1_DEPTH8 = 0x0100,
NEO_MODE1_DEPTH16 = 0x0200,
NEO_MODE1_DEPTH24 = 0x0300,
NEO_MODE1_X_320 = 0x0400,
NEO_MODE1_X_640 = 0x0800,
NEO_MODE1_X_800 = 0x0c00,
NEO_MODE1_X_1024 = 0x1000,
NEO_MODE1_X_1152 = 0x1400,
NEO_MODE1_X_1280 = 0x1800,
NEO_MODE1_X_1600 = 0x1c00,
NEO_MODE1_BLT_ON_ADDR = 0x2000,
};
/* Raster Operations */
enum {
GXclear = 0x000000, /* 0x0000 */
GXand = 0x080000, /* 0x1000 */
GXandReverse = 0x040000, /* 0x0100 */
GXcopy = 0x0c0000, /* 0x1100 */
GXandInvert = 0x020000, /* 0x0010 */
GXnoop = 0x0a0000, /* 0x1010 */
GXxor = 0x060000, /* 0x0110 */
GXor = 0x0e0000, /* 0x1110 */
GXnor = 0x010000, /* 0x0001 */
GXequiv = 0x090000, /* 0x1001 */
GXinvert = 0x050000, /* 0x0101 */
GXorReverse = 0x0d0000, /* 0x1101 */
GXcopyInvert = 0x030000, /* 0x0011 */
GXorInverted = 0x0b0000, /* 0x1011 */
GXnand = 0x070000, /* 0x0111 */
GXset = 0x0f0000, /* 0x1111 */
};
static void
waitforidle(VGAscr *scr)
{
ulong *mmio;
long x;
mmio = scr->mmio;
x = 0;
while((mmio[BltStat] & NEO_BS0_BLT_BUSY) && x++ < 1000000)
;
//if(x >= 1000000)
// iprint("idle stat %lud scrmmio %#.8lux scr %#p pc %#p\n", mmio[BltStat], scr->mmio, scr, getcallerpc(&scr));
}
static void
waitforfifo(VGAscr *scr, int entries)
{
ulong *mmio;
long x;
mmio = scr->mmio;
x = 0;
while(((mmio[BltStat]>>8) < entries) && x++ < 1000000)
;
//if(x >= 1000000)
// iprint("fifo stat %d scrmmio %#.8lux scr %#p pc %#p\n", mmio[BltStat]>>8, scr->mmio, scr, getcallerpc(&scr));
/* DirectFB says the above doesn't work. if so... */
/* waitforidle(scr); */
}
static int
neomagichwfill(VGAscr *scr, Rectangle r, ulong sval)
{
ulong *mmio;
mmio = scr->mmio;
waitforfifo(scr, 1);
mmio[FGColor] = sval;
waitforfifo(scr, 3);
mmio[BltCntl] = neomagicbltflags
| NEO_BC3_FIFO_EN
| NEO_BC0_SRC_IS_FG
| NEO_BC3_SKIP_MAPPING
| GXcopy;
mmio[DstStartOff] = scr->paddr
+ r.min.y*scr->gscreen->width*sizeof(ulong)
+ r.min.x*scr->gscreen->depth/BI2BY;
mmio[XYExt] = (Dy(r) << 16) | (Dx(r) & 0xffff);
waitforidle(scr);
return 1;
}
static int
neomagichwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
{
ulong *mmio;
int pitch, pixel;
mmio = scr->mmio;
pitch = scr->gscreen->width*sizeof(ulong);
pixel = scr->gscreen->depth/BI2BY;
waitforfifo(scr, 4);
if (r.min.y < sr.min.y || (r.min.y == sr.min.y && r.min.x < sr.min.x)) {
/* start from upper-left */
mmio[BltCntl] = neomagicbltflags
| NEO_BC3_FIFO_EN
| NEO_BC3_SKIP_MAPPING
| GXcopy;
mmio[SrcStartOff] = scr->paddr
+ sr.min.y*pitch + sr.min.x*pixel;
mmio[DstStartOff] = scr->paddr
+ r.min.y*pitch + r.min.x*pixel;
} else {
/* start from lower-right */
mmio[BltCntl] = neomagicbltflags
| NEO_BC0_X_DEC
| NEO_BC0_DST_Y_DEC
| NEO_BC0_SRC_Y_DEC
| NEO_BC3_FIFO_EN
| NEO_BC3_SKIP_MAPPING
| GXcopy;
mmio[SrcStartOff] = scr->paddr
+ (sr.max.y-1)*pitch + (sr.max.x-1)*pixel;
mmio[DstStartOff] = scr->paddr
+ (r.max.y-1)*pitch + (r.max.x-1)*pixel;
}
mmio[XYExt] = (Dy(r) << 16) | (Dx(r) & 0xffff);
waitforidle(scr);
return 1;
}
static void
neomagicdrawinit(VGAscr *scr)
{
ulong *mmio;
uint bltmode, pitch;
mmio = scr->mmio;
pitch = scr->gscreen->width*sizeof(ulong);
neomagicbltflags = bltmode = 0;
switch(scr->gscreen->depth) {
case 8:
bltmode |= NEO_MODE1_DEPTH8;
neomagicbltflags |= NEO_BC1_DEPTH8;
break;
case 16:
bltmode |= NEO_MODE1_DEPTH16;
neomagicbltflags |= NEO_BC1_DEPTH16;
break;
case 24: /* I can't get it to work, and XFree86 doesn't either. */
default: /* give up */
return;
}
switch(Dx(scr->gscreen->r)) {
case 320:
bltmode |= NEO_MODE1_X_320;
neomagicbltflags |= NEO_BC1_X_320;
break;
case 640:
bltmode |= NEO_MODE1_X_640;
neomagicbltflags |= NEO_BC1_X_640;
break;
case 800:
bltmode |= NEO_MODE1_X_800;
neomagicbltflags |= NEO_BC1_X_800;
break;
case 1024:
bltmode |= NEO_MODE1_X_1024;
neomagicbltflags |= NEO_BC1_X_1024;
break;
case 1152:
bltmode |= NEO_MODE1_X_1152;
neomagicbltflags |= NEO_BC1_X_1152;
break;
case 1280:
bltmode |= NEO_MODE1_X_1280;
neomagicbltflags |= NEO_BC1_X_1280;
break;
case 1600:
bltmode |= NEO_MODE1_X_1600;
neomagicbltflags |= NEO_BC1_X_1600;
break;
default:
/* don't worry about it */
break;
}
waitforidle(scr);
mmio[BltStat] = bltmode << 16;
mmio[Pitch] = (pitch << 16) | (pitch & 0xffff);
scr->fill = neomagichwfill;
scr->scroll = neomagichwscroll;
}
VGAdev vganeomagicdev = {
"neomagic",
neomagicenable,
nil,
nil,
nil,
neomagicdrawinit,
};
VGAcur vganeomagiccur = {
"neomagichwgc",
neomagiccurenable,
neomagiccurdisable,
neomagiccurload,
neomagiccurmove,
};