]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/screen.c
pc, pc64: more conservative pcirouting
[plan9front.git] / sys / src / 9 / pc / screen.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "ureg.h"
8 #include "../port/error.h"
9
10 #define Image   IMAGE
11 #include <draw.h>
12 #include <memdraw.h>
13 #include <cursor.h>
14 #include "screen.h"
15
16 Rectangle physgscreenr;
17
18 Memimage *gscreen;
19
20 VGAscr vgascreen[1];
21
22 Cursor arrow = {
23         { -1, -1 },
24         { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, 
25           0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, 
26           0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, 
27           0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, 
28         },
29         { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, 
30           0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, 
31           0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, 
32           0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, 
33         },
34 };
35
36 int
37 screensize(int x, int y, int, ulong chan)
38 {
39         VGAscr *scr;
40
41         qlock(&drawlock);
42         if(waserror()){
43                 qunlock(&drawlock);
44                 nexterror();
45         }
46
47         if(memimageinit() < 0)
48                 error("memimageinit failed");
49
50         lock(&vgascreenlock);
51         if(waserror()){
52                 unlock(&vgascreenlock);
53                 nexterror();
54         }
55
56         scr = &vgascreen[0];
57         scr->gscreendata = nil;
58         scr->gscreen = nil;
59         if(gscreen){
60                 freememimage(gscreen);
61                 gscreen = nil;
62         }
63
64         if(scr->paddr == 0){
65                 if(scr->dev && scr->dev->page){
66                         scr->vaddr = KADDR(VGAMEM());
67                         scr->apsize = 1<<16;
68                 }
69                 scr->softscreen = 1;
70         }
71         if(scr->softscreen){
72                 gscreen = allocmemimage(Rect(0,0,x,y), chan);
73                 scr->useflush = 1;
74         }else{
75                 static Memdata md;
76
77                 md.ref = 1;
78                 if((md.bdata = scr->vaddr) == 0)
79                         error("framebuffer not maped");
80                 gscreen = allocmemimaged(Rect(0,0,x,y), chan, &md);
81                 scr->useflush = scr->dev && scr->dev->flush;
82         }
83         if(gscreen == nil)
84                 error("no memory for vga memimage");
85
86         scr->palettedepth = 6;  /* default */
87         scr->memdefont = getmemdefont();
88         scr->gscreen = gscreen;
89         scr->gscreendata = gscreen->data;
90
91         physgscreenr = gscreen->r;
92
93         vgaimageinit(chan);
94
95         unlock(&vgascreenlock);
96         poperror();
97
98         drawcmap();
99         swcursorinit();
100
101         qunlock(&drawlock);
102         poperror();
103
104         return 0;
105 }
106
107 int
108 screenaperture(int size, int align)
109 {
110         VGAscr *scr;
111
112         scr = &vgascreen[0];
113
114         if(scr->paddr)  /* set up during enable */
115                 return 0;
116
117         if(size == 0)
118                 return 0;
119
120         if(scr->dev && scr->dev->linear){
121                 scr->dev->linear(scr, size, align);
122                 return 0;
123         }
124
125         /*
126          * Need to allocate some physical address space.
127          * The driver will tell the card to use it.
128          */
129         size = PGROUND(size);
130         scr->paddr = upaalloc(size, align);
131         if(scr->paddr == 0)
132                 return -1;
133         scr->vaddr = vmap(scr->paddr, size);
134         if(scr->vaddr == nil)
135                 return -1;
136         scr->apsize = size;
137
138         return 0;
139 }
140
141 uchar*
142 attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen)
143 {
144         VGAscr *scr;
145
146         scr = &vgascreen[0];
147         if(scr->gscreen == nil || scr->gscreendata == nil)
148                 return nil;
149
150         *r = scr->gscreen->clipr;
151         *chan = scr->gscreen->chan;
152         *d = scr->gscreen->depth;
153         *width = scr->gscreen->width;
154         if(scr->gscreendata->allocd){
155                 /*
156                  * we use a memimage as softscreen. devdraw will create its own
157                  * screen image on the backing store of that image. when our gscreen
158                  * and devdraws screenimage gets freed, the imagedata will
159                  * be released.
160                  */
161                 *softscreen = 0xa110c;
162                 scr->gscreendata->ref++;
163         } else
164                 *softscreen = scr->useflush ? 1 : 0;
165         return scr->gscreendata->bdata;
166 }
167
168 void
169 flushmemscreen(Rectangle r)
170 {
171         VGAscr *scr;
172         uchar *sp, *disp, *sdisp, *edisp;
173         int y, len, incs, off, page;
174
175         scr = &vgascreen[0];
176         if(scr->gscreen == nil || scr->useflush == 0)
177                 return;
178         if(rectclip(&r, scr->gscreen->r) == 0)
179                 return;
180         if(scr->dev && scr->dev->flush){
181                 scr->dev->flush(scr, r);
182                 return;
183         }
184         disp = scr->vaddr;
185         incs = scr->gscreen->width*sizeof(ulong);
186         off = (r.min.x*scr->gscreen->depth) / 8;
187         len = (r.max.x*scr->gscreen->depth + 7) / 8;
188         len -= off;
189         off += r.min.y*incs;
190         sp = scr->gscreendata->bdata + scr->gscreen->zero + off;
191
192         /*
193          * Linear framebuffer with softscreen.
194          */
195         if(scr->paddr){
196                 sdisp = disp+off;
197                 for(y = r.min.y; y < r.max.y; y++) {
198                         memmove(sdisp, sp, len);
199                         sp += incs;
200                         sdisp += incs;
201                 }
202                 return;
203         }
204
205         /*
206          * Paged framebuffer window.
207          */
208         if(scr->dev == nil || scr->dev->page == nil)
209                 return;
210
211         page = off/scr->apsize;
212         off %= scr->apsize;
213         sdisp = disp+off;
214         edisp = disp+scr->apsize;
215
216         scr->dev->page(scr, page);
217         for(y = r.min.y; y < r.max.y; y++) {
218                 if(sdisp + incs < edisp) {
219                         memmove(sdisp, sp, len);
220                         sp += incs;
221                         sdisp += incs;
222                 }
223                 else {
224                         off = edisp - sdisp;
225                         page++;
226                         if(off <= len){
227                                 if(off > 0)
228                                         memmove(sdisp, sp, off);
229                                 scr->dev->page(scr, page);
230                                 if(len - off > 0)
231                                         memmove(disp, sp+off, len - off);
232                         }
233                         else {
234                                 memmove(sdisp, sp, len);
235                                 scr->dev->page(scr, page);
236                         }
237                         sp += incs;
238                         sdisp += incs - scr->apsize;
239                 }
240         }
241 }
242
243 void
244 getcolor(ulong p, ulong* pr, ulong* pg, ulong* pb)
245 {
246         VGAscr *scr;
247         ulong x;
248
249         scr = &vgascreen[0];
250         if(scr->gscreen == nil)
251                 return;
252
253         switch(scr->gscreen->depth){
254         default:
255                 x = 0x0F;
256                 break;
257         case 8:
258                 x = 0xFF;
259                 break;
260         }
261         p &= x;
262
263         lock(&cursor);
264         *pr = scr->colormap[p][0];
265         *pg = scr->colormap[p][1];
266         *pb = scr->colormap[p][2];
267         unlock(&cursor);
268 }
269
270 int
271 setpalette(ulong p, ulong r, ulong g, ulong b)
272 {
273         VGAscr *scr;
274         int d;
275
276         scr = &vgascreen[0];
277         d = scr->palettedepth;
278
279         lock(&cursor);
280         scr->colormap[p][0] = r;
281         scr->colormap[p][1] = g;
282         scr->colormap[p][2] = b;
283         vgao(PaddrW, p);
284         vgao(Pdata, r>>(32-d));
285         vgao(Pdata, g>>(32-d));
286         vgao(Pdata, b>>(32-d));
287         unlock(&cursor);
288
289         return ~0;
290 }
291
292 /*
293  * On some video cards (e.g. Mach64), the palette is used as the 
294  * DAC registers for >8-bit modes.  We don't want to set them when the user
295  * is trying to set a colormap and the card is in one of these modes.
296  */
297 int
298 setcolor(ulong p, ulong r, ulong g, ulong b)
299 {
300         VGAscr *scr;
301         int x;
302
303         scr = &vgascreen[0];
304         if(scr->gscreen == nil)
305                 return 0;
306
307         switch(scr->gscreen->depth){
308         case 1:
309         case 2:
310         case 4:
311                 x = 0x0F;
312                 break;
313         case 8:
314                 x = 0xFF;
315                 break;
316         default:
317                 return 0;
318         }
319         p &= x;
320
321         return setpalette(p, r, g, b);
322 }
323
324 void
325 swenable(VGAscr*)
326 {
327         swcursorload(&arrow);
328 }
329
330 void
331 swdisable(VGAscr*)
332 {
333 }
334
335 void
336 swload(VGAscr*, Cursor *curs)
337 {
338         swcursorload(curs);
339 }
340
341 int
342 swmove(VGAscr*, Point p)
343 {
344         swcursorhide();
345         swcursordraw(p);
346         return 0;
347 }
348
349 VGAcur swcursor =
350 {
351         "soft",
352         swenable,
353         swdisable,
354         swload,
355         swmove,
356 };
357
358
359 void
360 cursoron(void)
361 {
362         VGAscr *scr;
363         VGAcur *cur;
364
365         scr = &vgascreen[0];
366         cur = scr->cur;
367         if(cur == nil || cur->move == nil)
368                 return;
369
370         if(cur == &swcursor)
371                 qlock(&drawlock);
372         lock(&cursor);
373         cur->move(scr, mousexy());
374         unlock(&cursor);
375         if(cur == &swcursor)
376                 qunlock(&drawlock);
377 }
378
379 void
380 cursoroff(void)
381 {
382 }
383
384 void
385 setcursor(Cursor* curs)
386 {
387         VGAscr *scr;
388
389         scr = &vgascreen[0];
390         if(scr->cur == nil || scr->cur->load == nil)
391                 return;
392
393         scr->cur->load(scr, curs);
394 }
395
396 int hwaccel = 1;
397 int hwblank = 0;        /* turned on by drivers that are known good */
398 int panning = 0;
399
400 int
401 hwdraw(Memdrawparam *par)
402 {
403         VGAscr *scr;
404         Memimage *dst, *src, *mask;
405         Memdata *scrd;
406         int m;
407
408         scr = &vgascreen[0];
409         scrd = scr->gscreendata;
410         if(scr->gscreen == nil || scrd == nil)
411                 return 0;
412         if((dst = par->dst) == nil || dst->data == nil)
413                 return 0;
414         if((src = par->src) && src->data == nil)
415                 src = nil;
416         if((mask = par->mask) && mask->data == nil)
417                 mask = nil;
418         if(scr->cur == &swcursor){
419                 if(dst->data->bdata == scrd->bdata)
420                         swcursoravoid(par->r);
421                 if(src && src->data->bdata == scrd->bdata)
422                         swcursoravoid(par->sr);
423                 if(mask && mask->data->bdata == scrd->bdata)
424                         swcursoravoid(par->mr);
425         }
426         if(hwaccel == 0)
427                 return 0;
428         if(dst->data->bdata != scrd->bdata || src == nil || mask == nil)
429                 return 0;
430         if(scr->fill==nil && scr->scroll==nil)
431                 return 0;
432
433         /*
434          * If we have an opaque mask and source is one opaque
435          * pixel we can convert to the destination format and just
436          * replicate with memset.
437          */
438         m = Simplesrc|Simplemask|Fullmask;
439         if(scr->fill
440         && (par->state&m)==m
441         && ((par->srgba&0xFF) == 0xFF)
442         && (par->op&S) == S)
443                 return scr->fill(scr, par->r, par->sdval);
444
445         /*
446          * If no source alpha, an opaque mask, we can just copy the
447          * source onto the destination.  If the channels are the same and
448          * the source is not replicated, memmove suffices.
449          */
450         m = Simplemask|Fullmask;
451         if(scr->scroll
452         && src->data->bdata==dst->data->bdata
453         && !(src->flags&Falpha)
454         && (par->state&m)==m
455         && (par->op&S) == S)
456                 return scr->scroll(scr, par->r, par->sr);
457
458         return 0;       
459 }
460
461 void
462 blankscreen(int blank)
463 {
464         VGAscr *scr;
465
466         scr = &vgascreen[0];
467         if(hwblank){
468                 if(scr->blank)
469                         scr->blank(scr, blank);
470                 else
471                         vgablank(scr, blank);
472         }
473 }
474
475 static char*
476 vgalinearaddr0(VGAscr *scr, ulong paddr, int size)
477 {
478         int x, nsize;
479         ulong npaddr;
480
481         /*
482          * new approach.  instead of trying to resize this
483          * later, let's assume that we can just allocate the
484          * entire window to start with.
485          */
486         if(scr->paddr == paddr && size <= scr->apsize)
487                 return nil;
488
489         if(scr->paddr){
490                 /*
491                  * could call vunmap and vmap,
492                  * but worried about dangling pointers in devdraw
493                  */
494                 return "cannot grow vga frame buffer";
495         }
496
497         /* round to page boundary, just in case */
498         x = paddr&(BY2PG-1);
499         npaddr = paddr-x;
500         nsize = PGROUND(size+x);
501
502         /*
503          * Don't bother trying to map more than 4000x4000x32 = 64MB.
504          * We only have a 256MB window.
505          */
506         if(nsize > 64*MB)
507                 nsize = 64*MB;
508         scr->vaddr = vmap(npaddr, nsize);
509         if(scr->vaddr == 0)
510                 return "cannot allocate vga frame buffer";
511         scr->vaddr = (char*)scr->vaddr+x;
512         scr->paddr = paddr;
513         scr->apsize = nsize;
514
515         mtrr(npaddr, nsize, "wc");
516
517         return nil;
518 }
519
520 static char*
521 vgalinearpci0(VGAscr *scr)
522 {
523         ulong paddr;
524         int i, size, best;
525         Pcidev *p;
526         
527         p = scr->pci;
528
529         /*
530          * Scan for largest memory region on card.
531          * Some S3 cards (e.g. Savage) have enormous
532          * mmio regions (but even larger frame buffers).
533          * Some 3dfx cards (e.g., Voodoo3) have mmio
534          * buffers the same size as the frame buffer,
535          * but only the frame buffer is marked as
536          * prefetchable (bar&8).  If a card doesn't fit
537          * into these heuristics, its driver will have to
538          * call vgalinearaddr directly.
539          */
540         best = -1;
541         for(i=0; i<nelem(p->mem); i++){
542                 if(p->mem[i].bar&1)     /* not memory */
543                         continue;
544                 if(p->mem[i].size < 640*480)    /* not big enough */
545                         continue;
546                 if(best==-1 
547                 || p->mem[i].size > p->mem[best].size 
548                 || (p->mem[i].size == p->mem[best].size 
549                   && (p->mem[i].bar&8)
550                   && !(p->mem[best].bar&8)))
551                         best = i;
552         }
553         if(best >= 0){
554                 paddr = p->mem[best].bar & ~0x0F;
555                 size = p->mem[best].size;
556                 return vgalinearaddr0(scr, paddr, size);
557         }
558         return "no video memory found on pci card";
559 }
560
561 void
562 vgalinearpci(VGAscr *scr)
563 {
564         char *err;
565
566         if(scr->pci == nil)
567                 return;
568         if((err = vgalinearpci0(scr)) != nil)
569                 error(err);
570 }
571
572 void
573 vgalinearaddr(VGAscr *scr, ulong paddr, int size)
574 {
575         char *err;
576
577         if((err = vgalinearaddr0(scr, paddr, size)) != nil)
578                 error(err);
579 }
580
581 static char*
582 bootmapfb(VGAscr *scr, ulong pa, ulong sz)
583 {
584         ulong start, end;
585         Pcidev *p;
586         int i;
587
588         for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){
589                 for(i=0; i<nelem(p->mem); i++){
590                         if(p->mem[i].bar & 1)
591                                 continue;
592                         start = p->mem[i].bar & ~0xF;
593                         end = start + p->mem[i].size;
594                         if(pa == start && (pa + sz) <= end){
595                                 scr->pci = p;
596                                 return vgalinearpci0(scr);
597                         }
598                 }
599         }
600         return vgalinearaddr0(scr, pa, sz);
601 }
602
603 /*
604  * called early on boot to attach to framebuffer
605  * setup by bootloader or firmware.
606  */
607 void
608 bootscreeninit(void)
609 {
610         static Memdata md;
611         VGAscr *scr;
612         int x, y, z;
613         ulong chan, pa, sz;
614         char *s, *p, *err;
615
616         /* *bootscreen=WIDTHxHEIGHTxDEPTH CHAN PA [SZ] */
617         s = getconf("*bootscreen");
618         if(s == nil)
619                 return;
620
621         x = strtoul(s, &s, 0);
622         if(x == 0 || *s++ != 'x')
623                 return;
624
625         y = strtoul(s, &s, 0);
626         if(y == 0 || *s++ != 'x')
627                 return;
628
629         z = strtoul(s, &s, 0);
630         if(*s != ' ')
631                 return;
632         if((p = strchr(++s, ' ')) == nil)
633                 return;
634         *p = 0;
635         chan = strtochan(s);
636         *p = ' ';
637         if(chan == 0 || chantodepth(chan) != z)
638                 return;
639
640         sz = 0;
641         pa = strtoul(p+1, &s, 0);
642         if(pa == 0)
643                 return;
644         if(*s++ == ' ')
645                 sz = strtoul(s, nil, 0);
646         if(sz < x * y * (z+7)/8)
647                 sz = x * y * (z+7)/8;
648
649         /* map framebuffer */
650         scr = &vgascreen[0];
651         if((err = bootmapfb(scr, pa, sz)) != nil){
652                 print("bootmapfb: %s\n", err);
653                 return;
654         }
655
656         if(memimageinit() < 0)
657                 return;
658
659         md.ref = 1;
660         md.bdata = scr->vaddr;
661         gscreen = allocmemimaged(Rect(0,0,x,y), chan, &md);
662         if(gscreen == nil)
663                 return;
664
665         scr->palettedepth = 6;  /* default */
666         scr->memdefont = getmemdefont();
667         scr->gscreen = gscreen;
668         scr->gscreendata = gscreen->data;
669         scr->softscreen = 0;
670         scr->useflush = 0;
671         scr->dev = nil;
672
673         hwblank = 0;
674         hwaccel = 0;
675
676         physgscreenr = gscreen->r;
677
678         vgaimageinit(chan);
679         vgascreenwin(scr);
680
681         /* turn mouse cursor on */
682         swcursorinit();
683         scr->cur = &swcursor;
684         scr->cur->enable(scr);
685         cursoron();
686 }