]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/screen.c
aux/vga: simplify vesa by adding rgbmask2chan() function
[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 /*
591  * called early on boot to attach to framebuffer
592  * setup by bootloader/firmware or plan9.
593  */
594 void
595 bootscreeninit(void)
596 {
597         static Memdata md;
598         VGAscr *scr;
599         int x, y, z;
600         ulong chan, pa, sz;
601         char *s, *p, *err;
602
603         /* *bootscreen=WIDTHxHEIGHTxDEPTH CHAN PA [SZ] */
604         s = getconf("*bootscreen");
605         if(s == nil)
606                 return;
607
608         x = strtoul(s, &s, 0);
609         if(x == 0 || *s++ != 'x')
610                 return;
611
612         y = strtoul(s, &s, 0);
613         if(y == 0 || *s++ != 'x')
614                 return;
615
616         z = strtoul(s, &s, 0);
617         if(*s != ' ')
618                 return;
619         if((p = strchr(++s, ' ')) == nil)
620                 return;
621         *p = 0;
622         chan = strtochan(s);
623         *p = ' ';
624         if(chan == 0 || chantodepth(chan) != z)
625                 return;
626
627         sz = 0;
628         pa = strtoul(p+1, &s, 0);
629         if(pa == 0)
630                 return;
631         if(*s++ == ' ')
632                 sz = strtoul(s, nil, 0);
633         if(sz < x * y * (z+7)/8)
634                 sz = x * y * (z+7)/8;
635
636         /* map framebuffer */
637         scr = &vgascreen[0];
638         if((err = bootmapfb(scr, pa, sz)) != nil){
639                 print("bootmapfb: %s\n", err);
640                 return;
641         }
642
643         if(memimageinit() < 0)
644                 return;
645
646         md.ref = 1;
647         md.bdata = scr->vaddr;
648         gscreen = allocmemimaged(Rect(0,0,x,y), chan, &md);
649         if(gscreen == nil)
650                 return;
651
652         scr->palettedepth = 6;  /* default */
653         scr->memdefont = getmemdefont();
654         scr->gscreen = gscreen;
655         scr->gscreendata = gscreen->data;
656         scr->softscreen = 0;
657         scr->useflush = 0;
658         scr->dev = nil;
659
660         physgscreenr = gscreen->r;
661
662         vgaimageinit(chan);
663         vgascreenwin(scr);
664
665         /* turn mouse cursor on */
666         swcursorinit();
667         scr->cur = &swcursor;
668         scr->cur->enable(scr);
669         cursoron();
670
671         conf.monitor = 1;
672 }
673
674 /*
675  * called from devvga when the framebuffer is setup
676  * to set *bootscreen= that can be passed on to a
677  * new kernel on reboot.
678  */
679 void
680 bootscreenconf(VGAscr *scr)
681 {
682         char conf[100], chan[30];
683
684         conf[0] = '\0';
685         if(scr != nil && scr->paddr != 0)
686                 snprint(conf, sizeof(conf), "%dx%dx%d %s %#p %d\n",
687                         scr->gscreen->r.max.x, scr->gscreen->r.max.y,
688                         scr->gscreen->depth, chantostr(chan, scr->gscreen->chan),
689                         scr->paddr, scr->apsize);
690
691         ksetenv("*bootscreen", conf, 1);
692 }