plan9fox/sys/src/9/pc/vganvidia.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

527 lines
12 KiB
C

/* Portions of this file derived from work with the following copyright */
/***************************************************************************\
|* *|
|* Copyright 2003 NVIDIA, Corporation. All rights reserved. *|
|* *|
|* NOTICE TO USER: The source code is copyrighted under U.S. and *|
|* international laws. Users and possessors of this source code are *|
|* hereby granted a nonexclusive, royalty-free copyright license to *|
|* use this code in individual and commercial software. *|
|* *|
|* Any use of this source code must include, in the user documenta- *|
|* tion and internal comments to the code, notices to the end user *|
|* as follows: *|
|* *|
|* Copyright 2003 NVIDIA, Corporation. All rights reserved. *|
|* *|
|* NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY *|
|* OF THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" *|
|* WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND. NVIDIA, CORPOR- *|
|* ATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOURCE CODE, *|
|* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE- *|
|* MENT, AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL *|
|* NVIDIA, CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT, INCI- *|
|* DENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RE- *|
|* SULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION *|
|* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *|
|* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE. *|
|* *|
|* U.S. Government End Users. This source code is a "commercial *|
|* item," as that term is defined at 48 C.F.R. 2.101 (OCT 1995), *|
|* consisting of "commercial computer software" and "commercial *|
|* computer software documentation," as such terms are used in *|
|* 48 C.F.R. 12.212 (SEPT 1995) and is provided to the U.S. Govern- *|
|* ment only as a commercial end item. Consistent with 48 C.F.R. *|
|* 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (JUNE 1995), *|
|* all U.S. Government End Users acquire the source code with only *|
|* those rights set forth herein. *|
|* *|
\***************************************************************************/
#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"
#include "nv_dma.h"
enum {
Pramin = 0x00710000,
Pramdac = 0x00680000,
Fifo = 0x00800000,
Pgraph = 0x00400000,
Pfb = 0x00100000
};
enum {
hwCurPos = Pramdac + 0x0300,
};
#define SKIPS 8
static struct {
ulong *dmabase;
int dmacurrent;
int dmaput;
int dmafree;
int dmamax;
} nv;
static void
nvidiaenable(VGAscr* scr)
{
Pcidev *p;
ulong *q;
int tmp;
if(scr->mmio)
return;
p = scr->pci;
if(p == nil)
return;
scr->id = p->did;
if(p->mem[0].bar & 1)
return;
scr->mmio = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
if(scr->mmio == nil)
return;
addvgaseg("nvidiammio", p->mem[0].bar&~0x0F, p->mem[0].size);
vgalinearpci(scr);
if(scr->apsize)
addvgaseg("nvidiascreen", scr->paddr, scr->apsize);
/* find video memory size */
switch (scr->id & 0x0ff0) {
case 0x0020:
case 0x00A0:
q = (void*)((uchar*)scr->mmio + Pfb);
tmp = *q;
if (tmp & 0x0100) {
scr->storage = ((tmp >> 12) & 0x0F) * 1024 + 1024 * 2;
} else {
tmp &= 0x03;
if (tmp)
scr->storage = (1024*1024*2) << tmp;
else
scr->storage = 1024*1024*32;
}
break;
case 0x01A0:
p = pcimatchtbdf(MKBUS(BusPCI, 0, 0, 1));
tmp = pcicfgr32(p, 0x7C);
scr->storage = (((tmp >> 6) & 31) + 1) * 1024 * 1024;
break;
case 0x01F0:
p = pcimatchtbdf(MKBUS(BusPCI, 0, 0, 1));
tmp = pcicfgr32(p, 0x84);
scr->storage = (((tmp >> 4) & 127) + 1) * 1024 * 1024;
break;
default:
q = (void*)((uchar*)scr->mmio + Pfb + 0x020C);
tmp = (*q >> 20) & 0xFFF;
if (tmp == 0)
tmp = 16;
scr->storage = tmp*1024*1024;
break;
}
}
static void
nvidialinear(VGAscr *, int, int)
{
}
static void
nvidiacurdisable(VGAscr* scr)
{
if(scr->mmio == 0)
return;
vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) & ~0x01);
}
static void
nvidiacurload(VGAscr* scr, Cursor* curs)
{
ulong* p;
int i,j;
ushort c,s;
ulong tmp;
if(scr->mmio == 0)
return;
vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) & ~0x01);
switch (scr->id & 0x0ff0) {
case 0x0020:
case 0x00A0:
p = (void*)((uchar*)scr->mmio + Pramin + 0x1E00 * 4);
break;
default:
/*
* Reset the cursor location, since the kernel may
* have allocated less storage than aux/vga
* expected.
*/
tmp = scr->apsize - 96*1024;
p = (void*)((uchar*)scr->vaddr + tmp);
vgaxo(Crtx, 0x30, 0x80|(tmp>>17));
vgaxo(Crtx, 0x31, (tmp>>11)<<2);
vgaxo(Crtx, 0x2F, tmp>>24);
break;
}
for(i=0; i<16; i++) {
c = (curs->clr[2 * i] << 8) | curs->clr[2 * i+1];
s = (curs->set[2 * i] << 8) | curs->set[2 * i+1];
tmp = 0;
for (j=0; j<16; j++){
if(s&0x8000)
tmp |= 0x80000000;
else if(c&0x8000)
tmp |= 0xFFFF0000;
if (j&0x1){
*p++ = tmp;
tmp = 0;
} else {
tmp>>=16;
}
c<<=1;
s<<=1;
}
for (j=0; j<8; j++)
*p++ = 0;
}
for (i=0; i<256; i++)
*p++ = 0;
scr->offset = curs->offset;
vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) | 0x01);
}
static int
nvidiacurmove(VGAscr* scr, Point p)
{
ulong* cursorpos;
if(scr->mmio == 0)
return 1;
cursorpos = (void*)((uchar*)scr->mmio + hwCurPos);
*cursorpos = ((p.y+scr->offset.y)<<16)|((p.x+scr->offset.x) & 0xFFFF);
return 0;
}
static void
nvidiacurenable(VGAscr* scr)
{
nvidiaenable(scr);
if(scr->mmio == 0)
return;
vgaxo(Crtx, 0x1F, 0x57);
nvidiacurload(scr, &cursor);
nvidiacurmove(scr, ZP);
vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) | 0x01);
}
void
writeput(VGAscr *scr, int data)
{
uchar *p, scratch;
ulong *fifo;
outb(0x3D0,0);
p = scr->vaddr;
scratch = *p;
fifo = (void*)((uchar*)scr->mmio + Fifo);
fifo[0x10] = (data << 2);
USED(scratch);
}
ulong
readget(VGAscr *scr)
{
ulong *fifo;
fifo = (void*)((uchar*)scr->mmio + Fifo);
return (fifo[0x0011] >> 2);
}
void
nvdmakickoff(VGAscr *scr)
{
if(nv.dmacurrent != nv.dmaput) {
nv.dmaput = nv.dmacurrent;
writeput(scr, nv.dmaput);
}
}
static void
nvdmanext(ulong data)
{
nv.dmabase[nv.dmacurrent++] = data;
}
void
nvdmawait(VGAscr *scr, int size)
{
int dmaget;
size++;
while(nv.dmafree < size) {
dmaget = readget(scr);
if(nv.dmaput >= dmaget) {
nv.dmafree = nv.dmamax - nv.dmacurrent;
if(nv.dmafree < size) {
nvdmanext(0x20000000);
if(dmaget <= SKIPS) {
if (nv.dmaput <= SKIPS) /* corner case - will be idle */
writeput(scr, SKIPS + 1);
do { dmaget = readget(scr); }
while(dmaget <= SKIPS);
}
writeput(scr, SKIPS);
nv.dmacurrent = nv.dmaput = SKIPS;
nv.dmafree = dmaget - (SKIPS + 1);
}
} else
nv.dmafree = dmaget - nv.dmacurrent - 1;
}
}
static void
nvdmastart(VGAscr *scr, ulong tag, int size)
{
if (nv.dmafree <= size)
nvdmawait(scr, size);
nvdmanext((size << 18) | tag);
nv.dmafree -= (size + 1);
}
static void
waitforidle(VGAscr *scr)
{
ulong* pgraph;
int x;
pgraph = (void*)((uchar*)scr->mmio + Pgraph);
x = 0;
while((readget(scr) != nv.dmaput) && x++ < 1000000)
;
if(x >= 1000000)
iprint("idle stat %lud put %d scr %#p pc %#p\n", readget(scr), nv.dmaput, scr, getcallerpc(&scr));
x = 0;
while(pgraph[0x00000700/4] & 0x01 && x++ < 1000000)
;
if(x >= 1000000)
iprint("idle stat %lud scrio %#p scr %#p pc %#p\n", *pgraph, scr->mmio, scr, getcallerpc(&scr));
}
static int
nvresetgraphics(VGAscr *scr)
{
ulong surfaceFormat, patternFormat, rectFormat, lineFormat;
int pitch, i;
if(scr->paddr == 0)
return -1;
pitch = scr->gscreen->width*sizeof(ulong);
/*
* DMA is at the end of the virtual window,
* but we might have cut it short when mapping it.
*/
if(nv.dmabase == nil){
if(scr->storage <= scr->apsize)
nv.dmabase = (ulong*)((uchar*)scr->vaddr + scr->storage - 128*1024);
else{
nv.dmabase = (void*)vmap(scr->paddr + scr->storage - 128*1024, 128*1024);
if(nv.dmabase == 0){
print("vmap nvidia dma failed\n");
return -1;
}
}
}
for(i=0; i<SKIPS; i++)
nv.dmabase[i] = 0x00000000;
nv.dmabase[0x0 + SKIPS] = 0x00040000;
nv.dmabase[0x1 + SKIPS] = 0x80000010;
nv.dmabase[0x2 + SKIPS] = 0x00042000;
nv.dmabase[0x3 + SKIPS] = 0x80000011;
nv.dmabase[0x4 + SKIPS] = 0x00044000;
nv.dmabase[0x5 + SKIPS] = 0x80000012;
nv.dmabase[0x6 + SKIPS] = 0x00046000;
nv.dmabase[0x7 + SKIPS] = 0x80000013;
nv.dmabase[0x8 + SKIPS] = 0x00048000;
nv.dmabase[0x9 + SKIPS] = 0x80000014;
nv.dmabase[0xA + SKIPS] = 0x0004A000;
nv.dmabase[0xB + SKIPS] = 0x80000015;
nv.dmabase[0xC + SKIPS] = 0x0004C000;
nv.dmabase[0xD + SKIPS] = 0x80000016;
nv.dmabase[0xE + SKIPS] = 0x0004E000;
nv.dmabase[0xF + SKIPS] = 0x80000017;
nv.dmaput = 0;
nv.dmacurrent = 16 + SKIPS;
nv.dmamax = 8191;
nv.dmafree = nv.dmamax - nv.dmacurrent;
switch(scr->gscreen->depth) {
case 32:
case 24:
surfaceFormat = SURFACE_FORMAT_DEPTH24;
patternFormat = PATTERN_FORMAT_DEPTH24;
rectFormat = RECT_FORMAT_DEPTH24;
lineFormat = LINE_FORMAT_DEPTH24;
break;
case 16:
case 15:
surfaceFormat = SURFACE_FORMAT_DEPTH16;
patternFormat = PATTERN_FORMAT_DEPTH16;
rectFormat = RECT_FORMAT_DEPTH16;
lineFormat = LINE_FORMAT_DEPTH16;
break;
default:
surfaceFormat = SURFACE_FORMAT_DEPTH8;
patternFormat = PATTERN_FORMAT_DEPTH8;
rectFormat = RECT_FORMAT_DEPTH8;
lineFormat = LINE_FORMAT_DEPTH8;
break;
}
nvdmastart(scr, SURFACE_FORMAT, 4);
nvdmanext(surfaceFormat);
nvdmanext(pitch | (pitch << 16));
nvdmanext(0);
nvdmanext(0);
nvdmastart(scr, PATTERN_FORMAT, 1);
nvdmanext(patternFormat);
nvdmastart(scr, RECT_FORMAT, 1);
nvdmanext(rectFormat);
nvdmastart(scr, LINE_FORMAT, 1);
nvdmanext(lineFormat);
nvdmastart(scr, PATTERN_COLOR_0, 4);
nvdmanext(~0);
nvdmanext(~0);
nvdmanext(~0);
nvdmanext(~0);
nvdmastart(scr, ROP_SET, 1);
nvdmanext(0xCC);
nvdmakickoff(scr);
waitforidle(scr);
return 0;
}
static int
nvidiahwfill(VGAscr *scr, Rectangle r, ulong sval)
{
nvdmastart(scr, RECT_SOLID_COLOR, 1);
nvdmanext(sval);
nvdmastart(scr, RECT_SOLID_RECTS(0), 2);
nvdmanext((r.min.x << 16) | r.min.y);
nvdmanext((Dx(r) << 16) | Dy(r));
//if ( (Dy(r) * Dx(r)) >= 512)
nvdmakickoff(scr);
waitforidle(scr);
return 1;
}
static int
nvidiahwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
{
nvdmastart(scr, BLIT_POINT_SRC, 3);
nvdmanext((sr.min.y << 16) | sr.min.x);
nvdmanext((r.min.y << 16) | r.min.x);
nvdmanext((Dy(r) << 16) | Dx(r));
//if ( (Dy(r) * Dx(r)) >= 512)
nvdmakickoff(scr);
waitforidle(scr);
return 1;
}
void
nvidiablank(VGAscr*, int blank)
{
uchar seq1, crtc1A;
seq1 = vgaxi(Seqx, 1) & ~0x20;
crtc1A = vgaxi(Crtx, 0x1A) & ~0xC0;
if(blank){
seq1 |= 0x20;
// crtc1A |= 0xC0;
crtc1A |= 0x80;
}
vgaxo(Seqx, 1, seq1);
vgaxo(Crtx, 0x1A, crtc1A);
}
static void
nvidiadrawinit(VGAscr *scr)
{
scr->blank = nvidiablank;
if(nvresetgraphics(scr) < 0)
return;
scr->fill = nvidiahwfill;
scr->scroll = nvidiahwscroll;
}
VGAdev vganvidiadev = {
"nvidia",
nvidiaenable,
nil,
nil,
nvidialinear,
nvidiadrawinit,
};
VGAcur vganvidiacur = {
"nvidiahwgc",
nvidiacurenable,
nvidiacurdisable,
nvidiacurload,
nvidiacurmove,
};