#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; void *regva; 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 = pcidev->mem[0].bar & ~0x0F; regva = vmap(regpa, pcidev->mem[0].size); if (regva == 0) { print("lml: failed to map registers\n"); return; } lml->pciBaseAddr = (ulong)regva; 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, devshutdown, 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; }