#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"
#define DBGREAD 0x01
#define DBGWRIT 0x02
#define DBGINTR 0x04
#define DBGINTS 0x08
int debug = 0;
// Lml 22 driver
enum{
Qdir,
Qctl0,
Qjpg0,
Qraw0,
Qctl1,
Qjpg1,
Qraw1,
};
#define QID(q) ((ulong)(q).path)
#define QIDLML(q) ((((ulong)(q).path)-1)>>1)
static Dirtab lmldir[]={
".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
"lml0ctl", {Qctl0}, 0, 0666,
"lml0jpg", {Qjpg0}, 0, 0444,
"lml0raw", {Qraw0}, 0, 0444,
"lml1ctl", {Qctl1}, 0, 0666,
"lml1jpg", {Qjpg1}, 0, 0444,
"lml1raw", {Qraw1}, 0, 0444,
};
typedef struct LML LML;
struct LML {
// Hardware
Pcidev * pcidev;
ulong pciBaseAddr;
// Allocated memory
CodeData * codedata;
// Software state
ulong jpgframeno;
int frameNo;
Rendez sleepjpg;
int jpgopens;
} lmls[NLML];
int nlml;
static FrameHeader jpgheader = {
MRK_SOI, MRK_APP3, (sizeof(FrameHeader)-4) << 8,
{ 'L', 'M', 'L', '\0'},
-1, 0, 0, 0
};
#define writel(v, a) *(ulong *)(a) = (v)
#define readl(a) *(ulong*)(a)
static int
getbuffer(void *x){
static last = NBUF-1;
int l = last;
LML *lml;
lml = x;
for (;;) {
last = (last+1) % NBUF;
if (lml->codedata->statCom[last] & STAT_BIT)
return last + 1;
if (last == l)
return 0;
}
return 0;
}
static long
jpgread(LML *lml, void *va, long nbytes, vlong, int dosleep) {
int bufno;
FrameHeader *jpgheader;
// reads should be of size 1 or sizeof(FrameHeader)
// Frameno is the number of the buffer containing the data
while ((bufno = getbuffer(lml)) == 0 && dosleep)
sleep(&lml->sleepjpg, getbuffer, lml);
if (--bufno < 0)
return 0;
jpgheader = (FrameHeader*)(lml->codedata->frag[bufno].hdr+2);
if (nbytes == sizeof(FrameHeader)) {
memmove(va, jpgheader, sizeof(FrameHeader));
return sizeof(FrameHeader);
}
if (nbytes == 1) {
*(char *)va = bufno;
return 1;
}
return 0;
}
static void lmlintr(Ureg *, void *);
static void
prepbuf(LML *lml) {
int i;
for (i = 0; i < NBUF; i++) {
lml->codedata->statCom[i] = PADDR(&(lml->codedata->fragdesc[i]));
lml->codedata->fragdesc[i].addr = PADDR(lml->codedata->frag[i].fb);
// Length is in double words, in position 1..20
lml->codedata->fragdesc[i].leng = (FRAGSIZE >> 1) | FRAGM_FINAL_B;
memmove(lml->codedata->frag[i].hdr+2, &jpgheader, sizeof(FrameHeader)-2);
}
}
static void
lmlreset(void)
{
Physseg segbuf;
ulong regpa;
ISAConf isa;
char name[32];
Pcidev *pcidev;
LML *lml;
pcidev = nil;
for (nlml = 0; nlml < NLML && (pcidev = pcimatch(pcidev, VENDOR_ZORAN, ZORAN_36067)); nlml++){
if(isaconfig("lml", nlml, &isa) == 0) {
if (debug) print("lml %d not in plan9.ini\n", nlml);
break;
}
lml = &lmls[nlml];
lml->pcidev = pcidev;
lml->codedata = (CodeData*)(((ulong)xalloc(Codedatasize+ BY2PG) + BY2PG-1) & ~(BY2PG-1));
if (lml->codedata == nil) {
print("devlml: xalloc(%ux, %ux, 0)\n", Codedatasize, BY2PG);
return;
}
print("Installing Motion JPEG driver %s, irq %d\n", MJPG_VERSION, pcidev->intl);
print("MJPG buffer at 0x%.8lux, size 0x%.8ux\n", lml->codedata, Codedatasize);
// Get access to DMA memory buffer
lml->codedata->pamjpg = PADDR(lml->codedata->statCom);
prepbuf(lml);
print("zr36067 found at 0x%.8lux", pcidev->mem[0].bar & ~0x0F);
regpa = upamalloc(pcidev->mem[0].bar & ~0x0F, pcidev->mem[0].size, 0);
if (regpa == 0) {
print("lml: failed to map registers\n");
return;
}
lml->pciBaseAddr = (ulong)KADDR(regpa);
print(", mapped at 0x%.8lux\n", lml->pciBaseAddr);
memset(&segbuf, 0, sizeof(segbuf));
segbuf.attr = SG_PHYSICAL;
sprint(name, "lml%d.mjpg", nlml);
kstrdup(&segbuf.name, name);
segbuf.pa = PADDR(lml->codedata);
segbuf.size = Codedatasize;
if (addphysseg(&segbuf) == -1) {
print("lml: physsegment: %s\n", name);
return;
}
memset(&segbuf, 0, sizeof(segbuf));
segbuf.attr = SG_PHYSICAL;
sprint(name, "lml%d.regs", nlml);
kstrdup(&segbuf.name, name);
segbuf.pa = (ulong)regpa;
segbuf.size = pcidev->mem[0].size;
if (addphysseg(&segbuf) == -1) {
print("lml: physsegment: %s\n", name);
return;
}
// Interrupt handler
intrenable(pcidev->intl, lmlintr, lml, pcidev->tbdf, "lml");
}
return;
}
static Chan*
lmlattach(char *spec)
{
return devattach('V', spec);
}
static Walkqid*
lmlwalk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, lmldir, nelem(lmldir), devgen);
}
static int
lmlstat(Chan *c, uchar *db, int n)
{
return devstat(c, db, n, lmldir, nelem(lmldir), devgen);
}
static Chan*
lmlopen(Chan *c, int omode) {
int i;
LML *lml;
if (omode != OREAD)
error(Eperm);
c->aux = 0;
i = 0;
switch((ulong)c->qid.path){
case Qctl1:
i++;
case Qctl0:
if (i >= nlml)
error(Eio);
break;
case Qjpg1:
case Qraw1:
i++;
case Qjpg0:
case Qraw0:
// allow one open
if (i >= nlml)
error(Eio);
lml = lmls+i;
if (lml->jpgopens)
error(Einuse);
lml->jpgopens = 1;
lml->jpgframeno = 0;
prepbuf(lml);
break;
}
return devopen(c, omode, lmldir, nelem(lmldir), devgen);
}
static void
lmlclose(Chan *c) {
int i;
i = 0;
switch((ulong)c->qid.path){
case Qjpg1:
case Qraw1:
i++;
case Qjpg0:
case Qraw0:
lmls[i].jpgopens = 0;
break;
}
}
static long
lmlread(Chan *c, void *va, long n, vlong voff) {
int i;
uchar *buf = va;
long off = voff;
LML *lml;
static char lmlinfo[1024];
int len;
i = 0;
switch((ulong)c->qid.path){
case Qdir:
return devdirread(c, (char *)buf, n, lmldir, nelem(lmldir), devgen);
case Qctl1:
i++;
case Qctl0:
if (i >= nlml)
error(Eio);
lml = lmls+i;
len = snprint(lmlinfo, sizeof lmlinfo, "lml%djpg lml%draw\nlml%d.regs 0x%lux 0x%ux\nlml%d.mjpg 0x%lux 0x%ux\n",
i, i,
i, lml->pcidev->mem[0].bar & ~0x0F, lml->pcidev->mem[0].size,
i, PADDR(lml->codedata), Codedatasize);
if (voff > len)
return 0;
if (n > len - voff)
n = len - voff;
memmove(va, lmlinfo+voff, n);
return n;
case Qjpg1:
i++;
case Qjpg0:
if (i >= nlml)
error(Eio);
return jpgread(lmls+i, buf, n, off, 1);
case Qraw1:
i++;
case Qraw0:
if (i >= nlml)
error(Eio);
return jpgread(lmls+i, buf, n, off, 0);
}
}
static long
lmlwrite(Chan *, void *, long, vlong) {
error(Eperm);
return 0;
}
Dev lmldevtab = {
'V',
"video",
lmlreset,
devinit,
lmlattach,
lmlwalk,
lmlstat,
lmlopen,
devcreate,
lmlclose,
lmlread,
devbread,
lmlwrite,
devbwrite,
devremove,
devwstat,
};
static void
lmlintr(Ureg *, void *x) {
FrameHeader *jpgheader;
ulong fstart, fno, flags, statcom;
LML *lml;
lml = x;
flags = readl(lml->pciBaseAddr+INTR_STAT);
// Reset all interrupts from 067
writel(0xff000000, lml->pciBaseAddr + INTR_STAT);
if(flags & INTR_JPEGREP) {
if(debug&(DBGINTR))
print("MjpgDrv_intrHandler stat=0x%.8lux\n", flags);
fstart = lml->jpgframeno & 0x00000003;
for (;;) {
lml->jpgframeno++;
fno = lml->jpgframeno & 0x00000003;
if (lml->codedata->statCom[fno] & STAT_BIT)
break;
if (fno == fstart) {
if (debug & DBGINTR)
print("Spurious lml jpg intr?\n");
return;
}
}
statcom = lml->codedata->statCom[fno];
jpgheader = (FrameHeader *)(lml->codedata->frag[fno].hdr+2);
jpgheader->frameNo = lml->jpgframeno;
jpgheader->ftime = todget(nil);
jpgheader->frameSize = (statcom & 0x00ffffff) >> 1;
jpgheader->frameSeqNo = statcom >> 24;
wakeup(&lml->sleepjpg);
}
return;
}