Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * 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) == -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;
                }

                /* 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);
        }
}