ref: bfc9136801651ac4921c78e5b9b3a71cf382f26d
parent: 3e66836a5bee73986b30e86805baf12abc8bb4c2
author: glenda <glenda@cirno>
date: Sun May 5 21:31:40 EDT 2024
first import of devnix.c, based on devlml.c
--- /dev/null
+++ b/sys/src/nix/pc64/devnix.c
@@ -1,0 +1,404 @@
+/*
+ * Lml 22 driver
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "../port/pci.h"
+
+#include "devlml.h"
+
+#define DBGREAD 0x01
+#define DBGWRIT 0x02
+#define DBGINTR 0x04
+#define DBGINTS 0x08
+#define DBGFS 0x10
+
+int debug = DBGREAD|DBGWRIT|DBGFS;
+
+enum{
+ Qdir,
+ Qctl0,
+ Qjpg0,
+ Qraw0,
+ Qctl1,
+ Qjpg1,
+ Qraw1,
+};
+
+static Dirtab lmldir[] = {
+ ".", {Qdir, 0, QTDIR}, 0, 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;
+ uintptr 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;
+ }
+}
+
+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;
+ CodeData *cd;
+
+ cd = lml->codedata;
+ for(i = 0; i < NBUF; i++){
+ cd->statCom[i] = PADDR(&(cd->fragdesc[i]));
+ cd->fragdesc[i].addr = PADDR(cd->frag[i].fb);
+ /* Length is in double words, in position 1..20 */
+ cd->fragdesc[i].leng = FRAGSIZE >> 1 | FRAGM_FINAL_B;
+ memmove(cd->frag[i].hdr+2, &jpgheader, sizeof(FrameHeader)-2);
+ }
+}
+
+static void
+lmlreset(void)
+{
+ uvlong regpa;
+ char name[32];
+ void *regva;
+ LML *lml;
+ Pcidev *pcidev;
+ Physseg segbuf;
+
+ pcidev = nil;
+
+ for(nlml = 0; nlml < NLML && (pcidev = pcimatch(pcidev, VENDOR_ZORAN,
+ ZORAN_36067)); nlml++){
+ 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%.8p, 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 = (uintptr)regva;
+ print(", mapped at %#p\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) == nil){
+ print("lml: physsegment: %s\n", name);
+ return;
+ }
+
+ memset(&segbuf, 0, sizeof(segbuf));
+ segbuf.attr = SG_PHYSICAL | SG_DEVICE | SG_NOEXEC;
+ sprint(name, "lml%d.regs", nlml);
+ kstrdup(&segbuf.name, name);
+ segbuf.pa = (uintptr)regpa;
+ segbuf.size = pcidev->mem[0].size;
+ if(addphysseg(&segbuf) == nil){
+ print("lml: physsegment: %s\n", name);
+ return;
+ }
+
+ /* set up interrupt handler */
+ intrenable(pcidev->intl, lmlintr, lml, pcidev->tbdf, "lml");
+ }
+}
+
+static Chan*
+lmlattach(char *spec)
+{
+ if(debug&DBGFS)
+ print("lmlattach\n");
+ return devattach(L'Λ', spec);
+}
+
+static Walkqid*
+lmlwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ if(debug&DBGFS)
+ print("lmlwalk\n");
+ return devwalk(c, nc, name, nname, lmldir, 3*nlml+1, devgen);
+}
+
+static int
+lmlstat(Chan *c, uchar *db, int n)
+{
+ if(debug&DBGFS)
+ print("lmlstat\n");
+ return devstat(c, db, n, lmldir, 3*nlml+1, devgen);
+}
+
+static Chan*
+lmlopen(Chan *c, int omode)
+{
+ int i;
+ LML *lml;
+
+ if(debug&DBGFS)
+ print("lmlopen\n");
+ if(omode != OREAD)
+ error(Eperm);
+ c->aux = 0;
+ i = 0;
+ switch((ulong)c->qid.path){
+ case Qctl1:
+ i++;
+ /* fall through */
+ case Qctl0:
+ if(i >= nlml)
+ error(Eio);
+ break;
+ case Qjpg1:
+ case Qraw1:
+ i++;
+ /* fall through */
+ 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, 3*nlml+1, devgen);
+}
+
+static void
+lmlclose(Chan *c)
+{
+ int i;
+
+ if(debug&DBGFS)
+ print("lmlclose\n");
+ i = 0;
+ switch((ulong)c->qid.path){
+ case Qjpg1:
+ case Qraw1:
+ i++;
+ /* fall through */
+ case Qjpg0:
+ case Qraw0:
+ lmls[i].jpgopens = 0;
+ break;
+ }
+}
+
+static long
+lmlread(Chan *c, void *va, long n, vlong voff)
+{
+ int i, len;
+ long off = voff;
+ uchar *buf = va;
+ LML *lml;
+ static char lmlinfo[1024];
+
+ i = 0;
+ switch((ulong)c->qid.path){
+ case Qdir:
+ n = devdirread(c, (char *)buf, n, lmldir, 3*nlml+1, devgen);
+ if(debug&(DBGFS|DBGREAD))
+ print("lmlread %ld\n", n);
+ return n;
+ case Qctl1:
+ i++;
+ /* fall through */
+ 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++;
+ /* fall through */
+ case Qjpg0:
+ if(i >= nlml)
+ error(Eio);
+ return jpgread(lmls+i, buf, n, off, 1);
+ case Qraw1:
+ i++;
+ /* fall through */
+ case Qraw0:
+ if(i >= nlml)
+ error(Eio);
+ return jpgread(lmls+i, buf, n, off, 0);
+ }
+ return -1;
+}
+
+static long
+lmlwrite(Chan *, void *, long, vlong)
+{
+ error(Eperm);
+ return 0;
+}
+
+Dev lmldevtab = {
+ L'Λ',
+ "video",
+
+ lmlreset,
+ devinit,
+ devshutdown,
+ lmlattach,
+ lmlwalk,
+ lmlstat,
+ lmlopen,
+ devcreate,
+ lmlclose,
+ lmlread,
+ devbread,
+ lmlwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
+
+static void
+lmlintr(Ureg *, void *x)
+{
+ ulong fstart, fno, flags, statcom;
+ FrameHeader *jpgheader;
+ 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 & 3;
+ for(;;){
+ lml->jpgframeno++;
+ fno = lml->jpgframeno & 3;
+ 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);
+ }
+}