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