| plan 9 kernel history: overview | file list | diff list |
1999/0430/pc/devlml.c (diff list | history)
| pc/devlml.c on 1999/0422 | ||
| 1999/0422 | #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" // Lml 22 driver enum{ Q819, Q856, | |
| 1999/0428 | Qreg, | |
| 1999/0422 | Qvideo, Qjframe, }; static Dirtab viddir[]={ // name, qid, size, mode "vid819", {Q819}, 0, 0644, "vid856", {Q856}, 0, 0644, | |
| 1999/0428 | "vidreg", {Qreg}, 0, 0644, | |
| 1999/0422 | "video", {Qvideo}, 0, 0666, "jframe", {Qjframe}, 0, 0666, }; CodeData * codeData; | |
| 1999/0424 | int currentBuffer; int currentBufferLength; void * currentBufferPtr; int frameNo; Rendez sleeper; int singleFrame; int bufferPrepared; int hdrPos; int nopens; | |
| 1999/0429 | uchar q856[3]; | |
| 1999/0424 | static FrameHeader frameHeader = { MRK_SOI, MRK_APP3, (sizeof(FrameHeader)-4) << 8, { 'L', 'M', 'L', '\0'}, -1, 0, 0, 0, 0 }; | |
| 1999/0423 | static void | |
| 1999/0429 | i2c_pause(void) { | |
| 1999/0422 | ||
| 1999/0423 | microdelay(I2C_DELAY); } | |
| 1999/0422 | static void | |
| 1999/0429 | i2c_waitscl(void) { | |
| 1999/0423 | int i; ulong a; for(i=0;;i++) { a = readl(pciBaseAddr + ZR36057_I2C_BUS); if (a & ZR36057_I2C_SCL) break; if (i>I2C_TIMEOUT) error(Eio); } } static void | |
| 1999/0429 | i2c_start(void) { | |
| 1999/0423 | writel(ZR36057_I2C_SCL|ZR36057_I2C_SDA, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_waitscl(); i2c_pause(); | |
| 1999/0423 | writel(ZR36057_I2C_SCL, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_pause(); | |
| 1999/0423 | writel(0, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_pause(); | |
| 1999/0423 | } static void | |
| 1999/0429 | i2c_stop(void) { | |
| 1999/0423 | // the clock should already be low, make sure data is writel(0, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_pause(); | |
| 1999/0423 | // set clock high and wait for device to catch up writel(ZR36057_I2C_SCL, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_waitscl(); i2c_pause(); | |
| 1999/0423 | // set the data high to indicate the stop bit writel(ZR36057_I2C_SCL|ZR36057_I2C_SDA, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_pause(); | |
| 1999/0423 | } | |
| 1999/0429 | static void i2c_wrbit(int bit) { | |
| 1999/0423 | if (bit){ writel(ZR36057_I2C_SDA, pciBaseAddr + ZR36057_I2C_BUS); // set data | |
| 1999/0429 | i2c_pause(); | |
| 1999/0423 | writel(ZR36057_I2C_SDA|ZR36057_I2C_SCL, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_waitscl(); i2c_pause(); | |
| 1999/0423 | writel(ZR36057_I2C_SDA, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_pause(); | |
| 1999/0423 | } else { writel(0, pciBaseAddr + ZR36057_I2C_BUS); // clr data | |
| 1999/0429 | i2c_pause(); | |
| 1999/0423 | writel(ZR36057_I2C_SCL, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_waitscl(); i2c_pause(); | |
| 1999/0423 | writel(0, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_pause(); | |
| 1999/0423 | } } static int | |
| 1999/0429 | i2c_rdbit(void) { | |
| 1999/0423 | int bit; // the clk line should be low // ensure we are not asserting the data line writel(ZR36057_I2C_SDA, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_pause(); | |
| 1999/0423 | // set the clock high and wait for device to catch up writel(ZR36057_I2C_SDA|ZR36057_I2C_SCL, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_waitscl(); i2c_pause(); | |
| 1999/0423 | // the data line should be a valid bit bit = readl(pciBaseAddr+ZR36057_I2C_BUS); if (bit & ZR36057_I2C_SDA){ bit = 1; } else { bit = 0; } // set the clock low to indicate end of cycle writel(ZR36057_I2C_SDA, pciBaseAddr + ZR36057_I2C_BUS); | |
| 1999/0429 | i2c_pause(); | |
| 1999/0423 | return bit; } static int | |
| 1999/0429 | i2c_rdbyte(uchar *v) { | |
| 1999/0423 | int i, ack; uchar res; res = 0; for (i=0;i<8;i++){ res = res << 1; | |
| 1999/0429 | res += i2c_rdbit(); | |
| 1999/0423 | } | |
| 1999/0429 | ack = i2c_rdbit(); | |
| 1999/0423 | *v = res; return ack; } static int | |
| 1999/0429 | i2c_wrbyte(uchar v) { | |
| 1999/0423 | int i, ack; for (i=0;i<8;i++){ | |
| 1999/0429 | i2c_wrbit(v & 0x80); | |
| 1999/0423 | v = v << 1; } | |
| 1999/0429 | ack = i2c_rdbit(); | |
| 1999/0423 | return ack; } static void | |
| 1999/0429 | i2c_write_bytes(uchar addr, uchar sub, uchar *bytes, long num) { | |
| 1999/0423 | int ack; long i; | |
| 1999/0429 | i2c_start(); | |
| 1999/0423 | ||
| 1999/0429 | ack = i2c_wrbyte(addr); | |
| 1999/0423 | if (ack == 1) error(Eio); | |
| 1999/0429 | ack = i2c_wrbyte(sub); | |
| 1999/0423 | if (ack == 1) error(Eio); for(i=0;i<num;i+=1){ | |
| 1999/0429 | ack = i2c_wrbyte(bytes[i]); | |
| 1999/0423 | if (ack == 1) error(Eio); } | |
| 1999/0429 | i2c_stop(); | |
| 1999/0423 | } | |
| 1999/0424 | static int | |
| 1999/0429 | i2c_bt856rd8(void) { uchar ret; i2c_start (); if (i2c_wrbyte(BT856Addr + 1) == 1 || i2c_rdbyte(&ret) == 0) { i2c_stop (); return -1; } i2c_stop (); return ret; } static int i2c_rd8(int addr, int sub) | |
| 1999/0423 | { uchar msb; | |
| 1999/0429 | i2c_start(); | |
| 1999/0423 | ||
| 1999/0429 | if (i2c_wrbyte(addr) == 1 || i2c_wrbyte(sub) == 1) { i2c_stop(); | |
| 1999/0424 | return -1; | |
| 1999/0423 | } | |
| 1999/0429 | i2c_start(); | |
| 1999/0423 | ||
| 1999/0429 | if (i2c_wrbyte(addr+1) == 1 || i2c_rdbyte(&msb) == 0){ i2c_stop(); | |
| 1999/0424 | return -1; | |
| 1999/0423 | } | |
| 1999/0429 | i2c_stop(); | |
| 1999/0423 | return msb; } | |
| 1999/0424 | static int | |
| 1999/0429 | i2c_wr8(uchar addr, uchar sub, uchar msb) { | |
| 1999/0424 | ||
| 1999/0429 | i2c_start(); | |
| 1999/0424 | ||
| 1999/0429 | if (i2c_wrbyte(addr) == 1 || i2c_wrbyte(sub) == 1 || i2c_wrbyte(msb) == 1) | |
| 1999/0424 | return 0; | |
| 1999/0429 | i2c_stop(); | |
| 1999/0424 | return 1; } | |
| 1999/0423 | static int | |
| 1999/0424 | prepareBuffer(CodeData * this, int bufferNo) { if(bufferNo >= 0 && bufferNo < NBUF && (this->statCom[bufferNo] & STAT_BIT)) { this->statCom[bufferNo] = this->statComInitial[bufferNo]; return this->fragmDescr[bufferNo].fragmLength; } else return -1; } | |
| 1999/0423 | ||
| 1999/0424 | static int getProcessedBuffer(CodeData* this){ static lastBuffer=NBUF-1; int lastBuffer0 = lastBuffer; while (1) { lastBuffer = (lastBuffer+1) % NBUF; if(this->statCom[lastBuffer]&STAT_BIT) return lastBuffer; if(lastBuffer==lastBuffer0) break; | |
| 1999/0423 | } | |
| 1999/0424 | return -1; | |
| 1999/0423 | } | |
| 1999/0424 | static int getBuffer(CodeData *this, int bufferNo, void** bufferPtr, int* frameNo) { int codeLength; if(this->statCom[bufferNo] & STAT_BIT) { *bufferPtr = (void*)this->fragmDescr[bufferNo].fragmAddress; *frameNo = this->statCom[bufferNo] >> 24; codeLength=((this->statCom[bufferNo] & 0x00FFFFFF) >> 1); return codeLength; } else return -1; } | |
| 1999/0423 | ||
| 1999/0424 | static long vread(Chan *, void *va, long count, vlong pos) { int prevFrame; // how much bytes left to transfer for the header int hdrLeft; // Count of bytes that we need to copy into buf from code-buffer // (different from count only while in header reading mode) int cpcount = count; // Count of bytes that we copied into buf altogether and will return int retcount=0; vlong thetime; uchar *buf = va; //print("devlml::vread() count=%ld pos=%lld\n", count, pos); // If we just begin reading a file, pos would never be 0 otherwise if (pos == 0 && hdrPos == -1) { currentBuffer = -1; currentBufferLength = 0; frameNo = -1; | |
| 1999/0423 | } | |
| 1999/0424 | prevFrame = frameNo; // We get to the end of the current buffer, also covers just // open file, since 0 >= -1 if(hdrPos == -1 && pos >= currentBufferLength) { prepareBuffer(codeData, currentBuffer); // if not the first buffer read and single frame mode - return EOF if (currentBuffer != -1 && singleFrame) return 0; while((currentBuffer = getProcessedBuffer(codeData)) == -1) sleep(&sleeper, return0, 0); currentBufferLength = getBuffer(codeData, currentBuffer, ¤tBufferPtr, &frameNo); pos = 0; // ?????????????? // print("getBufffer %d -> %d 0x%x %d\n",currentBuffer, currentBufferLength, currentBufferPtr, frameNo); if(frameNo != (prevFrame + 1) % 256) print("Frames out of sequence: %d %d\n", prevFrame, frameNo); // Fill in APP marker fields here thetime = todget(); frameHeader.sec = (ulong)(thetime / 1000000000LL); frameHeader.usec = (ulong)(thetime % 1000000000LL) / 1000; frameHeader.frameSize = currentBufferLength - 2 + sizeof(FrameHeader); frameHeader.frameSeqNo++; frameHeader.frameNo = frameNo; hdrPos=0; } if (hdrPos != -1) { hdrLeft = sizeof(FrameHeader) - hdrPos; // Write the frame size here if (count >= hdrLeft) { memmove(buf, (char*)&frameHeader + hdrPos, hdrLeft); retcount += hdrLeft; cpcount = count - hdrLeft; pos = sizeof(frameHeader.mrkSOI); hdrPos = -1; } else { memmove(buf, (char*)&frameHeader + hdrPos, count); hdrPos += count; return count; } } if(cpcount + pos > currentBufferLength) cpcount = currentBufferLength - pos; memmove(buf + retcount, (char *)currentBufferPtr + pos, cpcount); retcount += cpcount; //pr_debug("return %d %d\n",cpcount,retcount); return retcount; | |
| 1999/0423 | } | |
| 1999/0424 | static long vwrite(Chan *, void *va, long count, vlong pos) { // how much bytes left to transfer for the header int hdrLeft; char *buf = va; //print("devlml::vwrite() count=0x%x pos=0x%x\n", count, pos); // We just started writing, not into the header copy if(pos==0 && hdrPos == -1) { currentBuffer=-1; currentBufferLength=0; frameNo=-1; bufferPrepared = 0; } // We need next buffer to fill (either because we're done with the // current buffer) of because we're just beginning (but not into the header) if (hdrPos == -1 && pos >= currentBufferLength) { while((currentBuffer = getProcessedBuffer(codeData)) == -1) sleep(&sleeper, return0, 0); // print("current buffer %d\n",currentBuffer); getBuffer(codeData, currentBuffer, ¤tBufferPtr, &frameNo); // We need to receive the header now hdrPos = 0; } // We're into the header processing if (hdrPos != -1) { // Calculate how many bytes we need to receive to fill the header hdrLeft = sizeof(FrameHeader) - hdrPos; // If we complete or go over the header with this count if (count >= hdrLeft) { // Adjust count of bytes that remain to be copied into video buffer count = count - hdrLeft; memmove((char*)&frameHeader + hdrPos, buf, hdrLeft); // Make sure we have a standard LML33 header if (frameHeader.mrkSOI == MRK_SOI | |
| 1999/0430 | && frameHeader.mrkAPP3 == MRK_APP3 && strcmp(frameHeader.nm, APP_NAME) == 0) { | |
| 1999/0424 | //print("Starting new buffer len=0x%x frame=%d\n", frameHeader.frameSize, frameHeader.frameSeqNo); // Obtain values we need for playback process from the header currentBufferLength = frameHeader.frameSize; } else if (singleFrame) { currentBufferLength = FRAGSIZE; } else { // We MUST have header for motion video decompression print("No frame size (APP3 marker) in MJPEG file\n"); error(Eio); } // Finish header processing hdrPos = -1; // Copy the header into the playback buffer memmove(currentBufferPtr, (char*)&frameHeader, sizeof(FrameHeader)); // And set position just behind header for playback buffer write pos = sizeof(FrameHeader); } else { memmove((char*)&frameHeader + hdrPos, buf, count); hdrPos += count; return count; } } else hdrLeft = 0; if(count + pos > currentBufferLength) { count = currentBufferLength - pos; } memmove((char *)currentBufferPtr + pos, buf + hdrLeft, count); pos += count; // print("return 0x%x 0x%x\n",pos,count); // Now is the right moment to initiate playback of the frame (if it's full) if(pos >= currentBufferLength) { // We have written the frame, time to display it //print("Passing written buffer to 067\n"); prepareBuffer(codeData, currentBuffer); bufferPrepared = 1; } //print("return 0x%lx 0x%x 0x%x 0x%x\n",pos,count,hdrLeft+count,currentBufferLength); return hdrLeft + count; } | |
| 1999/0423 | static void lmlintr(Ureg *, void *); static void | |
| 1999/0422 | vidreset(void) { ulong regpa; int i; | |
| 1999/0423 | pcidev = pcimatch(nil, PCI_VENDOR_ZORAN, PCI_DEVICE_ZORAN_36067); if (pcidev == nil) { print("No zr36067 found.\n"); return; } | |
| 1999/0422 | codeData = (CodeData*)xspanalloc(sizeof(CodeData), BY2PG, 0); if (codeData == nil) { print("devlml: xspanalloc(%ux, %ux, 0)\n", sizeof(CodeData), BY2PG); return; } print("Installing Motion JPEG driver %s\n", MJPG_VERSION); print("Buffer size %ux\n", sizeof(CodeData)); // Get access to DMA memory buffer memset(codeData, 0xAA, sizeof(CodeData)); strncpy(codeData->idString, MJPG_VERSION, strlen(MJPG_VERSION)); for(i = 0; i < NBUF; i++) { codeData->statCom[i] = PADDR(&(codeData->fragmDescr[i])); codeData->statComInitial[i] = codeData->statCom[i]; codeData->fragmDescr[i].fragmAddress = | |
| 1999/0423 | (Fragment *)PADDR(&(codeData->frag[i])); | |
| 1999/0422 | // Length is in double words, in position 1..20 codeData->fragmDescr[i].fragmLength = (FRAGSIZE >> 1) | FRAGM_FINAL_B; } print("initializing LML33 board..."); | |
| 1999/0423 | pciPhysBaseAddr = (void *)(pcidev->mem[0].bar & ~0x0F); | |
| 1999/0422 | ||
| 1999/0423 | print("zr36067 found at %lux\n", pciPhysBaseAddr); | |
| 1999/0422 | ||
| 1999/0423 | regpa = upamalloc(pcidev->mem[0].bar & ~0x0F, pcidev->mem[0].size, 0); | |
| 1999/0422 | if (regpa == 0) { print("lml: failed to map registers\n"); return; } | |
| 1999/0423 | pciBaseAddr = (ulong)KADDR(regpa); | |
| 1999/0422 | // make sure the device will respond to mem accesses // (pcicmd_master | pcicmd_memory) -- probably superfluous | |
| 1999/0423 | // pcicfgw32(pcidev, PciPCR, 0x04 | 0x02); | |
| 1999/0422 | // set bus latency -- probably superfluous | |
| 1999/0423 | // pcicfgw8(pcidev, PciLTR, 64); | |
| 1999/0422 | // Interrupt handler | |
| 1999/0423 | intrenable(pcidev->intl, lmlintr, nil, pcidev->tbdf); | |
| 1999/0422 | print("LML33 Installed\n"); return; } static Chan* vidattach(char *spec) { return devattach('V', spec); } static int vidwalk(Chan *c, char *name) { return devwalk(c, name, viddir, nelem(viddir), devgen); } static void vidstat(Chan *c, char *dp) { devstat(c, dp, viddir, nelem(viddir), devgen); } static Chan* vidopen(Chan *c, int omode) { c->aux = 0; switch(c->qid.path){ case Q819: case Q856: | |
| 1999/0428 | case Qreg: | |
| 1999/0422 | break; case Qvideo: case Qjframe: | |
| 1999/0424 | if (nopens) error(Einuse); nopens = 1; singleFrame = (c->qid.path == Qjframe) ? 1 : 0;; currentBuffer = 0; currentBufferLength = 0; currentBufferPtr = 0; frameNo = 0; bufferPrepared = 0; hdrPos = -1; | |
| 1999/0422 | // allow one open total for these two break; } return devopen(c, omode, viddir, nelem(viddir), devgen); } static void vidclose(Chan *c) { switch(c->qid.path){ case Q819: case Q856: | |
| 1999/0428 | case Qreg: | |
| 1999/0422 | case Qvideo: case Qjframe: authclose(c); } } static long | |
| 1999/0424 | vidread(Chan *c, void *va, long n, vlong off) { int i, d; uchar *buf = va; | |
| 1999/0423 | ||
| 1999/0422 | switch(c->qid.path){ case Q819: | |
| 1999/0424 | if (off < 0 || off + n > 0x20) | |
| 1999/0423 | return 0; | |
| 1999/0424 | for (i = 0; i < n; i++) { | |
| 1999/0429 | if ((d = i2c_rd8(BT819Addr, off++)) < 0) break; | |
| 1999/0424 | *buf++ = d; } return n - i; | |
| 1999/0422 | case Q856: | |
| 1999/0429 | if (n != 1) | |
| 1999/0423 | return 0; | |
| 1999/0429 | switch ((int)off) { case 0: if ((d = i2c_bt856rd8()) < 0) return 0; *buf = d; break; case 0xDA: *buf = q856[0]; break; case 0xDC: *buf = q856[1]; break; case 0xDE: *buf = q856[2]; break; default: return 0; | |
| 1999/0424 | } | |
| 1999/0429 | return 1; | |
| 1999/0428 | case Qreg: | |
| 1999/0430 | if (off < 0 || off + n > 0x400) | |
| 1999/0424 | return 0; | |
| 1999/0428 | switch(n) { case 1: *buf = readb(pciBaseAddr + off); break; case 2: | |
| 1999/0430 | if (off & (n-1)) return 0; | |
| 1999/0428 | *(short *)buf = readw(pciBaseAddr + off); break; case 4: | |
| 1999/0430 | if (off & (n-1)) return 0; | |
| 1999/0424 | *(long *)buf = readl(pciBaseAddr + off); | |
| 1999/0428 | break; default: return 0; | |
| 1999/0424 | } | |
| 1999/0428 | return n; | |
| 1999/0422 | case Qvideo: case Qjframe: | |
| 1999/0424 | return vread(c, buf, n, off); | |
| 1999/0422 | } } static long | |
| 1999/0424 | vidwrite(Chan *c, void *va, long n, vlong off) { int i; uchar *buf = va; | |
| 1999/0423 | switch(c->qid.path){ case Q819: | |
| 1999/0424 | if (off < 0 || off + n > 0x20) | |
| 1999/0423 | return 0; | |
| 1999/0424 | for (i = n; i > 0; i--) | |
| 1999/0429 | if (i2c_wr8(BT819Addr, off++, *buf++) == 0) | |
| 1999/0424 | break; return n - i; | |
| 1999/0423 | case Q856: | |
| 1999/0429 | if (n != 1 || off < 0xda || off + n > 0xe0) | |
| 1999/0423 | return 0; | |
| 1999/0429 | if (i2c_wr8(BT856Addr, off, *buf) == 0) return 0; switch ((int)off) { case 0xDA: q856[0] = *buf; break; case 0xDC: q856[1] = *buf; break; case 0xDE: q856[2] = *buf; break; } return 1; | |
| 1999/0428 | case Qreg: | |
| 1999/0424 | if (off < 0 || off + n > 0x200 || (off & 0x3)) return 0; | |
| 1999/0428 | switch (n) { case 1: writeb(*buf, pciBaseAddr + off); break; case 2: writew(*(short *)buf, pciBaseAddr + off); break; case 4: | |
| 1999/0424 | writel(*(long *)buf, pciBaseAddr + off); | |
| 1999/0428 | break; default: return 0; | |
| 1999/0424 | } | |
| 1999/0428 | return n; | |
| 1999/0423 | case Qvideo: case Qjframe: | |
| 1999/0424 | return vwrite(c, buf, n, off); | |
| 1999/0423 | } | |
| 1999/0422 | } Dev viddevtab = { 'V', "video", vidreset, devinit, vidattach, devclone, vidwalk, vidstat, vidopen, devcreate, vidclose, vidread, devbread, vidwrite, devbwrite, devremove, devwstat, }; static void | |
| 1999/0424 | lmlintr(Ureg *, void *) { ulong flags = readl(pciBaseAddr+ZR36057_INTR_STAT); // print("MjpgDrv_intrHandler stat=0x%08x\n", flags); | |
| 1999/0422 | ||
| 1999/0424 | // Reset all interrupts from 067 writel(0xff000000, pciBaseAddr + ZR36057_INTR_STAT); | |
| 1999/0423 | ||
| 1999/0424 | if(flags & ZR36057_INTR_JPEGREP) wakeup(&sleeper); return; | |
| 1999/0422 | } | |