#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, Q060, Q067, Qvideo, Qjframe, }; static Dirtab viddir[]={ // name, qid, size, mode "vid819", {Q819}, 0, 0644, "vid856", {Q856}, 0, 0644, "vid060", {Q060}, 0, 0644, "vid067", {Q067}, 0, 0644, "video", {Qvideo}, 0, 0666, "jframe", {Qjframe}, 0, 0666, }; CodeData * codeData; MjpgDrv * mjpgDrv; static void lml33_i2c_pause(void) { microdelay(I2C_DELAY); } static void lml33_i2c_waitscl(void) { 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 lml33_i2c_start(void) { writel(ZR36057_I2C_SCL|ZR36057_I2C_SDA, pciBaseAddr + ZR36057_I2C_BUS); lml33_i2c_waitscl(); lml33_i2c_pause(); writel(ZR36057_I2C_SCL, pciBaseAddr + ZR36057_I2C_BUS); lml33_i2c_pause(); writel(0, pciBaseAddr + ZR36057_I2C_BUS); lml33_i2c_pause(); } static void lml33_i2c_stop(void) { // the clock should already be low, make sure data is writel(0, pciBaseAddr + ZR36057_I2C_BUS); lml33_i2c_pause(); // set clock high and wait for device to catch up writel(ZR36057_I2C_SCL, pciBaseAddr + ZR36057_I2C_BUS); lml33_i2c_waitscl(); lml33_i2c_pause(); // set the data high to indicate the stop bit writel(ZR36057_I2C_SCL|ZR36057_I2C_SDA, pciBaseAddr + ZR36057_I2C_BUS); lml33_i2c_pause(); } static void lml33_i2c_wrbit(int bit) { if (bit){ writel(ZR36057_I2C_SDA, pciBaseAddr + ZR36057_I2C_BUS); // set data lml33_i2c_pause(); writel(ZR36057_I2C_SDA|ZR36057_I2C_SCL, pciBaseAddr + ZR36057_I2C_BUS); lml33_i2c_waitscl(); lml33_i2c_pause(); writel(ZR36057_I2C_SDA, pciBaseAddr + ZR36057_I2C_BUS); lml33_i2c_pause(); } else { writel(0, pciBaseAddr + ZR36057_I2C_BUS); // clr data lml33_i2c_pause(); writel(ZR36057_I2C_SCL, pciBaseAddr + ZR36057_I2C_BUS); lml33_i2c_waitscl(); lml33_i2c_pause(); writel(0, pciBaseAddr + ZR36057_I2C_BUS); lml33_i2c_pause(); } } static int lml33_i2c_rdbit(void) { int bit; // the clk line should be low // ensure we are not asserting the data line writel(ZR36057_I2C_SDA, pciBaseAddr + ZR36057_I2C_BUS); lml33_i2c_pause(); // set the clock high and wait for device to catch up writel(ZR36057_I2C_SDA|ZR36057_I2C_SCL, pciBaseAddr + ZR36057_I2C_BUS); lml33_i2c_waitscl(); lml33_i2c_pause(); // 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); lml33_i2c_pause(); return bit; } static int lml33_i2c_rdbyte(uchar *v) { int i, ack; uchar res; res = 0; for (i=0;i<8;i++){ res = res << 1; res += lml33_i2c_rdbit(); } ack = lml33_i2c_rdbit(); *v = res; return ack; } static int lml33_i2c_wrbyte(uchar v) { int i, ack; for (i=0;i<8;i++){ lml33_i2c_wrbit(v & 0x80); v = v << 1; } ack = lml33_i2c_rdbit(); return ack; } static void lml33_i2c_write_bytes(uchar addr, uchar sub, uchar *bytes, long num) { int ack; long i; lml33_i2c_start(); ack = lml33_i2c_wrbyte(addr); if (ack == 1) error(Eio); ack = lml33_i2c_wrbyte(sub); if (ack == 1) error(Eio); for(i=0;i>8 & 0x03); lml33_post_write(guest_id, 2, reg & 0xff); lml33_post_write(guest_id, 3, v); } static uchar lml33_zr060_read(int reg) { int guest_id; guest_id = GID060; lml33_post_write(guest_id, 1, reg>>8 & 0x03); lml33_post_write(guest_id, 2, reg & 0xff); return lml33_post_read(guest_id, 3); } long chipread(long addr, char *buf, long n, long off) { long i; for (i = 0; i < n; i++) { *buf++ = lml33_i2c_rd8(addr, off++); } return i; } long post060read(char *buf, long n, long off) { long i; for (i = 0; i < n; i++) { *buf++ = lml33_zr060_read(off++); } return i; } static void lmlintr(Ureg *, void *); static void vidreset(void) { ulong regpa; int i; pcidev = pcimatch(nil, PCI_VENDOR_ZORAN, PCI_DEVICE_ZORAN_36067); if (pcidev == nil) { print("No zr36067 found.\n"); return; } 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 = (Fragment *)PADDR(&(codeData->frag[i])); // Length is in double words, in position 1..20 codeData->fragmDescr[i].fragmLength = (FRAGSIZE >> 1) | FRAGM_FINAL_B; } // Get dynamic kernel memory allocaton for the driver if((mjpgDrv = xallocz(sizeof(MjpgDrv), 0)) == nil) { print("LML33: can't allocate dynamic memory for MjpgDrv\n"); return; } print("initializing LML33 board..."); pciPhysBaseAddr = (void *)(pcidev->mem[0].bar & ~0x0F); print("zr36067 found at %lux\n", pciPhysBaseAddr); regpa = upamalloc(pcidev->mem[0].bar & ~0x0F, pcidev->mem[0].size, 0); if (regpa == 0) { print("lml: failed to map registers\n"); return; } pciBaseAddr = (ulong)KADDR(regpa); // make sure the device will respond to mem accesses // (pcicmd_master | pcicmd_memory) -- probably superfluous // pcicfgw32(pcidev, PciPCR, 0x04 | 0x02); // set bus latency -- probably superfluous // pcicfgw8(pcidev, PciLTR, 64); // Interrupt handler intrenable(pcidev->intl, lmlintr, nil, pcidev->tbdf); 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: case Q060: case Q067: // allow one open per file break; case Qvideo: case Qjframe: // 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: case Q060: case Q067: case Qvideo: case Qjframe: authclose(c); } } static long vidread(Chan *c, void *buf, long n, vlong off) { switch(c->qid.path){ case Q819: if (off < 0 || off + n > 20) return 0; return chipread(BT819Addr, buf, n, off); case Q856: if (off < 0xda || off + n > 0xe0) return 0; return chipread(BT856Addr, buf, n, off); case Q060: return post060read(buf, n, off); case Q067: if (off < 0 || off + n > 20 || (off & 0x3) || n != 4) return 0; *(long *)buf = readl(pciBaseAddr + off); return 4; case Qvideo: case Qjframe: return videoread(c, buf, n, off); } } static long vidwrite(Chan *c, void *va, long n, vlong off) { switch(c->qid.path){ case Q819: if (off < 0 || off + n > 20) return 0; return chipwrite(BT819Addr, buf, n, off); case Q856: if (off < 0xda || off + n > 0xe0) return 0; return chipwrite(BT856Addr, buf, n, off); case Q060: return post060write(buf, n, off); case Q067: if (off < 0 || off + n > 20 || (off & 0x3) || n != 4) return 0; writel(*(long *)buf, pciBaseAddr + off); return 4; case Qvideo: case Qjframe: return videowrite(c, buf, n, off); } } Dev viddevtab = { 'V', "video", vidreset, devinit, vidattach, devclone, vidwalk, vidstat, vidopen, devcreate, vidclose, vidread, devbread, vidwrite, devbwrite, devremove, devwstat, }; static void lmlintr(Ureg *ur, void *) { }