]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/screen.c
devvga: disable hardware blanking when switching drivers, check softscreen
[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 = 0;
397 int hwblank = 0;
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 || scr->softscreen)
427                 return 0;
428         if(dst->data->bdata != scrd->bdata || src == nil || mask == nil)
429                 return 0;
430
431         /*
432          * If we have an opaque mask and source is one opaque
433          * pixel we can convert to the destination format and just
434          * replicate with memset.
435          */
436         m = Simplesrc|Simplemask|Fullmask;
437         if(scr->fill
438         && (par->state&m)==m
439         && ((par->srgba&0xFF) == 0xFF)
440         && (par->op&S) == S)
441                 return scr->fill(scr, par->r, par->sdval);
442
443         /*
444          * If no source alpha, an opaque mask, we can just copy the
445          * source onto the destination.  If the channels are the same and
446          * the source is not replicated, memmove suffices.
447          */
448         m = Simplemask|Fullmask;
449         if(scr->scroll
450         && src->data->bdata==dst->data->bdata
451         && !(src->flags&Falpha)
452         && (par->state&m)==m
453         && (par->op&S) == S)
454                 return scr->scroll(scr, par->r, par->sr);
455
456         return 0;       
457 }
458
459 void
460 blankscreen(int blank)
461 {
462         VGAscr *scr;
463
464         scr = &vgascreen[0];
465         if(hwblank){
466                 if(scr->blank)
467                         scr->blank(scr, blank);
468                 else
469                         vgablank(scr, blank);
470         }
471 }
472
473 static char*
474 vgalinearaddr0(VGAscr *scr, ulong paddr, int size)
475 {
476         int x, nsize;
477         ulong npaddr;
478
479         /*
480          * new approach.  instead of trying to resize this
481          * later, let's assume that we can just allocate the
482          * entire window to start with.
483          */
484         if(scr->paddr == paddr && size <= scr->apsize)
485                 return nil;
486
487         if(scr->paddr){
488                 /*
489                  * could call vunmap and vmap,
490                  * but worried about dangling pointers in devdraw
491                  */
492                 return "cannot grow vga frame buffer";
493         }
494
495         /* round to page boundary, just in case */
496         x = paddr&(BY2PG-1);
497         npaddr = paddr-x;
498         nsize = PGROUND(size+x);
499
500         /*
501          * Don't bother trying to map more than 4000x4000x32 = 64MB.
502          * We only have a 256MB window.
503          */
504         if(nsize > 64*MB)
505                 nsize = 64*MB;
506         scr->vaddr = vmap(npaddr, nsize);
507         if(scr->vaddr == 0)
508                 return "cannot allocate vga frame buffer";
509         scr->vaddr = (char*)scr->vaddr+x;
510         scr->paddr = paddr;
511         scr->apsize = nsize;
512
513         mtrr(npaddr, nsize, "wc");
514
515         return nil;
516 }
517
518 static char*
519 vgalinearpci0(VGAscr *scr)
520 {
521         ulong paddr;
522         int i, size, best;
523         Pcidev *p;
524         
525         p = scr->pci;
526
527         /*
528          * Scan for largest memory region on card.
529          * Some S3 cards (e.g. Savage) have enormous
530          * mmio regions (but even larger frame buffers).
531          * Some 3dfx cards (e.g., Voodoo3) have mmio
532          * buffers the same size as the frame buffer,
533          * but only the frame buffer is marked as
534          * prefetchable (bar&8).  If a card doesn't fit
535          * into these heuristics, its driver will have to
536          * call vgalinearaddr directly.
537          */
538         best = -1;
539         for(i=0; i<nelem(p->mem); i++){
540                 if(p->mem[i].bar&1)     /* not memory */
541                         continue;
542                 if(p->mem[i].size < 640*480)    /* not big enough */
543                         continue;
544                 if(best==-1 
545                 || p->mem[i].size > p->mem[best].size 
546                 || (p->mem[i].size == p->mem[best].size 
547                   && (p->mem[i].bar&8)
548                   && !(p->mem[best].bar&8)))
549                         best = i;
550         }
551         if(best >= 0){
552                 paddr = p->mem[best].bar & ~0x0F;
553                 size = p->mem[best].size;
554                 return vgalinearaddr0(scr, paddr, size);
555         }
556         return "no video memory found on pci card";
557 }
558
559 void
560 vgalinearpci(VGAscr *scr)
561 {
562         char *err;
563
564         if(scr->pci == nil)
565                 return;
566         if((err = vgalinearpci0(scr)) != nil)
567                 error(err);
568 }
569
570 void
571 vgalinearaddr(VGAscr *scr, ulong paddr, int size)
572 {
573         char *err;
574
575         if((err = vgalinearaddr0(scr, paddr, size)) != nil)
576                 error(err);
577 }
578
579 static char*
580 bootmapfb(VGAscr *scr, ulong pa, ulong sz)
581 {
582         ulong start, end;
583         Pcidev *p;
584         int i;
585
586         for(p = pcimatch(nil, 0, 0); p != nil; p = pcimatch(p, 0, 0)){
587                 for(i=0; i<nelem(p->mem); i++){
588                         if(p->mem[i].bar & 1)
589                                 continue;
590                         start = p->mem[i].bar & ~0xF;
591                         end = start + p->mem[i].size;
592                         if(pa == start && (pa + sz) <= end){
593                                 scr->pci = p;
594                                 return vgalinearpci0(scr);
595                         }
596                 }
597         }
598         return vgalinearaddr0(scr, pa, sz);
599 }
600
601 /*
602  * called early on boot to attach to framebuffer
603  * setup by bootloader or firmware.
604  */
605 void
606 bootscreeninit(void)
607 {
608         static Memdata md;
609         VGAscr *scr;
610         int x, y, z;
611         ulong chan, pa, sz;
612         char *s, *p, *err;
613
614         /* *bootscreen=WIDTHxHEIGHTxDEPTH CHAN PA [SZ] */
615         s = getconf("*bootscreen");
616         if(s == nil)
617                 return;
618
619         x = strtoul(s, &s, 0);
620         if(x == 0 || *s++ != 'x')
621                 return;
622
623         y = strtoul(s, &s, 0);
624         if(y == 0 || *s++ != 'x')
625                 return;
626
627         z = strtoul(s, &s, 0);
628         if(*s != ' ')
629                 return;
630         if((p = strchr(++s, ' ')) == nil)
631                 return;
632         *p = 0;
633         chan = strtochan(s);
634         *p = ' ';
635         if(chan == 0 || chantodepth(chan) != z)
636                 return;
637
638         sz = 0;
639         pa = strtoul(p+1, &s, 0);
640         if(pa == 0)
641                 return;
642         if(*s++ == ' ')
643                 sz = strtoul(s, nil, 0);
644         if(sz < x * y * (z+7)/8)
645                 sz = x * y * (z+7)/8;
646
647         /* map framebuffer */
648         scr = &vgascreen[0];
649         if((err = bootmapfb(scr, pa, sz)) != nil){
650                 print("bootmapfb: %s\n", err);
651                 return;
652         }
653
654         if(memimageinit() < 0)
655                 return;
656
657         md.ref = 1;
658         md.bdata = scr->vaddr;
659         gscreen = allocmemimaged(Rect(0,0,x,y), chan, &md);
660         if(gscreen == nil)
661                 return;
662
663         scr->palettedepth = 6;  /* default */
664         scr->memdefont = getmemdefont();
665         scr->gscreen = gscreen;
666         scr->gscreendata = gscreen->data;
667         scr->softscreen = 0;
668         scr->useflush = 0;
669         scr->dev = nil;
670
671         physgscreenr = gscreen->r;
672
673         vgaimageinit(chan);
674         vgascreenwin(scr);
675
676         /* turn mouse cursor on */
677         swcursorinit();
678         scr->cur = &swcursor;
679         scr->cur->enable(scr);
680         cursoron();
681 }