/* * ti omap35 display subsystem (dss) * * can handle 2ⁿ bits per pixel for 0 < n ≤ 4, and 12 and 24 bits. * can handle 1024×768 at 60 Hz with pixel clock of 63.5 MHz * 1280×800 at 59.91 Hz with pixel clock of 71 MHz * 1400×1050 lcd at 50 MHz with pixel clock of 75 MHz * has 256 24-bit entries in RGB palette */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "../port/error.h" #define Image IMAGE #include #include #include #include "screen.h" // #include "gamma.h" enum { Tabstop = 4, /* should be 8 */ Scroll = 8, /* lines to scroll at one time */ /* * screen settings for Wid and Ht, should a bit more dynamic? * http://www.epanorama.net/faq/vga2rgb/calc.html * used to calculate settings. */ // Hbp = (248-1) << 20, // Hfp = (48-1) << 8, // Hsw = 112-1, // Vbp = 38 << 20, // Vfp = 1 << 8, // Vsw = 3, Tft = 0x60, Loadmode = 2 << 1, Fifosize = 0x400, /* dispc sysconfig */ Midlemode = 2 << 12, Sidlemode = 2 << 3, EnableWakeup = 1 << 2, Autoidle = 1 << 0, /* dispc pool_freq */ Ipc = 1 << 14, Ihs = 1 << 13, Ivs = 1 << 12, Acb = 0x28, /* gfx attribs */ Burstsize = 2 << 6, Format = 6 << 1, Gfxenable = 1 << 0, /* dispc control */ Gpout1 = 1 << 16, Gpout0 = 1 << 15, Tftdata = 3 << 8, Digital = 1 << 6, Lcd = 1 << 5, Stntft = 1 << 3, Digitalen = 1 << 1, // Lcden = 1 << 0, /* unused */ }; typedef struct Dispcregs Dispc; typedef struct Dssregs Dss; typedef struct Ioregs Ioregs; struct Ioregs { /* common registers, 68 (0x44) bytes */ ulong rev; uchar _pad0[0x10-0x4]; ulong sysconf; ulong sysstat; ulong irqstat1; /* Dispc only regs */ ulong irqen1; ulong wkupen; ulong _pad1; ulong irqsts2; ulong irqen2; ulong _pad2[4]; ulong ctrl; }; struct Dssregs { /* display subsys at 0x48050000 */ Ioregs; ulong sdicrtl; ulong pllcrtl; uchar _pad3[0x5c-0x4c]; ulong sdistat; }; struct Dispcregs { /* display ctlr at 0x48050400 */ Ioregs; ulong config; ulong _pad3; ulong defaultcolor[2]; ulong transcolor[2]; ulong linestat; ulong linenum; ulong timing_h; ulong timing_v; ulong pol_req; ulong divisor; ulong alpha; ulong digsize; ulong lcdsize; ulong base[2]; /* should allocate both to avoid dithering */ ulong pos; ulong size; ulong _pad4[4]; ulong attrib; ulong fifothr; ulong fifosize; ulong rowinc; ulong pixelinc; ulong winskip; ulong palette; /* gfx_table_ba */ uchar _pad5[0x5d4 - 0x4bc]; ulong datacycle[3]; uchar _pad5[0x620 - 0x5e0]; ulong cprcoefr; ulong cprcoefg; ulong cprcoefb; ulong preload; }; int drawdebug; Point ZP = {0, 0}; Cursor arrow = { { -1, -1 }, { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, }, { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, }, }; OScreen oscreen; Settings settings[] = { [Res800x600] { 800, 600, 60, RGB16, 40000, 88, 40, 128, 23, 1, 5, }, [Res1024x768] { 1024, 768, 60, RGB16, 65000, 160, 24, 136, 29, 3, 7, }, [Res1280x1024] { 1280, 1024, 60, RGB16, 108000, 248, 48, 112, 38, 1, 4, }, [Res1400x1050] { 1400, 1050, 50, RGB16, 108000, 248, 48, 112, 38, 1, 4, }, // TODO }; Omap3fb *framebuf; Memimage *gscreen; static Memdata xgdata; static Memimage xgscreen = { { 0, 0, Wid, Ht }, /* r */ { 0, 0, Wid, Ht }, /* clipr */ Depth, /* depth */ 3, /* nchan */ RGB16, /* chan */ nil, /* cmap */ &xgdata, /* data */ 0, /* zero */ Wid*(Depth/BI2BY)/BY2WD, /* width in words of a single scan line */ 0, /* layer */ 0, /* flags */ }; static Memimage *conscol; static Memimage *back; static Memsubfont *memdefont; static Lock screenlock; static Point curpos; static int h, w; static int landscape = 0; /* screen orientation, default is 0: portrait */ static ushort *vscreen; /* virtual screen */ static Rectangle window; static Dispc *dispc = (Dispc *)PHYSDISPC; static Dss *dss = (Dss *)PHYSDSS; static void omapscreenputs(char *s, int n); static ulong rep(ulong, int); static void screenputc(char *buf); static void screenwin(void); /* * Software cursor. */ int swvisible; /* is the cursor visible? */ int swenabled; /* is the cursor supposed to be on the screen? */ Memimage* swback; /* screen under cursor */ Memimage* swimg; /* cursor image */ Memimage* swmask; /* cursor mask */ Memimage* swimg1; Memimage* swmask1; Point swoffset; Rectangle swrect; /* screen rectangle in swback */ Point swpt; /* desired cursor location */ Point swvispt; /* actual cursor location */ int swvers; /* incremented each time cursor image changes */ int swvisvers; /* the version on the screen */ static void lcdoff(void) { dispc->ctrl &= ~1; /* disable the lcd */ coherence(); dispc->irqstat1 |= 1; /* set framedone */ coherence(); /* the lcd never comes ready, so don't bother with this */ #ifdef notdef /* spin until the frame is complete, but not forever */ for(cnt = 50; !(dispc->irqstat1 & 1) && cnt-- > 0; ) delay(10); #endif delay(20); /* worst case for 1 frame, 50Hz */ } static void dssstart(void) { /* should reset the dss system */ dss->sysconf |= 1; coherence(); } /* see spruf98i §15.6.7.4.2 */ static void configdispc(void) { Settings *sp; sp = oscreen.settings; dss->ctrl &= 0x78; /* choose dss clock */ dispc->sysconf = Midlemode | Sidlemode | EnableWakeup | Autoidle; dispc->config = Loadmode; coherence(); /* pll */ dispc->defaultcolor[0] = 0; /* set background color to black? */ dispc->defaultcolor[1] = 0; dispc->transcolor[0] = 0; /* set transparency to full */ dispc->transcolor[1] = 0; dispc->timing_h = (sp->hbp-1) << 20 | (sp->hfp-1) << 8 | (sp->hsw-1); dispc->timing_v = sp->vbp << 20 | sp->vfp << 8 | (sp->vsw-1); dispc->pol_req = Ipc | Ihs | Ivs | Acb; dispc->divisor = 1 << 16 | HOWMANY(432000, sp->pixelclock); dispc->lcdsize = (sp->ht - 1) << 16 | (sp->wid - 1); coherence(); dispc->base[0] = PADDR(framebuf->pixel); dispc->base[1] = PADDR(framebuf->pixel); dispc->pos = 0; /* place screen in the left corner */ /* use the whole screen */ dispc->size = (sp->ht - 1) << 16 | (sp->wid - 1); /* what mode does plan 9 use for fb? */ dispc->attrib = Burstsize | Format | Gfxenable; dispc->preload = Tft; dispc->fifosize = Fifosize; /* 1008 is max for our Burstsize */ dispc->fifothr = (Fifosize - 1) << 16 | (1008 - 1); /* 1 byte is one pixel (not true, we use 2 bytes per pixel) */ dispc->rowinc = 1; dispc->pixelinc = 1; dispc->winskip = 0; /* don't skip anything */ coherence(); // dispc->palette = PADDR(framebuf->palette); } static void lcdon(int enable) { dispc->ctrl = Gpout1 | Gpout0 | Tftdata | Digital | Lcd | Stntft | Digitalen | enable; coherence(); delay(10); } static void lcdstop(void) { configscreengpio(); screenclockson(); lcdoff(); } static void lcdinit(void) { lcdstop(); dssstart(); configdispc(); } /* Paint the image data with blue pixels */ void screentest(void) { int i; for (i = nelem(framebuf->pixel) - 1; i >= 0; i--) framebuf->pixel[i] = 0x1f; /* blue */ // memset(framebuf->pixel, ~0, sizeof framebuf->pixel); /* white */ } void screenpower(int on) { blankscreen(on == 0); } /* * called with drawlock locked for us, most of the time. * kernel prints at inopportune times might mean we don't * hold the lock, but memimagedraw is now reentrant so * that should be okay: worst case we get cursor droppings. */ void swcursorhide(void) { if(swvisible == 0) return; if(swback == nil) return; swvisible = 0; memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S); flushmemscreen(swrect); } void swcursoravoid(Rectangle r) { if(swvisible && rectXrect(r, swrect)) swcursorhide(); } void swcursordraw(void) { if(swvisible) return; if(swenabled == 0) return; if(swback == nil || swimg1 == nil || swmask1 == nil) return; // assert(!canqlock(&drawlock)); // assertion fails on omap swvispt = swpt; swvisvers = swvers; swrect = rectaddpt(Rect(0,0,16,16), swvispt); memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S); memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD); flushmemscreen(swrect); swvisible = 1; } int cursoron(int dolock) { if (dolock) lock(&oscreen); cursoroff(0); swcursordraw(); if (dolock) unlock(&oscreen); return 0; } void cursoroff(int dolock) { if (dolock) lock(&oscreen); swcursorhide(); if (dolock) unlock(&oscreen); } void swload(Cursor *curs) { uchar *ip, *mp; int i, j, set, clr; if(!swimg || !swmask || !swimg1 || !swmask1) return; /* * Build cursor image and mask. * Image is just the usual cursor image * but mask is a transparent alpha mask. * * The 16x16x8 memimages do not have * padding at the end of their scan lines. */ ip = byteaddr(swimg, ZP); mp = byteaddr(swmask, ZP); for(i=0; i<32; i++){ set = curs->set[i]; clr = curs->clr[i]; for(j=0x80; j; j>>=1){ *ip++ = set&j ? 0x00 : 0xFF; *mp++ = (clr|set)&j ? 0xFF : 0x00; } } swoffset = curs->offset; swvers++; memimagedraw(swimg1, swimg1->r, swimg, ZP, memopaque, ZP, S); memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S); } /* called from devmouse */ void setcursor(Cursor* curs) { cursoroff(1); oscreen.Cursor = *curs; swload(curs); cursoron(1); } int swmove(Point p) { swpt = addpt(p, swoffset); return 0; } void swcursorclock(void) { int x; if(!swenabled) return; swmove(mousexy()); if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers) return; x = splhi(); if(swenabled) if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers) if(canqlock(&drawlock)){ swcursorhide(); swcursordraw(); qunlock(&drawlock); } splx(x); } void swcursorinit(void) { static int init; if(!init){ init = 1; addclock0link(swcursorclock, 10); } if(swback){ freememimage(swback); freememimage(swmask); freememimage(swmask1); freememimage(swimg); freememimage(swimg1); } swback = allocmemimage(Rect(0,0,32,32), gscreen->chan); swmask = allocmemimage(Rect(0,0,16,16), GREY8); swmask1 = allocmemimage(Rect(0,0,16,16), GREY1); swimg = allocmemimage(Rect(0,0,16,16), GREY8); swimg1 = allocmemimage(Rect(0,0,16,16), GREY1); if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){ print("software cursor: allocmemimage fails\n"); return; } memfillcolor(swmask, DOpaque); memfillcolor(swmask1, DOpaque); memfillcolor(swimg, DBlack); memfillcolor(swimg1, DBlack); } /* called from main and possibly later from devdss to change resolution */ void screeninit(void) { static int first = 1; if (first) { iprint("screeninit..."); oscreen.settings = &settings[Res1280x1024]; lcdstop(); if (framebuf) free(framebuf); /* mode is 16*32 = 512 */ framebuf = xspanalloc(sizeof *framebuf, 16*32, 0); } lcdinit(); lcdon(1); if (first) { memimageinit(); memdefont = getmemdefont(); screentest(); } xgdata.ref = 1; xgdata.bdata = (uchar *)framebuf->pixel; gscreen = &xgscreen; gscreen->r = Rect(0, 0, Wid, Ht); gscreen->clipr = gscreen->r; /* width, in words, of a single scan line */ gscreen->width = Wid * (Depth / BI2BY) / BY2WD; flushmemscreen(gscreen->r); blanktime = 3; /* minutes */ if (first) { iprint("on: blue for 3 seconds..."); delay(3*1000); iprint("\n"); screenwin(); /* draw border & top orange bar */ screenputs = omapscreenputs; iprint("screen: frame buffer at %#p for %dx%d\n", framebuf, oscreen.settings->wid, oscreen.settings->ht); swenabled = 1; swcursorinit(); /* needs gscreen set */ setcursor(&arrow); first = 0; } } /* flushmemscreen should change buffer? */ void flushmemscreen(Rectangle r) { ulong start, end; if (r.min.x < 0) r.min.x = 0; if (r.max.x > Wid) r.max.x = Wid; if (r.min.y < 0) r.min.y = 0; if (r.max.y > Ht) r.max.y = Ht; if (rectclip(&r, gscreen->r) == 0) return; start = (ulong)&framebuf->pixel[r.min.y*Wid + r.min.x]; end = (ulong)&framebuf->pixel[(r.max.y - 1)*Wid + r.max.x -1]; cachedwbse((ulong *)start, end - start); } /* * export screen to devdraw */ uchar* attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen) { *r = gscreen->r; *d = gscreen->depth; *chan = gscreen->chan; *width = gscreen->width; *softscreen = (landscape == 0); return (uchar *)gscreen->data->bdata; } void getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb) { USED(p, pr, pg, pb); } int setcolor(ulong p, ulong r, ulong g, ulong b) { USED(p, r, g, b); return 0; } void blankscreen(int blank) { if (blank) lcdon(0); else { lcdinit(); lcdon(1); } } static void omapscreenputs(char *s, int n) { int i; Rune r; char buf[4]; if (!islo()) { /* don't deadlock trying to print in interrupt */ if (!canlock(&screenlock)) return; /* discard s */ } else lock(&screenlock); while (n > 0) { i = chartorune(&r, s); if (i == 0) { s++; --n; continue; } memmove(buf, s, i); buf[i] = 0; n -= i; s += i; screenputc(buf); } unlock(&screenlock); } static void screenwin(void) { char *greet; Memimage *orange; Point p, q; Rectangle r; memsetchan(gscreen, RGB16); back = memwhite; conscol = memblack; orange = allocmemimage(Rect(0, 0, 1, 1), RGB16); orange->flags |= Frepl; orange->clipr = gscreen->r; orange->data->bdata[0] = 0x40; /* magic: colour? */ orange->data->bdata[1] = 0xfd; /* magic: colour? */ w = memdefont->info[' '].width; h = memdefont->height; r = insetrect(gscreen->r, 4); memimagedraw(gscreen, r, memblack, ZP, memopaque, ZP, S); window = insetrect(r, 4); memimagedraw(gscreen, window, memwhite, ZP, memopaque, ZP, S); memimagedraw(gscreen, Rect(window.min.x, window.min.y, window.max.x, window.min.y + h + 5 + 6), orange, ZP, nil, ZP, S); freememimage(orange); window = insetrect(window, 5); greet = " Plan 9 Console "; p = addpt(window.min, Pt(10, 0)); q = memsubfontwidth(memdefont, greet); memimagestring(gscreen, p, conscol, ZP, memdefont, greet); flushmemscreen(r); window.min.y += h + 6; curpos = window.min; window.max.y = window.min.y + ((window.max.y - window.min.y) / h) * h; } static void scroll(void) { int o; Point p; Rectangle r; /* move window contents up Scroll text lines */ o = Scroll * h; r = Rpt(window.min, Pt(window.max.x, window.max.y - o)); p = Pt(window.min.x, window.min.y + o); memimagedraw(gscreen, r, gscreen, p, nil, p, S); flushmemscreen(r); /* clear the bottom Scroll text lines */ r = Rpt(Pt(window.min.x, window.max.y - o), window.max); memimagedraw(gscreen, r, back, ZP, nil, ZP, S); flushmemscreen(r); curpos.y -= o; } static void screenputc(char *buf) { int w; uint pos; Point p; Rectangle r; static int *xp; static int xbuf[256]; if (xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) xp = xbuf; switch (buf[0]) { case '\n': if (curpos.y + h >= window.max.y) scroll(); curpos.y += h; screenputc("\r"); break; case '\r': xp = xbuf; curpos.x = window.min.x; break; case '\t': p = memsubfontwidth(memdefont, " "); w = p.x; if (curpos.x >= window.max.x - Tabstop * w) screenputc("\n"); pos = (curpos.x - window.min.x) / w; pos = Tabstop - pos % Tabstop; *xp++ = curpos.x; r = Rect(curpos.x, curpos.y, curpos.x + pos * w, curpos.y + h); memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S); flushmemscreen(r); curpos.x += pos * w; break; case '\b': if (xp <= xbuf) break; xp--; r = Rect(*xp, curpos.y, curpos.x, curpos.y + h); memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S); flushmemscreen(r); curpos.x = *xp; break; case '\0': break; default: p = memsubfontwidth(memdefont, buf); w = p.x; if (curpos.x >= window.max.x - w) screenputc("\n"); *xp++ = curpos.x; r = Rect(curpos.x, curpos.y, curpos.x + w, curpos.y + h); memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S); memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf); flushmemscreen(r); curpos.x += w; } }