#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#include "devlml.h"
static void * pciPhysBaseAddr;
static ulong pciBaseAddr;
static Pcidev * pcidev;
#define DBGREAD 0x01
#define DBGWRIT 0x02
#define DBGINTR 0x04
#define DBGINTS 0x08
int debug = 0;
// Lml 22 driver
enum{
Qdir,
Qjvideo,
Qjframe,
Qjcount,
};
static Dirtab lmldir[]={
// name, qid, size, mode
"jvideo", {Qjvideo}, 0, 0666,
"jframe", {Qjframe}, 0, 0666,
"jcount", {Qjcount}, 0, 0444,
};
CodeData * codeData;
typedef enum {
New,
Header,
Body,
Error,
} State;
int frameNo;
Rendez sleeper;
int singleFrame;
int nopens;
uchar q856[3];
State state = New;
static FrameHeader frameHeader = {
MRK_SOI, MRK_APP3, (sizeof(FrameHeader)-4) << 8,
{ 'L', 'M', 'L', '\0'},
-1, 0, 0, 0, 0
};
#define writel(v, a) *(ulong *)(a) = (v)
#define readl(a) *(ulong*)(a)
static int
getbuffer(void){
static last = NBUF-1;
int l = last;
for (;;) {
last = (last+1) % NBUF;
if (codeData->statCom[last] & STAT_BIT)
return last;
if (last == l)
sleep(&sleeper, return0, 0);
}
return 0;
}
static long
vcount(Chan *, void *va, long nbytes, vlong) {
char *p = (char *)va;
// reads always return one byte: the next available buffer number
if (nbytes <= 0) return 0;
*p = getbuffer();
return 1;
}
static long
vread(Chan *, void *va, long nbytes, vlong) {
static int bufpos;
static char *bufptr;
static int curbuf;
static int fragsize;
static int frameno;
static int frameprev;
char *p;
long count = nbytes;
int i;
vlong thetime;
p = (char *)va;
while (count > 0) {
switch (state) {
case New:
curbuf = getbuffer();
frameNo = codeData->statCom[curbuf] >> 24;
fragsize = (codeData->statCom[curbuf] & 0x00ffffff)>>1;
if (debug & DBGREAD)
pprint("devlml: got read buf %d, fr %d, size %d\n",
curbuf, frameNo, fragsize);
if (!singleFrame && frameno != (frameprev + 1) % 256)
pprint("Frame out of sequence: %d %d\n",
frameprev, frameno);
frameprev = frameno;
if (fragsize <= 0 || fragsize > sizeof(Fragment)) {
pprint("Wrong sized fragment, %d (ignored)\n",
fragsize);
codeData->statCom[curbuf] = PADDR(&(codeData->fragdesc[curbuf]));
break;
}
// Fill in APP marker fields here
thetime = todget();
frameHeader.sec = (ulong)(thetime / 1000000000LL);
frameHeader.usec = (ulong)(thetime % 1000000000LL) / 1000;
frameHeader.frameSize = fragsize - 2 + sizeof(FrameHeader);
frameHeader.frameSeqNo++;
frameHeader.frameNo = frameno;
bufpos = 0;
state = Header;
bufptr = (char *)(&frameHeader);
// Fall through
case Header:
i = sizeof(FrameHeader) - bufpos;
if (count <= i) {
memmove(p, bufptr, count);
bufptr += count;
bufpos += count;
return nbytes;
}
memmove(p, bufptr, i);
count -= i;
p += i;
bufpos = 2;
bufptr = codeData->frag[curbuf].fb + 2;
state = Body;
// Fall through
case Body:
i = fragsize - bufpos;
if (count < i) {
memmove(p, bufptr, count);
bufptr += count;
bufpos += count;
return nbytes;
}
memmove(p, bufptr, i);
count -= i;
p += i;
// Allow reuse of current buffer
codeData->statCom[curbuf] = PADDR(&(codeData->fragdesc[curbuf]));
state = New;
if (singleFrame) {
state = Error;
return nbytes - count;
}
break;
case Error:
return 0;
}
}
}
static long
vwrite(Chan *, void *va, long nbytes, vlong) {
static int bufpos;
static char *bufptr;
static int curbuf;
static int fragsize;
char *p;
long count = nbytes;
int i;
p = (char *)va;
while (count > 0) {
switch (state) {
case New:
curbuf = getbuffer();
if (debug&DBGWRIT)
pprint("current buffer %d\n", curbuf);
bufptr = codeData->frag[curbuf].fb;
bufpos = 0;
state = Header;
// Fall through
case Header:
if (count < sizeof(FrameHeader) - bufpos) {
memmove(bufptr, p, count);
bufptr += count;
bufpos += count;
return nbytes;
}
// Fill remainder of header
i = sizeof(FrameHeader) - bufpos;
memmove(bufptr, p, i);
bufptr += i;
bufpos += i;
p += i;
count -= i;
// verify header
if (codeData->frag[curbuf].fh.mrkSOI != MRK_SOI
|| codeData->frag[curbuf].fh.mrkAPP3 != MRK_APP3
|| strcmp(codeData->frag[curbuf].fh.nm, APP_NAME)) {
// Header is bad
pprint("devlml: header error: 0x%.4ux, 0x%.4ux, `%.4s'\n",
codeData->frag[curbuf].fh.mrkSOI,
codeData->frag[curbuf].fh.mrkAPP3,
codeData->frag[curbuf].fh.nm);
state = Error;
return nbytes - count;
}
fragsize = codeData->frag[curbuf].fh.frameSize;
if (fragsize <= sizeof(FrameHeader)
|| fragsize > sizeof(Fragment)) {
pprint("devlml: frame size error: 0x%.8ux\n",
fragsize);
state = Error;
return nbytes - count;
}
state = Body;
// Fall through
case Body:
i = fragsize - bufpos;
if (count < i) {
memmove(bufptr, p, count);
bufptr += count;
bufpos += count;
return nbytes;
}
memmove(bufptr, p, i);
bufptr += i;
bufpos += i;
p += i;
count -= i;
// We have written the frame, time to display it
codeData->statCom[curbuf] = PADDR(&(codeData->fragdesc[curbuf]));
if (debug&DBGWRIT)
pprint("Sending buffer %d\n", curbuf);
if (singleFrame) {
state = Error;
return nbytes - count;
}
state = New;
break;
case Error:
return 0;
}
}
}
static void lmlintr(Ureg *, void *);
static void
lmlreset(void)
{
Physseg segbuf;
Physseg segreg;
Physseg seggrab;
ulong regpa;
ulong cdsize;
ulong *grabpa;
ulong grablen;
int i;
pcidev = pcimatch(nil, PCI_VENDOR_ZORAN, PCI_DEVICE_ZORAN_36067);
if (pcidev == nil) {
return;
}
cdsize = (sizeof(CodeData) + BY2PG - 1) & ~(BY2PG - 1);
codeData = (CodeData*)xspanalloc(cdsize, BY2PG, 0);
if (codeData == nil) {
print("devlml: xspanalloc(%lux, %ux, 0)\n", cdsize, BY2PG);
return;
}
grablen = (720 * 480 * 2 * 2 + BY2PG - 1) & ~(BY2PG - 1);
grabpa = (ulong *)xspanalloc(grablen, BY2PG, 0);
if (grabpa == nil) {
print("devlml: xspanalloc(%lux, %ux, 0)\n", grablen, BY2PG);
return;
}
*grabpa = PADDR(grabpa);
print("Installing Motion JPEG driver %s\n", MJPG_VERSION);
print("MJPG buffer at 0x%.8lux, size 0x%.8lux\n", codeData, cdsize);
print("Grab buffer at 0x%.8lux, size 0x%.8lux\n", grabpa, grablen);
// Get access to DMA memory buffer
memset(codeData, 0xAA, sizeof(CodeData));
codeData->physaddr = PADDR(codeData->statCom);
for (i = 0; i < NBUF; i++) {
codeData->statCom[i] = PADDR(&(codeData->fragdesc[i]));
codeData->fragdesc[i].addr = PADDR(&(codeData->frag[i]));
// Length is in double words, in position 1..20
codeData->fragdesc[i].leng = ((sizeof codeData->frag[i]) >> 1) | FRAGM_FINAL_B;
}
print("initializing LML33 board...");
pciPhysBaseAddr = (void *)(pcidev->mem[0].bar & ~0x0F);
print("zr36067 found at 0x%.8lux", pciPhysBaseAddr);
regpa = upamalloc(pcidev->mem[0].bar & ~0x0F, pcidev->mem[0].size, 0);
if (regpa == 0) {
print("lml: failed to map registers\n");
return;
}
pciBaseAddr = (ulong)KADDR(regpa);
print(", mapped at 0x%.8lux\n", pciBaseAddr);
// Interrupt handler
intrenable(pcidev->intl, lmlintr, nil, pcidev->tbdf);
memset(&segbuf, 0, sizeof(segbuf));
segbuf.attr = SG_PHYSICAL;
segbuf.name = smalloc(NAMELEN);
snprint(segbuf.name, NAMELEN, "lmlmjpg");
segbuf.pa = PADDR(codeData);
segbuf.size = cdsize;
if (addphysseg(&segbuf) == -1) {
print("lml: physsegment: lmlmjpg\n");
return;
}
memset(&segreg, 0, sizeof(segreg));
segreg.attr = SG_PHYSICAL;
segreg.name = smalloc(NAMELEN);
snprint(segreg.name, NAMELEN, "lmlregs");
segreg.pa = (ulong)regpa;
segreg.size = pcidev->mem[0].size;
if (addphysseg(&segreg) == -1) {
print("lml: physsegment: lmlregs\n");
return;
}
memset(&seggrab, 0, sizeof(seggrab));
seggrab.attr = SG_PHYSICAL;
seggrab.name = smalloc(NAMELEN);
snprint(seggrab.name, NAMELEN, "lmlgrab");
seggrab.pa = (ulong)PADDR(grabpa);
seggrab.size = grablen;
if (addphysseg(&seggrab) == -1) {
print("lml: physsegment: lmlgrab\n");
return;
}
return;
}
static Chan*
lmlattach(char *spec)
{
return devattach('V', spec);
}
static int
lmlwalk(Chan *c, char *name)
{
return devwalk(c, name, lmldir, nelem(lmldir), devgen);
}
static void
lmlstat(Chan *c, char *dp)
{
devstat(c, dp, lmldir, nelem(lmldir), devgen);
}
static Chan*
lmlopen(Chan *c, int omode) {
int i;
c->aux = 0;
switch(c->qid.path){
case Qjframe:
case Qjvideo:
case Qjcount:
if (nopens)
error(Einuse);
nopens = 1;
singleFrame = (c->qid.path == Qjframe) ? 1 : 0;;
state = New;
for (i = 0; i < NBUF; i++)
codeData->statCom[i] = PADDR(&(codeData->fragdesc[i]));
// allow one open total for these two
break;
}
return devopen(c, omode, lmldir, nelem(lmldir), devgen);
}
static void
lmlclose(Chan *c) {
switch(c->qid.path){
case Qjvideo:
case Qjframe:
case Qjcount:
nopens = 0;
authclose(c);
}
}
static long
lmlread(Chan *c, void *va, long n, vlong voff) {
uchar *buf = va;
long off = voff;
switch(c->qid.path & ~CHDIR){
case Qdir:
return devdirread(c, (char *)buf, n, lmldir, nelem(lmldir), devgen);
case Qjvideo:
case Qjframe:
return vread(c, buf, n, off);
case Qjcount:
return vcount(c, buf, n, off);
}
}
static long
lmlwrite(Chan *c, void *va, long n, vlong voff) {
uchar *buf = va;
long off = voff;
switch(c->qid.path & ~CHDIR){
case Qdir:
case Qjcount:
error(Eperm);
case Qjvideo:
case Qjframe:
return vwrite(c, buf, n, off);
}
}
Dev lmldevtab = {
'V',
"video",
lmlreset,
devinit,
lmlattach,
devclone,
lmlwalk,
lmlstat,
lmlopen,
devcreate,
lmlclose,
lmlread,
devbread,
lmlwrite,
devbwrite,
devremove,
devwstat,
};
static void
lmlintr(Ureg *, void *) {
static count;
ulong flags = readl(pciBaseAddr+INTR_STAT);
if(debug&(DBGINTR))
print("MjpgDrv_intrHandler stat=0x%.8lux\n", flags);
// Reset all interrupts from 067
writel(0xff000000, pciBaseAddr + INTR_STAT);
if(flags & INTR_JPEGREP) {
if ((debug&DBGINTR) || ((debug&DBGINTS) && (count++ % 128) == 0))
print("MjpgDrv_intrHandler wakeup\n");
wakeup(&sleeper);
}
return;
}