/*
* 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 <draw.h>
#include <memdraw.h>
#include <cursor.h>
#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;
}
}
|