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