Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"

/*
 * Mailbox interface with videocore gpu
 */

#define MAILBOX         (VIRTIO+0xB880)

typedef struct Prophdr Prophdr;
typedef struct Fbinfo Fbinfo;

enum {
        Read            = 0x00>>2,
        Write           = 0x00>>2,
        Peek            = 0x10>>2,
        Sender          = 0x14>>2,
        Status          = 0x18>>2,
                Full            = 1<<31,
                Empty           = 1<<30,
        Config          = 0x1C>>2,
        NRegs           = 0x20>>2,

        ChanMask        = 0xF,
        ChanProps       = 8,
        ChanFb          = 1,

        Req                     = 0x0,
        RspOk           = 0x80000000,
        TagResp         = 1<<31,

        TagGetfwrev     = 0x00000001,
        TagGetmac       = 0x00010003,
        TagGetram       = 0x00010005,
        TagGetpower     = 0x00020001,
        TagSetpower     = 0x00028001,
                Powerwait       = 1<<1,
        TagGetclkspd= 0x00030002,
        TagFballoc      = 0x00040001,
        TagFbfree       = 0x00048001,
        TagFbblank      = 0x00040002,
        TagGetres       = 0x00040003,
        TagSetres       = 0x00048003,
        TagGetvres      = 0x00040004,
        TagSetvres      = 0x00048004,
        TagGetdepth     = 0x00040005,
        TagSetdepth     = 0x00048005,
        TagGetrgb       = 0x00044006,
        TagSetrgb       = 0x00048006,
};

struct Fbinfo {
        u32int  xres;
        u32int  yres;
        u32int  xresvirtual;
        u32int  yresvirtual;
        u32int  pitch;                  /* returned by gpu */
        u32int  bpp;
        u32int  xoffset;
        u32int  yoffset;
        u32int  base;                   /* returned by gpu */
        u32int  screensize;             /* returned by gpu */
};


struct Prophdr {
        u32int  len;
        u32int  req;
        u32int  tag;
        u32int  tagbuflen;
        u32int  taglen;
        u32int  data[1];
};

static void
vcwrite(uint chan, int val)
{
        u32int *r;

        r = (u32int*)MAILBOX + NRegs;
        val &= ~ChanMask;
        while(r[Status]&Full)
                ;
        coherence();
        r[Write] = val | chan;
}

static int
vcread(uint chan)
{
        u32int *r;
        int x;

        r = (u32int*)MAILBOX;
        do{
                while(r[Status]&Empty)
                        ;
                coherence();
                x = r[Read];
        }while((x&ChanMask) != chan);
        return x & ~ChanMask;
}

/*
 * Property interface
 */

static int
vcreq(int tag, void *buf, int vallen, int rsplen)
{
        uintptr r;
        int n;
        Prophdr *prop;
        static uintptr base = BUSDRAM;

        if(rsplen < vallen)
                rsplen = vallen;
        rsplen = (rsplen+3) & ~3;
        prop = (Prophdr*)(VCBUFFER);
        n = sizeof(Prophdr) + rsplen + 8;
        memset(prop, 0, n);
        prop->len = n;
        prop->req = Req;
        prop->tag = tag;
        prop->tagbuflen = rsplen;
        prop->taglen = vallen;
        if(vallen > 0)
                memmove(prop->data, buf, vallen);
        cachedwbinvse(prop, prop->len);
        for(;;){
                vcwrite(ChanProps, PADDR(prop) + base);
                r = vcread(ChanProps);
                if(r == PADDR(prop) + base)
                        break;
                if(base == 0)
                        return -1;
                base = 0;
        }
        if(prop->req == RspOk &&
           prop->tag == tag &&
           (prop->taglen&TagResp)) {
                if((n = prop->taglen & ~TagResp) < rsplen)
                        rsplen = n;
                memmove(buf, prop->data, rsplen);
        }else
                rsplen = -1;

        return rsplen;
}

/*
 * Framebuffer
 */

static int
fbdefault(int *width, int *height, int *depth)
{
        u32int buf[3];

        if(vcreq(TagGetres, &buf[0], 0, 2*4) != 2*4 ||
           vcreq(TagGetdepth, &buf[2], 0, 4) != 4)
                return -1;
        *width = buf[0];
        *height = buf[1];
        *depth = buf[2];
        return 0;
}

void*
fbinit(int set, int *width, int *height, int *depth)
{
        Fbinfo *fi;
        uintptr va;

        if(!set)
                fbdefault(width, height, depth);
        /* Screen width must be a multiple of 16 */
        *width &= ~0xF;
        fi = (Fbinfo*)(VCBUFFER);
        memset(fi, 0, sizeof(*fi));
        fi->xres = fi->xresvirtual = *width;
        fi->yres = fi->yresvirtual = *height;
        fi->bpp = *depth;
        cachedwbinvse(fi, sizeof(*fi));
        vcwrite(ChanFb, DMAADDR(fi));
        if(vcread(ChanFb) != 0)
                return 0;
        va = mmukmap(FRAMEBUFFER, PADDR(fi->base), fi->screensize);
        if(va)
                memset((char*)va, 0x7F, fi->screensize);
        return (void*)va;
}

int
fbblank(int blank)
{
        u32int buf[1];

        buf[0] = blank;
        if(vcreq(TagFbblank, buf, sizeof buf, sizeof buf) != sizeof buf)
                return -1;
        return buf[0] & 1;
}

/*
 * Power management
 */
void
setpower(int dev, int on)
{
        u32int buf[2];

        buf[0] = dev;
        buf[1] = Powerwait | (on? 1 : 0);
        vcreq(TagSetpower, buf, sizeof buf, sizeof buf);
}

int
getpower(int dev)
{
        u32int buf[2];

        buf[0] = dev;
        buf[1] = 0;
        if(vcreq(TagGetpower, buf, sizeof buf[0], sizeof buf) != sizeof buf)
                return -1;
        return buf[0] & 1;
}

/*
 * Get ethernet address (as hex string)
 *       [not reentrant]
 */
char *
getethermac(void)
{
        uchar ea[8];
        char *p;
        int i;
        static char buf[16];

        memset(ea, 0, sizeof ea);
        vcreq(TagGetmac, ea, 0, sizeof ea);
        p = buf;
        for(i = 0; i < 6; i++)
                p += sprint(p, "%.2x", ea[i]);
        return buf;
}

/*
 * Get firmware revision
 */
uint
getfirmware(void)
{
        u32int buf[1];

        if(vcreq(TagGetfwrev, buf, 0, sizeof buf) != sizeof buf)
                return 0;
        return buf[0];
}

/*
 * Get ARM ram
 */
void
getramsize(Confmem *mem)
{
        u32int buf[2];

        if(vcreq(TagGetram, buf, 0, sizeof buf) != sizeof buf)
                return;
        mem->base = buf[0];
        mem->limit = buf[1];
}

/*
 * Get clock rate
 */
ulong
getclkrate(int clkid)
{
        u32int buf[2];

        buf[0] = clkid;
        if(vcreq(TagGetclkspd, buf, sizeof(buf[0]), sizeof(buf)) != sizeof buf)
                return 0;
        return buf[1];
}