shithub: riscv

ref: 8ea917d119179cc4e712f29815b535e06d14599d
dir: /sys/src/9/bcm/vcore.c/

View raw version
#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,
	TagGetbrdrev	= 0x00010002,
	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];
}

uint
getrevision(void)
{
	u32int buf[1];
	if(vcreq(TagGetbrdrev, 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];
}

uint
gettemp(int tempid)
{
	u32int buf[2];
	buf[0] = tempid;
	if(vcreq(0x00030006, buf, sizeof(buf[0]), sizeof(buf)) != sizeof buf)
		return 0;

	return buf[1];
}