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