04c3a6f66e
access to the axi segment hangs the machine when the fpga is not programmed yet. to prevent access, we introduce a new SG_FAULT flag, that when set on the Segment.type or Physseg.attr, causes the fault handler to immidiately return with an error (as if the segment would not be mapped). during programming, we temporarily set the SG_FAULT flag on the axi physseg, flush all processes tlb's that have the segment mapped and when programming is done, we clear the flag again.
404 lines
7.5 KiB
C
404 lines
7.5 KiB
C
/*
|
|
* 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 "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;
|
|
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;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
ulong 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 = (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) == nil){
|
|
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) == 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);
|
|
}
|
|
}
|