ref: 04759ec9af6dcc78ea5873ceaf6db2e3b3920b22
dir: /sys/src/9/pc/screen.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 "ureg.h" #include "../port/error.h" #define Image IMAGE #include <draw.h> #include <memdraw.h> #include <cursor.h> #include "screen.h" extern VGAcur vgasoftcur; Memimage *gscreen; VGAscr vgascreen[1]; char *tiltstr[4] = { "none", "left", "inverted", "right", }; static Point tiltpt(int tilt, Point dim, Point p) { switch(tilt&3){ case 1: return Pt(dim.y-p.y-1, p.x); case 2: return Pt(dim.x-p.x-1, dim.y-p.y-1); case 3: return Pt(p.y, dim.x-p.x-1); } return p; } static Rectangle tiltrect(int tilt, Point dim, Rectangle r) { switch(tilt&3){ case 1: return Rect(dim.y-r.max.y, r.min.x, dim.y-r.min.y, r.max.x); case 2: return Rect(dim.x-r.max.x, dim.y-r.max.y, dim.x-r.min.x, dim.y-r.min.y); case 3: return Rect(r.min.y, dim.x-r.max.x, r.max.y, dim.x-r.min.x); } return r; } static Point tiltsize(int tilt, Point dim) { return (tilt & 1) != 0 ? Pt(dim.y, dim.x) : dim; } Rectangle actualscreensize(VGAscr *scr) { return Rpt(ZP, tiltsize(-scr->tilt, scr->gscreen->clipr.max)); } void setactualsize(VGAscr *scr, Rectangle r) { r.min = ZP; r.max = tiltsize(scr->tilt, r.max); if(rectclip(&r, scr->gscreen->r) == 0) return; scr->gscreen->clipr = r; } static char* setscreensize0(VGAscr *scr, int width, int height, int depth, ulong chan, int tilt) { int bpp, pitch; scr->gscreendata = nil; scr->gscreen = nil; if(gscreen != nil){ freememimage(gscreen); gscreen = nil; } if(scr->paddr == 0){ if(scr->dev && scr->dev->page){ scr->vaddr = KADDR(VGAMEM()); scr->apsize = 1<<16; } scr->softscreen = 1; } depth = chantodepth(chan); bpp = (depth+7) / 8; pitch = ((width * depth+31) & ~31) / 8; if(tilt) scr->softscreen = 1; if(scr->softscreen){ gscreen = allocmemimage(Rpt(ZP, tiltsize(tilt, Pt(width, height))), chan); scr->useflush = 1; }else{ static Memdata md; md.ref = 1; if((md.bdata = scr->vaddr) == 0) error("framebuffer not maped"); gscreen = allocmemimaged(Rpt(ZP, Pt(width, height)), chan, &md); scr->useflush = scr->dev && scr->dev->flush; } if(gscreen == nil) return "no memory for vga memimage"; scr->bpp = bpp; scr->pitch = pitch; scr->width = width; scr->height = height; scr->tilt = tilt & 3; scr->palettedepth = 6; /* default */ scr->memdefont = getmemdefont(); scr->gscreen = gscreen; scr->gscreendata = gscreen->data; return nil; } void setscreensize(VGAscr *scr, int x, int y, int z, ulong chan, int tilt) { char *err; if(waserror()) nexterror(); if(memimageinit() < 0) error("memimageinit failed"); lock(&vgascreenlock); if(waserror()){ unlock(&vgascreenlock); nexterror(); } err = setscreensize0(scr, x, y, z, chan, tilt); if(err != nil) error(err); vgaimageinit(chan); bootscreenconf(scr); unlock(&vgascreenlock); poperror(); drawcmap(); if(scr->cur && scr->cur != &vgasoftcur){ cursoroff(); setcursor(&cursor); cursoron(); } poperror(); } int screenaperture(VGAscr *scr, int size, int align) { uvlong pa; if(size == 0) return 0; if(scr->paddr) /* set up during enable */ return 0; if(scr->dev && scr->dev->linear){ scr->dev->linear(scr, size, align); return 0; } /* * Need to allocate some physical address space. * The driver will tell the card to use it. */ size = PGROUND(size); pa = upaalloc(-1ULL, size, align); if(pa == -1) return -1; scr->paddr = pa; scr->vaddr = vmap(pa, size); if(scr->vaddr == nil) return -1; scr->apsize = size; return 0; } Memdata* attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen) { VGAscr *scr; scr = &vgascreen[0]; if(scr->gscreen == nil || scr->gscreendata == nil) return nil; *r = scr->gscreen->clipr; *chan = scr->gscreen->chan; *d = scr->gscreen->depth; *width = scr->gscreen->width; *softscreen = (scr->gscreendata->allocd || scr->useflush) ? 1 : 0; scr->gscreendata->ref++; return scr->gscreendata; } void flushmemscreen(Rectangle r) { VGAscr *scr; uchar *sp, *disp, *sdisp, *edisp; int x, y, len, incs, off, page; scr = &vgascreen[0]; if(scr->gscreen == nil || scr->useflush == 0) return; if(rectclip(&r, scr->gscreen->clipr) == 0) return; if(scr->tilt){ Point size; /* only supported on linear framebuffer */ disp = scr->vaddr; if(scr->paddr == 0 || disp == nil) return; size = scr->gscreen->clipr.max; r = tiltrect(-scr->tilt, size, r); size = tiltsize(-scr->tilt, size); sp = byteaddr(scr->gscreen, tiltpt(scr->tilt, size, r.min)); incs = byteaddr(scr->gscreen, tiltpt(scr->tilt, size, Pt(r.min.x+1, r.min.y))) - sp; for(;;){ sdisp = disp + r.min.y * scr->pitch; for(x = r.min.x; x < r.max.x; x++, sp += incs){ switch(scr->bpp){ case 4: ((ulong*)sdisp)[x] = *(ulong*)sp; break; case 3: sdisp[x*3+0] = sp[0]; sdisp[x*3+1] = sp[1]; sdisp[x*3+2] = sp[2]; break; case 2: ((ushort*)sdisp)[x] = *(ushort*)sp; break; case 1: sdisp[x] = sp[0]; break; } } if(++r.min.y >= r.max.y) break; sp = byteaddr(scr->gscreen, tiltpt(scr->tilt, size, r.min)); } return; } if(scr->dev && scr->dev->flush){ scr->dev->flush(scr, r); return; } disp = scr->vaddr; incs = scr->pitch; off = (r.min.x*scr->gscreen->depth) / 8; len = (r.max.x*scr->gscreen->depth + 7) / 8; len -= off; off += r.min.y*incs; sp = scr->gscreendata->bdata + scr->gscreen->zero + off; /* * Linear framebuffer with softscreen. */ if(scr->paddr){ sdisp = disp+off; for(y = r.min.y; y < r.max.y; y++) { memmove(sdisp, sp, len); sp += incs; sdisp += incs; } return; } /* * Paged framebuffer window. */ if(scr->dev == nil || scr->dev->page == nil) return; page = off/scr->apsize; off %= scr->apsize; sdisp = disp+off; edisp = disp+scr->apsize; scr->dev->page(scr, page); for(y = r.min.y; y < r.max.y; y++) { if(sdisp + incs < edisp) { memmove(sdisp, sp, len); sp += incs; sdisp += incs; } else { off = edisp - sdisp; page++; if(off <= len){ if(off > 0) memmove(sdisp, sp, off); scr->dev->page(scr, page); if(len - off > 0) memmove(disp, sp+off, len - off); } else { memmove(sdisp, sp, len); scr->dev->page(scr, page); } sp += incs; sdisp += incs - scr->apsize; } } } void getcolor(ulong p, ulong* pr, ulong* pg, ulong* pb) { VGAscr *scr; ulong x; scr = &vgascreen[0]; if(scr->gscreen == nil) return; switch(scr->gscreen->depth){ default: x = 0x0F; break; case 8: x = 0xFF; break; } p &= x; *pr = scr->colormap[p][0]; *pg = scr->colormap[p][1]; *pb = scr->colormap[p][2]; } int setpalette(ulong p, ulong r, ulong g, ulong b) { VGAscr *scr; int d; scr = &vgascreen[0]; d = scr->palettedepth; scr->colormap[p][0] = r; scr->colormap[p][1] = g; scr->colormap[p][2] = b; vgao(PaddrW, p); vgao(Pdata, r>>(32-d)); vgao(Pdata, g>>(32-d)); vgao(Pdata, b>>(32-d)); return ~0; } /* * On some video cards (e.g. Mach64), the palette is used as the * DAC registers for >8-bit modes. We don't want to set them when the user * is trying to set a colormap and the card is in one of these modes. */ int setcolor(ulong p, ulong r, ulong g, ulong b) { VGAscr *scr; int x; scr = &vgascreen[0]; if(scr->gscreen == nil) return 0; switch(scr->gscreen->depth){ case 1: case 2: case 4: x = 0x0F; break; case 8: x = 0xFF; break; default: return 0; } p &= x; return setpalette(p, r, g, b); } static void tiltcursor(int t, Cursor *src, Cursor *dst) { static Point dim = {16, 16}; uint i, j, im, jm; Point p; for(i = 0; i < 16*16; i++){ p = tiltpt(t, dim, Pt(i&15,i>>4)); j = p.y<<4 | p.x; im = 0x80>>(i&7); jm = 0x80>>(j&7); if(src->clr[i>>3] & im) dst->clr[j>>3] |= jm; else dst->clr[j>>3] &= ~jm; if(src->set[i>>3] & im) dst->set[j>>3] |= jm; else dst->set[j>>3] &= ~jm; } p = Pt(-src->offset.x & 15, -src->offset.y & 15); p = tiltpt(t, dim, p); dst->offset = Pt(-p.x, -p.y); } void cursoron(void) { VGAscr *scr; VGAcur *cur; Point p; scr = &vgascreen[0]; cur = scr->cur; if(cur && cur->move){ p = mousexy(); if(scr->tilt && cur != &vgasoftcur) p = tiltpt(-scr->tilt, scr->gscreen->clipr.max, p); cur->move(scr, p); } } void cursoroff(void) { } void setcursor(Cursor* curs) { VGAscr *scr; VGAcur *cur; scr = &vgascreen[0]; cur = scr->cur; if(cur && cur->load){ if(scr->tilt && cur != &vgasoftcur){ static Cursor tmp; tiltcursor(-scr->tilt, curs, &tmp); curs = &tmp; } cur->load(scr, curs); } } int hwaccel = 0; int hwblank = 0; int hwdraw(Memdrawparam *par) { VGAscr *scr; Memimage *dst, *src, *mask; Memdata *scrd; int m; scr = &vgascreen[0]; scrd = scr->gscreendata; if(scr->gscreen == nil || scrd == nil) return 0; if((dst = par->dst) == nil || dst->data == nil) return 0; if((src = par->src) && src->data == nil) src = nil; if((mask = par->mask) && mask->data == nil) mask = nil; if(scr->cur == &vgasoftcur){ if(dst->data->bdata == scrd->bdata) swcursoravoid(par->r); if(src && src->data->bdata == scrd->bdata) swcursoravoid(par->sr); if(mask && mask->data->bdata == scrd->bdata) swcursoravoid(par->mr); } if(!hwaccel || scr->softscreen || scr->tilt) return 0; if(dst->data->bdata != scrd->bdata || src == nil || mask == nil) return 0; /* * If we have an opaque mask and source is one opaque * pixel we can convert to the destination format and just * replicate with memset. */ m = Simplesrc|Simplemask|Fullmask; if(scr->fill && (par->state&m)==m && ((par->srgba&0xFF) == 0xFF) && (par->op&S) == S) return scr->fill(scr, par->r, par->sdval); /* * If no source alpha, an opaque mask, we can just copy the * source onto the destination. If the channels are the same and * the source is not replicated, memmove suffices. */ m = Simplemask|Fullmask; if(scr->scroll && src->data->bdata==dst->data->bdata && !(src->flags&Falpha) && (par->state&m)==m && (par->op&S) == S) return scr->scroll(scr, par->r, par->sr); return 0; } void blankscreen(int blank) { VGAscr *scr; if(!hwblank) return; scr = &vgascreen[0]; if(scr->blank) scr->blank(scr, blank); else vgablank(scr, blank); } static char* vgalinearaddr0(VGAscr *scr, uvlong paddr, vlong size) { int x; vlong nsize; uvlong npaddr; /* * new approach. instead of trying to resize this * later, let's assume that we can just allocate the * entire window to start with. */ if(scr->paddr == paddr && size <= scr->apsize) return nil; if(scr->paddr){ /* * could call vunmap and vmap, * but worried about dangling pointers in devdraw */ return "cannot grow vga frame buffer"; } /* round to page boundary, just in case */ x = paddr&(BY2PG-1); npaddr = paddr-x; nsize = PGROUND(size+x); /* * Don't bother trying to map more than 4000x4000x32 = 64MB. * We only have a 256MB window. */ if(nsize > 64*MB) nsize = 64*MB; scr->vaddr = vmap(npaddr, nsize); if(scr->vaddr == nil) return "cannot map vga frame buffer"; patwc(scr->vaddr, nsize); scr->vaddr = (char*)scr->vaddr+x; scr->paddr = paddr; scr->apsize = (int)nsize; mtrr(npaddr, nsize, "wc"); return nil; } static char* vgalinearpci0(VGAscr *scr) { int i, best; Pcidev *p; p = scr->pci; if(p == nil) return "no pci card"; /* * Scan for largest memory region on card. * Some S3 cards (e.g. Savage) have enormous * mmio regions (but even larger frame buffers). * Some 3dfx cards (e.g., Voodoo3) have mmio * buffers the same size as the frame buffer, * but only the frame buffer is marked as * prefetchable (bar&8). If a card doesn't fit * into these heuristics, its driver will have to * call vgalinearaddr directly. */ best = -1; for(i=0; i<nelem(p->mem); i++){ if(p->mem[i].bar&1) /* not memory */ continue; if(p->mem[i].size < 640*480) /* not big enough */ continue; if(best==-1 || p->mem[i].size > p->mem[best].size || (p->mem[i].size == p->mem[best].size && (p->mem[i].bar&8) && !(p->mem[best].bar&8))) best = i; } if(best < 0) return "no video memory found on pci card"; return vgalinearaddr0(scr, p->mem[best].bar&~0xF, p->mem[best].size); } void vgalinearpci(VGAscr *scr) { char *err; if(scr->pci == nil) return; if((err = vgalinearpci0(scr)) != nil) error(err); } void vgalinearaddr(VGAscr *scr, uvlong paddr, vlong size) { char *err; if((err = vgalinearaddr0(scr, paddr, size)) != nil) error(err); } static char* bootmapfb(VGAscr *scr, uvlong pa, ulong sz) { uvlong start, end; Pcidev *p; int i; for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){ for(i=0; i<nelem(p->mem); i++){ if(p->mem[i].size == 0 || (p->mem[i].bar & 1) != 0) continue; start = p->mem[i].bar & ~0xF; end = start + p->mem[i].size; if(pa == start && (pa + sz) <= end){ scr->pci = p; return vgalinearpci0(scr); } } } upaalloc(pa, sz, 0); return vgalinearaddr0(scr, pa, sz); } char* rgbmask2chan(char *buf, int depth, u32int rm, u32int gm, u32int bm) { u32int m[4], dm; /* r,g,b,x */ char tmp[32]; int c, n; dm = 1<<depth-1; dm |= dm-1; m[0] = rm & dm; m[1] = gm & dm; m[2] = bm & dm; m[3] = (~(m[0] | m[1] | m[2])) & dm; buf[0] = 0; Next: for(c=0; c<4; c++){ for(n = 0; m[c] & (1<<n); n++) ; if(n){ m[0] >>= n, m[1] >>= n, m[2] >>= n, m[3] >>= n; snprint(tmp, sizeof tmp, "%c%d%s", "rgbx"[c], n, buf); strcpy(buf, tmp); goto Next; } } return buf; } /* * called early on boot to attach to framebuffer * setup by bootloader/firmware or plan9. */ void bootscreeninit(void) { VGAscr *scr; int x, y, z, tilt; uvlong pa; ulong chan, sz; char *s, *p, *err; /* *bootscreen=WIDTHxHEIGHTxDEPTH CHAN PA [SZ] */ s = getconf("*bootscreen"); if(s == nil) return; x = strtoul(s, &s, 0); if(x == 0 || *s++ != 'x') return; y = strtoul(s, &s, 0); if(y == 0 || *s++ != 'x') return; z = strtoul(s, &s, 0); if(*s != ' ') return; if((p = strchr(++s, ' ')) == nil) return; *p = 0; chan = strtochan(s); *p = ' '; if(chan == 0 || chantodepth(chan) != z) return; sz = 0; pa = strtoull(p+1, &s, 0); if(pa == 0) return; if(*s == ' ') sz = strtoul(s+1, nil, 0); if(sz < x * y * (z+7)/8) sz = x * y * (z+7)/8; tilt = 0; if((p = getconf("tiltscreen")) != nil){ for(; tilt < nelem(tiltstr); tilt++) if(strcmp(p, tiltstr[tilt]) == 0) break; tilt &= 3; } scr = &vgascreen[0]; scr->dev = nil; scr->softscreen = 1; if((err = bootmapfb(scr, pa, sz)) != nil){ print("bootmapfb: %s\n", err); return; } if(memimageinit() < 0){ print("memimageinit failed\n"); return; } if((err = setscreensize0(scr, x, y, z, chan, tilt)) != nil){ print("setscreensize0: %s\n", err); return; } vgaimageinit(chan); vgascreenwin(scr); drawcmap(); scr->cur = &vgasoftcur; scr->cur->enable(scr); conf.monitor = 1; } /* * called from devvga when the framebuffer is setup * to set *bootscreen= that can be passed on to a * new kernel on reboot. */ void bootscreenconf(VGAscr *scr) { char conf[100], chan[30]; conf[0] = '\0'; if(scr != nil && scr->paddr != 0 && scr->gscreen != nil){ snprint(conf, sizeof(conf), "%dx%dx%d %s 0x%.8llux %d\n", scr->width, scr->height, scr->gscreen->depth, chantostr(chan, scr->gscreen->chan), scr->paddr, scr->apsize); ksetenv("tiltscreen", tiltstr[scr->tilt], 1); } ksetenv("*bootscreen", conf, 1); }