/* * Driver for Hauppage TV board * * Control commands: * * init * window %d %d %d %d * colorkey %d %d %d %d %d %d * capture %d %d %d %d * capbrightness %d * capcontrast %d * capsaturation %d * caphue %d * capbw %d * brightness %d * contrast %d * saturation %d * source %d * svideo %d * format %d * channel %d %d * signal * volume %d [ %d ] * bass %d * treble %d * freeze %d */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "tv.h" #include enum { MemSize= 1, MemAddr= 0xB8000, CompressReg= -14, /* smart lock registers */ SLReg1= -2, SLReg2= -1, /* the Bt812 registers */ Bt812Index= -5, Bt812Data= -6, Bt2VideoPresent= 0x40, Bt4ColorBars= 0x40, Bt5YCFormat= 0x80, Bt7TriState= 0x0C, /* VxP 500 registers */ Vxp500Index= 0, Vxp500Data= 1, /* video controller registers */ MemoryWindowBaseAddrA= 0x14, MemoryWindowBaseAddrB= 0x15, MemoryPageReg= 0x16, MemoryConfReg= 0x18, ISAControl= 0x30, I2CControl= 0x34, InputVideoConfA= 0x38, InputVideoConfB= 0x39, ISASourceWindowWidthA= 0x3A, ISASourceWindowWidthB= 0x3B, ISASourceWindowHeightA= 0x3C, ISASourceWindowHeightB= 0x3D, InputHorzCropLeftA= 0x40, InputHorzCropLeftB= 0x41, InputHorzCropRightA= 0x44, InputHorzCropRightB= 0x45, InputHorzCropTopA= 0x48, InputHorzCropTopB= 0x49, InputHorzCropBottomA= 0x4C, InputHorzCropBottomB= 0x4D, InputHorzFilter= 0x50, InputHorzScaleControlA= 0x54, InputHorzScaleControlB= 0x55, InputVertInterpolControl= 0x58, InputVertScaleControlA= 0x5C, InputVertScaleControlB= 0x5D, InputFieldPixelBufStatus= 0x64, VideoInputFrameBufDepthA= 0x68, VideoInputFrameBufDepthB= 0x69, AcquisitionControl= 0x6C, AcquisitionAddrA= 0x70, AcquisitionAddrB= 0x71, AcquisitionAddrC= 0x72, VideoBufferLayoutControl= 0x73, CaptureControl= 0x80, CaptureViewPortAddrA= 0x81, CaptureViewPortAddrB= 0x82, CaptureViewPortAddrC= 0x83, CaptureViewPortWidthA= 0x84, CaptureViewPortWidthB= 0x85, CaptureViewPortHeightA= 0x86, CaptureViewPortHeightB= 0x87, CapturePixelBufLow= 0x88, CapturePixelBufHigh= 0x89, CaptureMultiBufDepthA= 0x8A, CaptureMultiBufDepthB= 0x8B, DisplayControl= 0x92, VGAControl= 0x94, OutputProcControlA= 0x96, OutputProcControlB= 0x97, DisplayViewPortStartAddrA= 0xA0, DisplayViewPortStartAddrB= 0xA1, DisplayViewPortStartAddrC= 0xA2, DisplayViewPortWidthA= 0xA4, DisplayViewPortWidthB= 0xA5, DisplayViewPortHeightA= 0xA6, DisplayViewPortHeightB= 0xA7, DisplayViewPortOrigTopA= 0xA8, DisplayViewPortOrigTopB= 0xA9, DisplayViewPortOrigLeftA= 0xAA, DisplayViewPortOrigLeftB= 0xAB, DisplayWindowLeftA= 0xB0, DisplayWindowLeftB= 0xB1, DisplayWindowRightA= 0xB4, DisplayWindowRightB= 0xB5, DisplayWindowTopA= 0xB8, DisplayWindowTopB= 0xB9, DisplayWindowBottomA= 0xBC, DisplayWindowBottomB= 0xBD, OutputVertZoomControlA= 0xC0, OutputVertZoomControlB= 0xC1, OutputHorzZoomControlA= 0xC4, OutputHorzZoomControlB= 0xC5, BrightnessControl= 0xC8, ContrastControl= 0xC9, SaturationControl= 0xCA, VideoOutIntrStatus= 0xD3, /* smart lock bits */ PixelClk= 0x03, SmartLock= 0x00, FeatureConnector= 0x01, Divider= 0x02, Window= 0x08, KeyWindow= 0x0C, HSyncLow= 0x20, VSyncLow= 0x40, ClkBit= 0x01, DataBit= 0x02, HoldBit= 0x04, SelBit= 0x08, DivControl= 0x40, /* i2c bus control bits */ I2C_Clock= 0x02, I2C_Data= 0x08, I2C_RdClock= 0x10, I2C_RdData= 0x20, I2C_RdData_D= 0x40, /* I2C bus addresses */ Adr5249= 0x22, /* teletext decoder */ Adr8444= 0x48, /* 6-bit DAC (TDA 8444) */ Adr6300= 0x80, /* sound fader (TEA 6300) */ Adr6320= 0x80, /* sound fader (TEA 6320T) */ AdrTuner= 0xC0, /* Philips audio chips */ TEA6300= 0, TEA6320T= 1, /* input formats */ NTSC_M = 0, NTSC_443 = 1, External = 2, NTSCCropLeft= 36, /* NTSC 3.6 usec */ NTSCCropRight= 558, /* NTSC 55.8 usec */ /* color control indices */ Vxp500Brightness= 1, Vxp500Contrast= 2, Vxp500Saturation= 3, Bt812Brightness= 4, Bt812Contrast= 5, Bt812Saturation= 6, Bt812Hue= 7, Bt812BW= 8, /* board revision numbers */ RevisionPP= 0, RevisionA= 1, HighQ= 2, /* VGA controller registers */ VGAMiscOut= 0x3CC, VGAIndex= 0x3D4, VGAData= 0x3D5, VGAHorzTotal= 0x00, }; enum { Qdir, Qdata, Qctl, }; static Dirtab tvtab[]={ "tv", {Qdata, 0}, 0, 0600, "tvctl", {Qctl, 0}, 0, 0600, }; static int ports[] = { /* board addresses */ 0x51C, 0x53C, 0x55C, 0x57C, 0x59C, 0x5BC, 0x5DC, 0x5FC }; /* * Default settings, settings between 0..100 */ static int defaults[] = { Vxp500Brightness, 0, Vxp500Contrast, 54, Vxp500Saturation, 54, Bt812Brightness, 13, Bt812Contrast, 57, Bt812Saturation, 51, Bt812Hue, 0, Bt812BW, 0, }; static int port; static int soundchip; static int boardrev; static int left, right; static int vsync, hsync; static ulong xtalfreq; static ushort cropleft, cropright; static ushort cropbottom, croptop; static Rectangle window, capwindow; static void setreg(int, int); static void setbt812reg(int, int); static void videoinit(void); static void createwindow(Rectangle); static void setcontrols(int, uchar); static void setcolorkey(int, int, int, int, int, int); static void soundinit(void); static void setvolume(int, int); static void setbass(int); static void settreble(int); static void setsoundsource(int); static void tunerinit(void); static void settuner(int, int); static void setvideosource(int); static int waitvideosignal(void); static void freeze(int); static void setsvideo(int); static void setinputformat(int); static void enablevideo(void); static void *saveframe(int *); static int min(int a, int b) { return a < b ? a : b; } static int max(int a, int b) { return a < b ? b : a; } static int present(int port) { outb(port+Vxp500Index, 0xAA); if (inb(port+Vxp500Index) != 0xAA) return 0; outb(port+Vxp500Index, 0x55); outb(port+Vxp500Data, 0xAA); if (inb(port+Vxp500Index) != 0x55) return 0; if (inb(port+Vxp500Data) != 0xAA) return 0; outb(port+Vxp500Data, 0x55); if (inb(port+Vxp500Index) != 0x55) return 0; if (inb(port+Vxp500Data) != 0x55) return 0; return 1; } static int getvsync(void) { int vslow, vshigh, s; ushort timo; s = splhi(); outb(port+Vxp500Index, VideoOutIntrStatus); /* wait for VSync to go high then low */ for (timo = ~0; timo; timo--) if (inb(port+Vxp500Data) & 2) break; for (timo = ~0; timo; timo--) if ((inb(port+Vxp500Data) & 2) == 0) break; /* count how long it stays low and how long it stays high */ for (vslow = 0, timo = ~0; timo; timo--, vslow++) if (inb(port+Vxp500Data) & 2) break; for (vshigh = 0, timo = ~0; timo; timo--, vshigh++) if ((inb(port+Vxp500Data) & 2) == 0) break; splx(s); return vslow < vshigh; } static int gethsync(void) { int hslow, hshigh, s; ushort timo; s = splhi(); outb(port+Vxp500Index, VideoOutIntrStatus); /* wait for HSync to go high then low */ for (timo = ~0; timo; timo--) if (inb(port+Vxp500Data) & 1) break; for (timo = ~0; timo; timo--) if ((inb(port+Vxp500Data) & 1) == 0) break; /* count how long it stays low and how long it stays high */ for (hslow = 0, timo = ~0; timo; timo--, hslow++) if (inb(port+Vxp500Data) & 1) break; for (hshigh = 0, timo = ~0; timo; timo--, hshigh++) if ((inb(port+Vxp500Data) & 1) == 0) break; splx(s); return hslow < hshigh; } static void tvinit(void) { int i; for (i = 0, port = 0; i < nelem(ports); i++) { if (present(ports[i])) { port = ports[i]; break; } } if (i == nelem(ports)) return; /* * the following routines are the prefered way to * find out the sync polarities. Unfortunately, it * doesn't always work. */ #ifndef VSync vsync = getvsync(); hsync = gethsync(); #else vsync = VSync; hsync = HSync; #endif left = right = 80; soundinit(); tunerinit(); videoinit(); } static Chan* tvattach(char *spec) { if (port == 0) error(Enonexist); return devattach('V', spec); } static int tvwalk(Chan *c, char *name) { return devwalk(c, name, tvtab, nelem(tvtab), devgen); } static void tvstat(Chan *c, char *db) { devstat(c, db, tvtab, nelem(tvtab), devgen); } static Chan* tvopen(Chan *c, int omode) { return devopen(c, omode, tvtab, nelem(tvtab), devgen); } static void tvclose(Chan *) { } static long tvread(Chan *c, void *a, long n, ulong offset) { static void *frame; static int size; USED(offset); switch(c->qid.path & ~CHDIR){ case Qdir: return devdirread(c, a, n, tvtab, nelem(tvtab), devgen); case Qdata: if (eqrect(capwindow, Rect(0, 0, 0, 0))) error(Ebadarg); if (offset == 0) frame = saveframe(&size); if (frame) { if (n > size - offset) n = size - offset; memmove(a, (char *)frame + offset, n); } else error(Enovmem); break; default: n=0; break; } return n; } static long tvwrite(Chan *c, char *a, long n, ulong offset) { char buf[128], *field[10]; int i, nf, source; static Rectangle win; static int hsize, size = 0; static void *frame; USED(offset); switch(c->qid.path & ~CHDIR){ case Qctl: if (n > sizeof(buf)-1) n = sizeof(buf)-1; memmove(buf, a, n); buf[n] = '\0'; nf = parsefields(buf, field, nelem(field), " \t"); if (nf < 1) error(Ebadarg); if (strcmp(field[0], "init") == 0) { window = Rect(0, 0, 0, 0); capwindow = Rect(0, 0, 0, 0); source = 0; /* video 0 input */ setvideosource(source); left = right = 80; setsoundsource(source); for (i = 0; i < nelem(defaults); i += 2) setcontrols(defaults[i], defaults[i+1]); } else if (strcmp(field[0], "colorkey") == 0) { if (nf < 7) error(Ebadarg); setcolorkey(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0), strtoul(field[3], 0, 0), strtoul(field[4], 0, 0), strtoul(field[5], 0, 0), strtoul(field[6], 0, 0)); } else if (strcmp(field[0], "window") == 0) { if (nf < 5) error(Ebadarg); createwindow(Rect(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0), strtoul(field[3], 0, 0), strtoul(field[4], 0, 0))); setvolume(left, right); } else if (strcmp(field[0], "capture") == 0) { if (nf < 5) error(Ebadarg); capwindow = Rect(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0), strtoul(field[3], 0, 0), strtoul(field[4], 0, 0)); } else if (strcmp(field[0], "freeze") == 0) { if (nf < 2) error(Ebadarg); freeze(strtoul(field[1], 0, 0)); } else if (strcmp(field[0], "capbrightness") == 0) { if (nf < 2) error(Ebadarg); setcontrols(Bt812Brightness, strtoul(field[1], 0, 0)); } else if (strcmp(field[0], "capcontrast") == 0) { if (nf < 2) error(Ebadarg); setcontrols(Bt812Contrast, strtoul(field[1], 0, 0)); } else if (strcmp(field[0], "capsaturation") == 0) { if (nf < 2) error(Ebadarg); setcontrols(Bt812Saturation, strtoul(field[1], 0, 0)); } else if (strcmp(field[0], "caphue") == 0) { if (nf < 2) error(Ebadarg); setcontrols(Bt812Hue, strtoul(field[1], 0, 0)); } else if (strcmp(field[0], "capbw") == 0) { if (nf < 2) error(Ebadarg); setcontrols(Bt812BW, strtoul(field[1], 0, 0)); } else if (strcmp(field[0], "brightness") == 0) { if (nf < 2) error(Ebadarg); setcontrols(Vxp500Brightness, strtoul(field[1], 0, 0)); } else if (strcmp(field[0], "contrast") == 0) { if (nf < 2) error(Ebadarg); setcontrols(Vxp500Contrast, strtoul(field[1], 0, 0)); } else if (strcmp(field[0], "saturation") == 0) { if (nf < 2) error(Ebadarg); setcontrols(Vxp500Saturation, strtoul(field[1], 0, 0)); } else if (strcmp(field[0], "source") == 0) { if (nf < 2) error(Ebadarg); source = strtoul(field[1], 0, 0); setvideosource(source); setsoundsource(source); } else if (strcmp(field[0], "svideo") == 0) { if (nf < 2) error(Ebadarg); setsvideo(strtoul(field[1], 0, 0)); } else if (strcmp(field[0], "format") == 0) { if (nf < 2) error(Ebadarg); setinputformat(strtoul(field[1], 0, 0)); } else if (strcmp(field[0], "channel") == 0) { if (nf < 3) error(Ebadarg); setvolume(0, 0); settuner(strtoul(field[1], 0, 0), strtoul(field[2], 0, 0)); tsleep(&up->sleep, return0, 0, 300); setvolume(left, right); } else if (strcmp(field[0], "signal") == 0) { if (!waitvideosignal()) error(Etimedout); } else if (strcmp(field[0], "volume") == 0) { if (nf < 2) error(Ebadarg); left = strtoul(field[1], 0, 0); if (nf < 3) right = left; else right = strtoul(field[2], 0, 0); setvolume(left, right); } else if (strcmp(field[0], "bass") == 0) { if (nf < 2) error(Ebadarg); setbass(strtoul(field[1], 0, 0)); } else if (strcmp(field[0], "treble") == 0) { if (nf < 2) error(Ebadarg); settreble(strtoul(field[1], 0, 0)); } else error(Ebadctl); break; default: error(Ebadusefd); } return n; } Dev tvdevtab = { 'V', "tv", devreset, tvinit, tvattach, devdetach, devclone, tvwalk, tvstat, tvopen, devcreate, tvclose, tvread, devbread, tvwrite, devbwrite, devremove, devwstat, }; static void setreg(int index, int data) { outb(port+Vxp500Index, index); outb(port+Vxp500Data, data); } static unsigned int getreg(int index) { outb(port+Vxp500Index, index); return inb(port+Vxp500Data); } /* * I2C routines */ static void delayi2c(void) { int i, val; /* delay for 4.5 usec to guarantee clock time */ for (i = 0; i < 75; i++) { /* was 50 */ val = inb(port+Vxp500Data); USED(val); } } static int waitSDA(void) { ushort timo; /* wait for i2c clock to float high */ for (timo = ~0; timo; timo--) if (inb(port+Vxp500Data) & I2C_RdData) break; if (!timo) print("devtv: waitSDA fell out of loop\n"); return !timo; } static int waitSCL(void) { ushort timo; /* wait for i2c clock to float high */ for (timo = ~0; timo; timo--) if (inb(port+Vxp500Data) & I2C_RdClock) break; delayi2c(); if (!timo) print("devtv: waitSCL fell out of loop\n"); return !timo; } static int seti2cdata(int data) { int b, reg, val; int error; error = 0; reg = inb(port+Vxp500Data); for (b = 0x80; b; b >>= 1) { if (data & b) reg |= I2C_Data; else reg &= ~I2C_Data; outb(port+Vxp500Data, reg); reg |= I2C_Clock; outb(port+Vxp500Data, reg); error |= waitSCL(); reg &= ~I2C_Clock; outb(port+Vxp500Data, reg); delayi2c(); } reg |= I2C_Data; outb(port+Vxp500Data, reg); reg |= I2C_Clock; outb(port+Vxp500Data, reg); error |= waitSCL(); val = inb(port+Vxp500Data); USED(val); reg &= ~I2C_Clock; outb(port+Vxp500Data, reg); delayi2c(); return error; } static int seti2creg(int id, int index, int data) { int reg, error; error = 0; /* set i2c control register to enable i2c clock and data lines */ setreg(I2CControl, I2C_Data|I2C_Clock); error |= waitSDA(); error |= waitSCL(); outb(port+Vxp500Data, I2C_Clock); delayi2c(); outb(port+Vxp500Data, 0); delayi2c(); error |= seti2cdata(id); error |= seti2cdata(index); error |= seti2cdata(data); reg = inb(port+Vxp500Data); reg &= ~I2C_Data; outb(port+Vxp500Data, reg); reg |= I2C_Clock; outb(port+Vxp500Data, reg); error |= waitSCL(); reg |= I2C_Data; outb(port+Vxp500Data, reg); error |= waitSDA(); return error; } static int seti2cregs(int id, int index, int n, uchar *data) { int reg, error; error = 0; /* set i2c control register to enable i2c clock and data lines */ setreg(I2CControl, I2C_Data|I2C_Clock); error |= waitSDA(); error |= waitSCL(); outb(port+Vxp500Data, I2C_Clock); delayi2c(); outb(port+Vxp500Data, 0); delayi2c(); /* send data */ error |= seti2cdata(id); error |= seti2cdata(index); while (n--) error |= seti2cdata(*data++); /* send stop */ reg = inb(port+Vxp500Data); reg &= ~I2C_Data; outb(port+Vxp500Data, reg); reg |= I2C_Clock; outb(port+Vxp500Data, reg); error |= waitSCL(); reg |= I2C_Data; outb(port+Vxp500Data, reg); error |= waitSDA(); return error; } /* * Audio routines */ static void setvolume(int left, int right) { int vol, loudness = 0; if (soundchip == TEA6300) { seti2creg(Adr6300, 0, (63L * left) / 100); seti2creg(Adr6300, 1, (63L * right) / 100); vol = (15L * max(left, right)) / 100; seti2creg(Adr6300, 4, 0x30 | vol); } else { vol = (63L * max(left, right)) / 100; seti2creg(Adr6320, 0, vol | (loudness << 6)); seti2creg(Adr6320, 1, (63L * right) / 100); seti2creg(Adr6320, 2, (63L * left) / 100); } } static void setbass(int bass) { if (soundchip == TEA6300) seti2creg(Adr6300, 2, (15L * bass) / 100); else seti2creg(Adr6320, 5, max((31L * bass) / 100, 4)); } static void settreble(int treble) { if (soundchip == TEA6300) seti2creg(Adr6300, 3, (15L * treble) / 100); else seti2creg(Adr6320, 6, max((31L * treble) / 100, 7)); } static void setsoundsource(int source) { if (soundchip == TEA6300) seti2creg(Adr6300, 5, 1 << source); else seti2creg(Adr6320, 7, source); setbass(50); settreble(50); setvolume(left, right); } static void soundinit(void) { if (seti2creg(Adr6320, 7, 0) && seti2creg(Adr6300, 4, 0)) print("devtv: Audio init failed\n"); soundchip = AudioChip; setvolume(0, 0); } /* * Tuner routines */ static long hrcfreq[] = { /* HRC CATV frequencies */ 0, 7200, 5400, 6000, 6600, 7800, 8400, 17400, 18000, 18600, 19200, 19800, 20400, 21000, 12000, 12600, 13200, 13800, 14400, 15000, 15600, 16200, 16800, 21600, 22200, 22800, 23400, 24000, 24600, 25200, 25800, 26400, 27000, 27600, 28200, 28800, 29400, 30000, 30600, 31200, 31800, 32400, 33000, 33600, 34200, 34800, 35400, 36000, 36600, 37200, 37800, 38400, 39000, 39600, 40200, 40800, 41400, 42000, 42600, 43200, 43800, 44400, 45000, 45600, 46200, 46800, 47400, 48000, 48600, 49200, 49800, 50400, 51000, 51600, 52200, 52800, 53400, 54000, 54600, 55200, 55800, 56400, 57000, 57600, 58200, 58800, 59400, 60000, 60600, 61200, 61800, 62400, 63000, 63600, 64200, 9000, 9600, 10200, 10800, 11400, 64800, 65400, 66000, 66600, 67200, 67800, 68400, 69000, 69600, 70200, 70800, 71400, 72000, 72600, 73200, 73800, 74400, 75000, 75600, 76200, 76800, 77400, 78000, 78600, 79200, 79800, }; static void settuner(int channel, int finetune) { static long lastfreq; uchar data[3]; long freq; int cw2, n, sa; if (channel < 0 || channel > nelem(hrcfreq)) error(Ebadarg); freq = hrcfreq[channel]; /* these settings are all for (FS936E) USA Tuners */ if (freq < 16025) /* low band */ cw2 = 0xA4; else if (freq < 45425) /* mid band */ cw2 = 0x94; else cw2 = 0x34; /* * Channels are stored are 1/100 MHz resolutions, but * the tuner wants stuff in MHZ, so divide by 100, we * then have to shift by 4 to get the prog. div. value */ n = ((freq + 4575L) * 16) / 100L + finetune; if (freq > lastfreq) { sa = (n >> 8) & 0xFF; data[0] = n & 0xFF; data[1] = 0x8E; data[2] = cw2; } else { sa = 0x8E; data[0] = cw2; data[1] = (n >> 8) & 0xFF; data[2] = n & 0xFF; } lastfreq = freq; seti2cregs(AdrTuner, sa, 3, data); } static void tunerinit(void) { if (seti2creg(AdrTuner, 0, 0)) print("devtv: Tuner init failed\n"); } /* * Video routines */ static int slreg1 = 0; static int slreg2 = 0; static int vcogain = 0; static int phdetgain = 2; static int plln1 = 2; static int pllp2 = 1; static void waitforretrace(void) { ushort timo; for (timo = ~0; (getreg(VideoOutIntrStatus) & 2) == 0 && timo; timo--) /* wait for VSync inactive */; for (timo = ~0; (getreg(VideoOutIntrStatus) & 2) && timo; timo--) /* wait for VSync active */; } static void updateshadowregs(void) { int val; setreg(InputVideoConfA, getreg(InputVideoConfA) | 0x40); val = getreg(OutputProcControlB); setreg(OutputProcControlB, val & 0x7F); setreg(OutputProcControlB, val | 0x80); } static void setvgareg(int data) { /* set HSync & VSync first, to make sure VSync works properly */ setreg(VGAControl, (getreg(VGAControl) & ~0x06) | (data & 0x06)); /* wait for VSync and set the whole register */ waitforretrace(); setreg(VGAControl, data); } static void setbt812reg(int index, int data) { outb(port+Bt812Index, index); outb(port+Bt812Data, data); } static int getbt812reg(int index) { outb(port+Bt812Index, index); return inb(port+Bt812Data); } static void setbt812regpair(int index, ushort data) { outb(port+Bt812Index, index); outb(port+Bt812Data, data); outb(port+Bt812Data, data >> 8); } static void setvideosource(int source) { int s; source &= 7; s = source & 3; setbt812reg(0, ((s << 2) | s) << 3); s = (source & 4) << 4; setbt812reg(4, (getbt812reg(4) & ~Bt4ColorBars) | s); } static void setsvideo(int enable) { if (enable) setbt812reg(5, getbt812reg(5) | Bt5YCFormat); else setbt812reg(5, getbt812reg(5) & ~Bt5YCFormat); } static int waitvideosignal(void) { ushort timo; for (timo = ~0; timo; timo--) if (getbt812reg(2) & Bt2VideoPresent) return 1; return 0; } /* * ICS1572 Programming Configuration * * R = 1 * M = x * A = x * N1 = 4 * N2 = internal divide ratio */ static uchar ICSbits[7] = { 0x01, /* bits 8 - 1 00000001 */ 0x05, /* bits 16 - 9 00000101 */ 0xFF, /* bits 24 - 17 11111111 */ 0x8C, /* bits 32 - 25 10001100 */ 0xBF, /* bits 40 - 33 10111111 */ 0x00, /* bits 48 - 41 00000000 */ 0x00, /* bits 56 - 49 00000000 */ }; static void sendbit(int val, int hold) { slreg2 &= ~(HoldBit|DataBit|ClkBit); if (val) slreg2 |= DataBit; if (hold) slreg2 |= HoldBit; outb(port+SLReg2, slreg2); outb(port+SLReg2, slreg2|ClkBit); outb(port+SLReg2, slreg2); } static void load1572(int select) { int reg; uchar mask; if (select) slreg2 |= SelBit; else slreg2 &= ~SelBit; outb(port+SLReg2, slreg2); for (reg = 0; reg < sizeof(ICSbits); reg++) { for (mask = 1; mask != 0; mask <<= 1) { if (reg == sizeof(ICSbits)-1 && mask == 0x80) { sendbit(ICSbits[reg] & mask, 1); } else sendbit(ICSbits[reg] & mask, 0); } } } static void smartlockdiv(int count, int vcogain, int phdetgain, int n1, int p2) { int extdiv, intdiv; int nslreg2, external; nslreg2 = slreg2; extdiv = ((count - 1) / 512) + 1; intdiv = (count / extdiv); nslreg2 &= ~0xC0; switch (extdiv) { case 1: external = 0; break; case 2: external = 1; break; case 3: external = 1; nslreg2 |= 0x40; break; case 4: external = 1; nslreg2 |= 0x80; break; default: return; } if ((slreg1 & PixelClk) == 0) { slreg2 = nslreg2; outb(port+SLReg2, slreg2); } /* set PLL divider */ ICSbits[0] &= ~0x07; ICSbits[0] |= n1 & 0x07; ICSbits[3] &= ~0xB7; ICSbits[3] |= vcogain & 0x07; ICSbits[3] |= (phdetgain & 0x03) << 4; ICSbits[3] |= p2 << 7; if (external) ICSbits[1] |= 0x04; /* set EXTFBKEN */ else ICSbits[1] &= ~0x04; /* clear EXTFBKEN */ intdiv--; ICSbits[2] = intdiv; /* set N2 */ ICSbits[3] &= ~ 0x08; ICSbits[3] |= (intdiv >> 5) & 0x08; load1572(1); } static void disablecolorkey(void) { setreg(DisplayControl, getreg(DisplayControl) & 0xFE); updateshadowregs(); } static uchar colorkeylimit[6] = { 15, /* upper limit green */ 255, /* lower limit green */ 63, /* upper limit red */ 63, /* upper limit blue */ 15, /* lower limit red */ 15, /* lower limit blue */ }; static void enablecolorkey(int enable) { int i; if (enable) { for (i = 0; i < 6; i++) seti2creg(Adr8444, 0xF0 | i, colorkeylimit[i]); slreg1 &= ~0x1C; if (colorkeylimit[4] == 255) slreg1 |= 0x04; /* disable red lower limit */ if (colorkeylimit[1] == 255) slreg1 |= 0x08; /* disable green lower limit */ if (colorkeylimit[5] == 255) slreg1 |= 0x10; /* disable blue lower limit */ } else { for (i = 0; i < 6; i++) seti2creg(Adr8444, 0xF0 | i, 63); slreg1 |= 0x1C; } outb(port+SLReg1, slreg1); disablecolorkey(); } static void setcolorkey(int rl, int rh, int gl, int gh, int bl, int bh) { colorkeylimit[0] = gh; colorkeylimit[1] = gl; colorkeylimit[2] = rh; colorkeylimit[3] = bh; colorkeylimit[4] = rl; colorkeylimit[5] = bl; enablecolorkey(1); } static void waitvideoframe(void) { ushort timo; int val; /* clear status bits and wait for start of an even field */ val = getreg(InputFieldPixelBufStatus); USED(val); for (timo = ~0; timo; timo--) if ((getreg(InputFieldPixelBufStatus) & 2) == 0) break; if (!timo) print("devtv: Wait for video frame failed\n"); } static void freeze(int enable) { ushort timo; int reg; if (enable) { waitvideoframe(); waitvideoframe(); setreg(InputVideoConfB, getreg(InputVideoConfB) | 0x08); updateshadowregs(); for (timo = ~0; timo; timo--) if (getreg(InputVideoConfB) & 0x80) break; waitvideoframe(); reg = getreg(OutputProcControlB); if ((reg & 0x20) == 0) { setreg(ISAControl, 0x80); setreg(OutputProcControlB, getreg(OutputProcControlB) | 0x20); setreg(ISAControl, 0x42); reg = getreg(OutputProcControlB); setreg(OutputProcControlB, reg & 0x7F); setreg(OutputProcControlB, reg | 0x80); } } else { setreg(InputVideoConfB, getreg(InputVideoConfB) & ~0x08); updateshadowregs(); for (timo = ~0; timo; timo--) if (getreg(InputVideoConfB) & 0x40) break; waitvideoframe(); reg = getreg(InputFieldPixelBufStatus); USED(reg); } } static void enablevideo(void) { setreg(DisplayControl, 0x04); updateshadowregs(); } static void disablevideo(void) { setreg(DisplayControl, 0x18); updateshadowregs(); } static uchar vxp500init[] = { /* video register initialization in (index,data) hex pairs */ 0x30, 0x82, 0x39, 0x40, 0x58, 0x0C, 0x73, 0x02, 0x80, 0x00, 0x25, 0x0F, 0x26, 0x0F, 0x38, 0x46, 0x30, 0x03, 0x12, 0x3B, 0x97, 0x20, 0x13, 0x00, 0x14, 0x34, 0x15, 0x04, 0x16, 0x00, 0x17, 0x53, 0x18, 0x04, 0x19, 0x62, 0x1C, 0x00, 0x1D, 0x00, 0x34, 0x3A, 0x38, 0x06, 0x3A, 0x00, 0x3B, 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x40, 0x40, 0x41, 0x40, 0x44, 0xFF, 0x45, 0xFF, 0x48, 0x40, 0x49, 0x40, 0x4C, 0xFF, 0x4D, 0xFF, 0x50, 0xF0, 0x54, 0x30, 0x55, 0x00, 0x5C, 0x04, 0x5D, 0x00, 0x60, 0x00, 0x68, 0x00, 0x69, 0x00, 0x6C, 0x06, 0x6D, 0x00, 0x70, 0x00, 0x71, 0x00, 0x72, 0x00, 0x78, 0x01, 0x79, 0x0C, 0x80, 0x10, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, 0x88, 0x04, 0x89, 0x10, 0x8A, 0x00, 0x8B, 0x00, 0x90, 0x05, 0x91, 0x0C, 0x92, 0x18, 0x93, 0x00, 0x96, 0x18, 0x9A, 0x30, 0x9C, 0x2D, 0x9D, 0x00, 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA4, 0x50, 0xA5, 0x50, 0xA6, 0xF0, 0xA7, 0xF0, 0xA8, 0x19, 0xA9, 0x18, 0xAA, 0x64, 0xAB, 0x64, 0xB0, 0x64, 0xB1, 0x64, 0xB4, 0xA4, 0xB5, 0xA5, 0xB8, 0x19, 0xB9, 0x18, 0xBC, 0x09, 0xBD, 0x09, 0xC0, 0x00, 0xC1, 0x02, 0xC4, 0x00, 0xC5, 0x00, 0xC8, 0x00, 0xC9, 0x08, 0xCA, 0x08, 0xCE, 0x00, 0xCF, 0x00, 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0x38, 0x46, 0x97, 0xA0, 0x97, 0x20, 0x97, 0xA0, }; static uchar bt812init[] = { /* bt812 initializations */ 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0xC0, 0x04, 0x08, 0x05, 0x00, 0x06, 0x40, 0x07, 0x00, 0x08, 0x10, 0x09, 0x90, 0x0A, 0x80, 0x0B, 0x00, 0x0C, 0x0C, 0x0D, 0x03, 0x0E, 0x66, 0x0F, 0x00, 0x10, 0x80, 0x11, 0x02, 0x12, 0x16, 0x13, 0x00, 0x14, 0xE5, 0x15, 0x01, 0x16, 0xAB, 0x17, 0xAA, 0x18, 0x12, 0x19, 0x51, 0x1A, 0x46, 0x1B, 0x00, 0x1C, 0x00, 0x1D, 0x37, }; static ushort actpixs = 720; static ulong Hdesired = 13500000L; /* NTSC-M NTSC-443 EXTERNAL */ static ushort horzfreq[] = { 15734, 15625, 0 }; static ushort Vdelay[] = { 22, 25, 25 }; static ushort s2b[] = { 90, 90, 0 }; static ushort actlines[] = { 485, 485, 575 }; static ulong subcarfreq[] = { 3579545, 4433619, 4433619 }; static unsigned int framewidth[5][4] = { 1024, 512, 512, 512, /* mode 0 - single, double, single, quad */ 1536, 768, 768, 384, /* mode 1 - single, double, single, quad */ 2048, 1024, 1024, 512, /* mode 2 - single, double, single, quad */ 1024, 512, 512, 512, /* mode 3 - single, double, single, quad */ 1536, 768, 768, 384 /* mode 4 - single, double, single, quad */ }; static unsigned int frameheight[5][4] = { 512, 512, 1024, 512, /* mode 0 - single, double, single, quad */ 512, 512, 1024, 512, /* mode 1 - single, double, single, quad */ 512, 512, 1024, 512, /* mode 2 - single, double, single, quad */ 512, 512, 1024, 512, /* mode 3 - single, double, single, quad */ 512, 512, 1024, 256 /* mode 4 - single, double, single, quad */ }; static uchar horzfilter[] = { 3, 3, 2, 2, 1, 1, 0, 0 }; static uchar interleave[] = { 2, 3, 4, 2, 3 }; #define ADJUST(n) (((n) * hrsmult + hrsdiv - 1) / hrsdiv) static int q = 100; static int ilv = 2; static int hrsmult = 1; static int hrsdiv = 2; static ushort panmask[] = { 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE }; static void cropwindow(int left, int right, int top, int bottom) { top &= 0x3FE; bottom &= 0x3FE; setreg(InputHorzCropLeftA, left); setreg(InputHorzCropLeftB, left >> 8); setreg(InputHorzCropRightA, right); setreg(InputHorzCropRightB, right >> 8); setreg(InputHorzCropTopA, top); setreg(InputHorzCropTopB, top >> 8); setreg(InputHorzCropBottomA, bottom); setreg(InputHorzCropBottomB, bottom >> 8); } static void setinputformat(int format) { ushort hclock, hclockdesired; ulong subcarrier; int cr7; cr7 = getbt812reg(7) & ~Bt7TriState; if (format == External) cr7 |= Bt7TriState; setbt812reg(7, cr7); setbt812reg(5, getbt812reg(5) & 2); hclock = (xtalfreq >> 1) / horzfreq[format]; setbt812regpair(0x0C, hclock); setbt812regpair(0x0E, (ushort)(s2b[format] * (Hdesired / 10) / 1000000L) | 1); setbt812regpair(0x10, actpixs); setbt812regpair(0x12, Vdelay[format]); setbt812regpair(0x14, actlines[format]); subcarrier = (ulong) ((((long long)subcarfreq[format] * 0x1000000) / xtalfreq + 1) / 2); setbt812regpair(0x16, (int)(subcarrier & 0xFFFF)); /* subcarrier */ setbt812reg(0x18, (int)(subcarrier >> 16)); setbt812reg(0x19, (uchar)(((xtalfreq / 200) * 675) / 1000000L + 8)); setbt812reg(0x1A, (uchar)((xtalfreq * 65) / 20000000L - 10)); hclockdesired = (ushort) (Hdesired / horzfreq[format]); setbt812regpair(0x1B, (ushort)(((hclock - hclockdesired) * 65536L) / hclockdesired)); } static ushort vgadivider(void) { ushort horztotal; outb(VGAIndex, VGAHorzTotal); horztotal = (inb(VGAData) << 3) + 40; if (horztotal > ScreenWidth && horztotal < ((ScreenWidth * 3 ) / 2)) return horztotal; else return (ScreenWidth * 5) / 4; } static void videoinit(void) { int i, reg, width, tuner; /* early PLL smart lock initialization */ if (ScreenWidth == 640) { slreg1 = Window|HSyncLow|VSyncLow; slreg2 = 0x0D; } else { slreg1 = Window; slreg2 = 0x0C; } outb(port+CompressReg, 2); outb(port+SLReg1, slreg1); outb(port+SLReg2, slreg2); smartlockdiv((vgadivider() * hrsmult)/hrsdiv, vcogain, phdetgain, 2, 1); /* program the VxP-500 chip (disables video) */ waitforretrace(); for (i = 0; i < sizeof(vxp500init); i += 2) setreg(vxp500init[i], vxp500init[i+1]); /* set memory base for frame capture */ setreg(MemoryWindowBaseAddrA, MemAddr >> 14); setreg(MemoryWindowBaseAddrB, ((MemAddr >> 22) & 3) | (MemSize << 2)); setreg(MemoryPageReg, 0); /* generic 422 decoder, mode 3 and 4 */ setreg(MemoryConfReg, ilv+1); setreg(AcquisitionAddrA, 0); setreg(AcquisitionAddrB, 0); setreg(AcquisitionAddrC, 0); /* program VxP-500 for correct sync polarity */ reg = ScreenWidth > 1023 ? 0x01 : 0x00; reg |= (vsync << 1) | (hsync << 2); setvgareg(reg); setreg(VGAControl, reg); setreg(VideoBufferLayoutControl, 0); /* for ilv = 2 */ /* set sync polarities to get proper blanking */ if (vsync) slreg1 |= VSyncLow; if (!hsync) { slreg1 ^= HSyncLow; setreg(VGAControl, reg | 4); } outb(port+SLReg1, slreg1); if ((slreg1 & PixelClk) == 0) { /* smart lock active */ enablecolorkey(1); setreg(VGAControl, getreg(VGAControl) & 6); } else enablecolorkey(0); /* color key initializations */ if ((slreg1 & PixelClk) == 0) setreg(VGAControl, getreg(VGAControl) & 7); /* initialize Bt812 */ for (i = 0; i < sizeof(bt812init); i += 2) setbt812reg(bt812init[i], bt812init[i+1]); /* figure out clock source (Xtal or Oscillator) and revision */ setbt812reg(6, 0x40); reg = getreg(InputFieldPixelBufStatus) & 3; if ((getreg(InputFieldPixelBufStatus) & 3) == reg) { /* crystal - could be revision PP if R34 is installed */ setbt812reg(6, 0x00); reg = inb(port+SLReg1); if (reg & 0x20) { if ((reg & 0xE0) == 0xE0) boardrev = HighQ; else boardrev = RevisionA; } else boardrev = RevisionPP; } else /* revision A or newer with 27 MHz oscillator */ boardrev = RevisionA; /* figure out xtal frequency */ if (xtalfreq == 0) { if (boardrev == RevisionPP) { tuner = (inb(port+SLReg1) >> 6) & 3; if (tuner == 0) /* NTSC */ xtalfreq = 24545400L; else xtalfreq = 29500000L; } else if (boardrev == HighQ) xtalfreq = 29500000L; else xtalfreq = 27000000L; } // print("Hauppage revision %d (xtalfreq %ld)\n", boardrev, xtalfreq); /* on RevPP boards set early sync, on rev A and newer clear it */ if (boardrev == RevisionPP) setreg(InputVideoConfA, getreg(InputVideoConfA) | 4); else setreg(InputVideoConfA, getreg(InputVideoConfA) & ~4); switch (xtalfreq) { case 24545400L: actpixs = 640; break; case 29500000L: actpixs = 768; break; default: actpixs = 720; break; } /* set crop window (these values are for NTSC!) */ if (boardrev == RevisionPP) { Hdesired = xtalfreq / 2; cropleft = (NTSCCropLeft * ((Hdesired / 10))) / 1000000L; cropright = (NTSCCropRight * ((Hdesired / 10))) / 1000000L; } else { cropleft = actpixs / 100; cropright = actpixs - cropleft; } width = ((cropright - cropleft + ilv) / ilv) * ilv; cropright = cropleft + width + 1; croptop = 26; cropbottom = 505; cropwindow(cropleft, cropright, croptop, cropbottom); /* set input format */ setinputformat(NTSC_M); setsvideo(0); } static void panwindow(Point p) { int memmode, ilv, frw; ulong pos; memmode = getreg(MemoryConfReg) & 7; ilv = interleave[memmode]; frw = framewidth[memmode][getreg(VideoBufferLayoutControl) & 3]; pos = (p.y * (frw/ilv)) + ((p.x/ilv) & panmask[memmode]); setreg(DisplayViewPortStartAddrA, (uchar) pos); setreg(DisplayViewPortStartAddrB, (uchar) (pos >> 8)); setreg(DisplayViewPortStartAddrC, (uchar) (pos >> 16) & 0x03); updateshadowregs(); } static int testqfactor(void) { ulong timo; int reg; waitvideoframe(); for (reg = 0, timo = ~0; timo; timo--) { reg |= getreg(InputFieldPixelBufStatus); if (reg & 0xE) break; } if (reg & 0xC) return 0; waitvideoframe(); for (reg = 0, timo = ~0; timo; timo--) { reg |= getreg(InputFieldPixelBufStatus); if (reg & 0xE) break; } return (reg & 0xC) == 0; } static void newwindow(Rectangle r) { unsigned ww, wh, dx, dy, xs, ys, xe, ye; unsigned scalex, scaley; int frwidth, frheight, vidwidth, vidheight; int memmode, layout; int width, height; int filter, changed, val; changed = r.min.x != window.min.x || r.min.y != window.min.y || r.max.x != window.max.x || r.max.y != window.max.y; if (changed) window = r; if (r.min.x < 0) r.min.x = 0; if (r.max.x > ScreenWidth) r.max.x = ScreenWidth; if (r.min.y < 0) r.min.y = 0; if (r.max.y > ScreenHeight) r.max.y = ScreenHeight; if ((dx = r.max.x - r.min.x) <= 0) dx = 1; if ((dy = r.max.y - r.min.y) <= 0) dy = 1; wh = dy; ww = dx = ADJUST(dx); r.min.x = (r.min.x * hrsmult) / hrsdiv; memmode = getreg(MemoryConfReg) & 7; layout = getreg(VideoBufferLayoutControl) & 3; vidwidth = cropright - cropleft + 1; vidheight = (cropbottom & 0x3FE) - (croptop & 0x3FE) + 1; frwidth = min(framewidth[memmode][layout], vidwidth); frheight = min(frameheight[memmode][layout], vidheight); /* round up scale width to nearest multiple of interleave factor */ dx = ((ulong)dx * q) / 100; dx = ilv * ((dx + ilv - 1) / ilv); scalex = (((ulong)dx * 1024L) + vidwidth - 2) / (vidwidth - 1); if (dy > frheight) dy = frheight - 1; scaley = (((ulong)dy * 1024L) + vidheight - 2) / (vidheight - 1); setreg(InputHorzScaleControlA, (scalex << 6) & 0xC0); setreg(InputHorzScaleControlB, (scalex >> 2) & 0xFF); setreg(InputVertScaleControlA, (scaley << 6) & 0xC0); setreg(InputVertScaleControlB, (scaley >> 2) & 0xFF); /* turn on horizontal filtering if we are scaling down */ setreg(InputHorzFilter, horzfilter[((scalex - 1) >> 7) & 7]); /* set vertical interpolation */ filter = scaley > 512 ? (ScreenWidth == 640 ? 0x44 : 0xC5) : 0x46; /* magic */ if ((getreg(InputVertInterpolControl) & 0x1F) != (filter & 0x1F)) { setreg(ISAControl, 0x80); setreg(InputVertInterpolControl, filter & 0x1F); setreg(ISAControl, 0x42); } setreg(AcquisitionControl, ((filter >> 6) ^ 3) | 0x04); /* set viewport position and size */ width = ((ulong)ww * q) / 100; if (width >= frwidth - ilv) width = frwidth - ilv; width = ((width + ilv - 1) / ilv) + 2; height = ((ulong)wh * dy + wh - 1) / wh; if (height >= frheight) height = frheight - 3; height += 2; xs = r.min.x + XCorrection; if (xs < 0) xs = 2; ys = r.min.y + YCorrection; if (ys < 0) ys = 2; if (ScreenWidth > 1023) ys |= 1; setreg(DisplayViewPortWidthA, width); setreg(DisplayViewPortWidthB, width >> 8); setreg(DisplayViewPortHeightA, height); setreg(DisplayViewPortHeightB, height >> 8); setreg(DisplayViewPortOrigTopA, ys); setreg(DisplayViewPortOrigTopB, ys >> 8); setreg(DisplayViewPortOrigLeftA, xs); setreg(DisplayViewPortOrigLeftB, xs >> 8); xe = r.min.x + ww - 1 + XCorrection; if (xe < 0) xe = 2; ye = r.min.y + wh - 1 + YCorrection; if (ye < 0) ye = 2; setreg(DisplayWindowLeftA, xs); setreg(DisplayWindowLeftB, xs >> 8); setreg(DisplayWindowRightA, xe); setreg(DisplayWindowRightB, xe >> 8); setreg(DisplayWindowTopA, ys); setreg(DisplayWindowTopB, ys >> 8); setreg(DisplayWindowBottomA, ye); setreg(DisplayWindowBottomB, ye >> 8); if (dx < ww) { /* horizontal zoom */ int zoom = ((ulong) (dx - 1) * 2048) / ww; setreg(OutputProcControlA, getreg(OutputProcControlA) | 6); setreg(OutputHorzZoomControlA, zoom); setreg(OutputHorzZoomControlB, zoom >> 8); } else setreg(OutputProcControlA, getreg(OutputProcControlA) & 0xF9); if (dy < wh) { /* vertical zoom */ int zoom = ((ulong) (dy - 1) * 2048) / wh; setreg(OutputProcControlB, getreg(OutputProcControlB) | 1); setreg(OutputVertZoomControlA, zoom); setreg(OutputVertZoomControlB, zoom >> 8); } else setreg(OutputProcControlB, getreg(OutputProcControlB) & 0xFE); setreg(OutputProcControlB, getreg(OutputProcControlB) | 0x20); updateshadowregs(); if (changed) { setreg(OutputProcControlA, getreg(OutputProcControlA) & 0xDF); } else { val = getreg(InputFieldPixelBufStatus); USED(val); } panwindow(Pt(0, 0)); } static void createwindow(Rectangle r) { for (q = 100; q >= 30; q -= 10) { newwindow(r); if (testqfactor()) break; } enablevideo(); } static void setcontrols(int index, uchar val) { switch (index) { case Vxp500Brightness: setreg(BrightnessControl, (127L * val) / 100); updateshadowregs(); break; case Vxp500Contrast: setreg(ContrastControl, (15L * val) / 100); updateshadowregs(); break; case Vxp500Saturation: setreg(SaturationControl, (15L * val) / 100); updateshadowregs(); break; case Bt812Brightness: setbt812reg(0x08, ((126L * val) / 100) & 0xFE); break; case Bt812Contrast: setbt812reg(0x09, ((254L * val) / 100) & 0xFE); break; case Bt812Saturation: setbt812reg(0x0A, ((254L * val) / 100) & 0xFE); break; case Bt812Hue: setbt812reg(0x0B, ((254L * val) / 100) & 0xFE); break; case Bt812BW: setbt812reg(0x05, (getbt812reg(0x5) & ~2) | ((val << 1) & 2)); break; } } static void enablememwindow(void) { setreg(MemoryWindowBaseAddrB, getreg(MemoryWindowBaseAddrB) | 0x20); } static void disablememwindow(void) { setreg(MemoryWindowBaseAddrB, getreg(MemoryWindowBaseAddrB) & ~0x20); } volatile static ushort *fb = (ushort *)EISA(MemAddr); static uchar yuvpadbound[] = { 4, 12, 4, 2, 6 }; /* * Capture a frame in UY0, VY1 format */ static void * saveframe(int *nb) { int memmode, layout, ilv; int frwidth, frheight; int bound, n, val, toggle; unsigned save58, save6C; int x, y, w, h, width, height; ulong pos, size; char *p; static void *frame = 0; static ulong framesize = 0; width = capwindow.max.x - capwindow.min.x; height = capwindow.max.y - capwindow.min.y; memmode = getreg(MemoryConfReg) & 7; if (memmode <= 2) { print("devtv: cannot handle YUV411\n"); error(Egreg); /* actually, Eleendert */ } layout = getreg(VideoBufferLayoutControl) & 3; ilv = interleave[memmode]; frwidth = framewidth[memmode][layout]; frheight = frameheight[memmode][layout]; pos = getreg(AcquisitionAddrA) + (getreg(AcquisitionAddrB) << 8) + (getreg(AcquisitionAddrC) & 3) << 16; x = capwindow.min.x + (pos % frwidth); y = capwindow.min.y + (pos / frwidth); if (x > frwidth || y > frheight) return 0; if (x + width > frwidth) width = frwidth - x; if (y + height > frheight) height = frheight - y; pos = y * (frwidth / ilv) + (x / ilv); /* compute padding for each scan line */ bound = yuvpadbound[memmode]; switch (bound) { case 2: width = (width + 1) & ~1; break; case 4: width = (width + 3) & ~3; break; default: width = (width + (bound - 1)) / bound; break; } size = width * height * sizeof(ushort); if (size != framesize) { framesize = 0; if (frame) free(frame); frame = malloc(size + 256); } if (frame == 0) return 0; memset(frame, 0, size + 256); framesize = size; p = (char *) frame + snprint(frame, 256, "TYPE=ccir601\nWINDOW=%d %d %d %d\n\n", capwindow.min.x, capwindow.min.y, capwindow.min.x+width, capwindow.min.y+height); freeze(1); save58 = getreg(InputVertInterpolControl); save6C = getreg(AcquisitionControl); waitforretrace(); setreg(ISAControl, 0xC0); /* global reset */ setreg(InputVertInterpolControl, 0x0D); setreg(AcquisitionControl, 0x04); setreg(CaptureControl, 0x80); /* set capture mode */ setreg(VideoInputFrameBufDepthA, 0xFF); setreg(VideoInputFrameBufDepthB, 0x03); setreg(InputVideoConfA, getreg(InputVideoConfA) | 0x40); setreg(ISAControl, 0x44); /* tight decode, global reset off */ setreg(CaptureViewPortAddrA, (int) pos & 0xFF); setreg(CaptureViewPortAddrB, (int) (pos >> 8) & 0xFF); setreg(CaptureViewPortAddrC, (int) (pos >> 16) & 0x03); n = (width / ilv) - 1; setreg(CaptureViewPortWidthA, n & 0xFF); setreg(CaptureViewPortWidthB, n >> 8); setreg(CaptureViewPortHeightA, (height-1) & 0xFF); setreg(CaptureViewPortHeightB, (height-1) >> 8); setreg(CapturePixelBufLow, 0x04); /* pix buffer low */ setreg(CapturePixelBufHigh, 0x0E); /* pix buffer high */ setreg(CaptureMultiBufDepthA, 0xFF); /* multi buffer depth maximum */ setreg(CaptureMultiBufDepthB, 0x03); updateshadowregs(); setreg(CaptureControl, 0x90); /* capture reset */ val = getreg(InputFieldPixelBufStatus); /* clear read status */ USED(val); toggle = !(getreg(OutputProcControlA) & 0x01) ? 0x8000 : 0x0000; setreg(CaptureControl, 0xC0); /* capture enable, active */ while ((getreg(InputFieldPixelBufStatus) & 0x10) == 0) /* wait for capture FIFO to become ready */; enablememwindow(); for (h = height; h > 0; h--) { for (w = width; w > 0; w -= 2) { ushort uy0 = swab16(fb[0]) ^ toggle; ushort vy1 = swab16(fb[1]) ^ toggle; /* unfortunately p may not be properly aligned */ *p++ = uy0 >> 8; *p++ = uy0; *p++ = vy1 >> 8; *p++ = vy1; } } disablememwindow(); waitforretrace(); setreg(ISAControl, 0xC0); /* global reset */ setreg(CaptureControl, 0); /* clear capture mode */ setreg(InputVertInterpolControl, save58); setreg(AcquisitionControl, save6C); setreg(InputVideoConfA, getreg(InputVideoConfA) | 0x40); setreg(ISAControl, 0x40); /* clear global reset */ updateshadowregs(); freeze(0); *nb = p - (char *) frame; return frame; }