/* * Copyright (c) 1995 Mark Tinguely and Jim Lowe * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Mark Tinguely and Jim Lowe * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* Change History: 8/21/95 Release 8/23/95 On advice from Stefan Esser, added volatile to PCI memory pointers to remove PCI caching . 8/29/95 Fixes suggested by Bruce Evans. meteor_mmap should return -1 on error rather than 0. unit # > NMETEOR should be unit # >= NMETEOR. 10/24/95 Turn 50 Hz processing for SECAM and 60 Hz processing off for AUTOMODE. 11/11/95 Change UV from always begin signed to ioctl selected to either signed or unsigned. 12/07/95 Changed 7196 startup codes for 50 Hz as recommended by Luigi Rizzo (luigi@iet.unipi.it) 12/08/95 Clear SECAM bit in PAL/NTSC and set input field count bits for 50 Hz mode (PAL/SECAM) before I was setting the output count bits. by Luigi Rizzo (luigi@iet.unipi.it) 12/18/95 Correct odd DMA field (never exceed, but good for safety Changed 7196 startup codes for 50 Hz as recommended by Luigi Rizzo (luigi@iet.unipi.it) 12/19/95 Changed field toggle mode to enable (offset 0x3c) recommended by luigi@iet.unipi.it Added in prototyping, include file, staticizing, and DEVFS changes from FreeBSD team. Changed the default allocated pages from 151 (NTSC) to 217 (PAL). Cleaned up some old comments in iic_write(). Added a Field (even or odd) only capture mode to eliminate the high frequency problems with compression algorithms. Recommended by luigi@iet.unipi.it. Changed geometry ioctl so if it couldn't allocated a large enough contiguous space, it wouldn't free the stuff it already had. Added new mode called YUV_422 which delivers the data in planer Y followed by U followed by V. This differs from the standard YUV_PACKED mode in that the chrominance (UV) data is in the correct (different) order. This is for programs like vic and mpeg_encode so they don't have to reorder the chrominance data. Added field count to stats. Increment frame count stat if capturing continuous on even frame grabs. Added my email address to these comments (james@cs.uwm.edu) suggested by (luigi@iet.unipt.it :-). Changed the user mode signal mechanism to allow the user program to be interrupted at the end of a frame in any one of the modes. Added SSIGNAL ioctl. Added a SFPS/GFPS ioctl so one may set the frames per second that the card catpures. This code needs to be completed. Changed the interrupt routine so synchronous capture will work on fields or frames and the starting frame can be either even or odd. Added HALT_N_FRAMES and CONT_N_FRAMES so one could stop and continue synchronous capture mode. Change the tsleep/wakeup function to wait on mtr rather than &read_intr_wait. 1/22/96 Add option (METEOR_FreeBSD_210) for FreeBSD 2.1 to compile. Changed intr so it only printed errors every 50 times. Added unit number to error messages. Added get_meteor_mem and enabled range checking. 1/30/96 Added prelim test stuff for direct video dma transfers from Amancio Hasty (hasty@rah.star-gate.com). Until we get some stuff sorted out, this will be ifdef'ed with METEOR_DIRECT_VIDEO. This is very dangerous to use at present since we don't check the address that is passed by the user!!!!! 2/26/96 Added special SVIDEO input device type. 2/27/96 Added meteor_reg.h file and associate types Converted meteor.c over to using meteor.h file. Prompted by Lars Jonas Olsson . 2/28/96 Added meteor RGB code from Lars Jonas Olsson . I make some mods to this code, so I hope it still works as I don't have an rgb card to test with. 2/29/96 tested the meteor RGB and supplied me with diffs. Thanks, we now have a working RGB version of the driver. Still need to clean up this code. 3/1/96 Fixed a nasty little bug that was clearing the VTR mode bit when the 7196 status was requested. 3/15/96 Fixed bug introduced in previous version that stopped the only fields mode from working. Added METEOR{GS}TS ioctl, still needs work. 3/25/96 Added YUV_9 and YUV_12 modes. Cleaned up some of the code and converted variables to use the new register types. 4/8/96 Fixed the a bug in with the range enable. Pointed out by Jim Bray. 5/13/96 Fix the FPS ioctl so it actually sets the frames per second. Code supplied by ian@robots.ox.ac.uk. The new code implements a new define: METEOR_SYSTEM_DEFAULT which should be defined as METEOR_PAL, METEOR_SECAM, or METEOR_NTSC in your system configuration file. If METEOR_SYSTEM_DEFAULT isn't defined, and there is not a signal when set_fps is called, then the call has no effect. Changed the spelling of PLANER to PLANAR as pointed out by Paco Hope and define PLANER to be PLANAR for backward compatibility. 5/28/95 METEOR_INPUT_DEV_RCA -> METEOR_INPUT_DEV0, not METEOR_GEO_DEV0. Pointed out by Ian Reid, . METEOR_DEV_MASK should be 0x0000f000 and not 0x2000f000, otherwise METEOR_RGB gets masked out. Pointed out by Ian Reid. Changed the fps code to give even distribution for low frame rates. Code supplied by Ian Reid. Fix some problems with the RGB version. Patch supplied by . Added METEOR_FIELD_MODE to include files for a future version of this driver. */ #include "meteor.h" #include "opt_meteor.h" #include #include #include #include #include #include #include #if defined(METEOR_FreeBSD_210) #include /* bootverbose */ #endif #include #include #include #include #include #include #include #include static void meteor_intr __P((void *arg)); /* * Allocate enough memory for: * 768x576 RGB 16 or YUV (16 storage bits/pixel) = 884736 = 216 pages * * You may override this using the options "METEOR_ALLOC_PAGES=value" in your * kernel configuration file. */ #ifndef METEOR_ALLOC_PAGES #define METEOR_ALLOC_PAGES 217 #endif #define METEOR_ALLOC (METEOR_ALLOC_PAGES * PAGE_SIZE) static meteor_reg_t meteor[NMETEOR]; #define METEOR_NUM(mtr) ((mtr - &meteor[0])/sizeof(meteor_reg_t)) #define METPRI (PZERO+8)|PCATCH static const char* met_probe (pcici_t tag, pcidi_t type); static void met_attach(pcici_t tag, int unit); static u_long met_count; static struct pci_device met_device = { "meteor", met_probe, met_attach, &met_count }; COMPAT_PCI_DRIVER (meteor, met_device); #if defined(METEOR_FreeBSD_210) /* XXX */ d_open_t meteor_open; d_close_t meteor_close; d_read_t meteor_read; d_write_t meteor_write; d_ioctl_t meteor_ioctl; d_mmap_t meteor_mmap; #else static d_open_t meteor_open; static d_close_t meteor_close; static d_read_t meteor_read; static d_write_t meteor_write; static d_ioctl_t meteor_ioctl; static d_mmap_t meteor_mmap; #define CDEV_MAJOR 67 static struct cdevsw meteor_cdevsw = { /* open */ meteor_open, /* close */ meteor_close, /* read */ meteor_read, /* write */ meteor_write, /* ioctl */ meteor_ioctl, /* stop */ nostop, /* reset */ noreset, /* devtotty */ nodevtotty, /* poll */ nopoll, /* mmap */ meteor_mmap, /* strategy */ nostrategy, /* name */ "meteor", /* parms */ noparms, /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* maxio */ 0, /* bmaj */ -1 }; #endif static mreg_t saa7116_pci_default[sizeof(struct saa7116_regs)/sizeof(mreg_t)]={ /* PCI Memory registers */ /* BITS Type Description */ /* 0x00 */ 0x00000000, /* 31:1 e*RW DMA 1 (Even) 0 RO 0x0 */ /* 0x04 */ 0x00000000, /* 31:2 e*RW DMA 2 (Even) 1:0 RO 0x0 */ /* 0x08 */ 0x00000000, /* 31:2 e*RW DMA 3 (Even) 1:0 RO 0x0 */ /* 0x0c */ 0x00000000, /* 31:1 o*RW DMA 1 (Odd) 0 RO 0x0 */ /* 0x10 */ 0x00000000, /* 31:2 o*RW DMA 2 (Odd) 1:0 RO 0x0 */ /* 0x14 */ 0x00000000, /* 31:2 o*RW DMA 3 (Odd) 1:0 RO 0x0 */ /* 0x18 */ 0x00000500, /* 15:2 e*RW Stride 1 (Even) 1:0 RO 0x0 */ /* 0x1c */ 0x00000000, /* 15:2 e*RW Stride 2 (Even) 1:0 RO 0x0 */ /* 0x20 */ 0x00000000, /* 15:2 e*RW Stride 3 (Even) 1:0 RO 0x0 */ /* 0x24 */ 0x00000500, /* 15:2 o*RW Stride 1 (Odd) 1:0 RO 0x0 */ /* 0x28 */ 0x00000000, /* 15:2 o*RW Stride 2 (Odd) 1:0 RO 0x0 */ /* 0x2c */ 0x00000000, /* 15:2 o*RW Stride 3 (Odd) 1:0 RO 0x0 */ /* 0x30 */ 0xeeeeee01, /* 31:8 *RW Route (Even) 7:0 *RW Mode (Even) */ /* 0x34 */ 0xeeeeee01, /* 31:8 *RW Route (Odd) 7:0 *RW Mode (Odd) */ /* 0x38 */ 0x00200020, /* 22:16 *RW FIFO Trigger Planer Mode, 6:0 *RW FIFO Trigger Packed Mode */ /* 0x3c */ 0x00000107, /* 9:8 *RW Reserved (0x0) 2 *RW Field Toggle 1 *RW Reserved (0x1) 0 *RW Reserved (0x1) */ /* 0x40 */ 0x000000c0, /* 15 *RW Range Enable 14 *RW Corrupt Disable 11 *RR Address Error (Odd) 10 *RR Address Error (Even) 9 *RR Field Corrupt (Odd) 8 *RR Field Corrupt (Even) 7 *RW Fifo Enable 6 *RW VRSTN# 5 *RR Field Done (Odd) 4 *RR Field Done (Even) 3 *RS Single Field Capture (Odd) 2 *RS Single Field Capture (Even) 1 *RW Capture (ODD) Continous 0 *RW Capture (Even) Continous */ /* 0x44 */ 0x00000000, /* 7:0 *RW Retry Wait Counter */ /* 0x48 */ 0x00000307, /* 10 *RW Interrupt mask, start of field 9 *RW Interrupt mask, end odd field 8 *RW Interrupt mask, end even field 2 *RR Interrupt status, start of field 1 *RR Interrupt status, end of odd 0 *RR Interrupt status, end of even */ /* 0x4c */ 0x00000001, /* 31:0 *RW Field Mask (Even) continous */ /* 0x50 */ 0x00000001, /* 31:0 *RW Field Mask (Odd) continous */ /* 0x54 */ 0x00000000, /* 20:16 *RW Mask Length (Odd) 4:0 *RW Mask Length (Even) */ /* 0x58 */ 0x0005007c, /* 22:16 *RW FIFO almost empty 6:0 *RW FIFO almost full */ /* 0x5c */ 0x461e1e0f, /* 31:24 *RW I2C Phase 4 23:16 *RW I2C Phase 3 15:8 *RW I2C Phase 2 7:0 *RW I2C Phase 1 */ /* 0x60 */ 0x00000300, /* 31:24 *RO I2C Read Data 23:16 **RW I2C Auto Address 11 RO I2C SCL Input 10 RO I2C SDA Input 9 RR I2C Direct Abort 8 RR I2C Auto Abort 3 RW I2C SCL Output 2 RW I2C SDA Output 1 RW I2C Bypass 0 RW I2C Auto Enable */ /* 0x64 */ 0x00000000, /* 24 RS I2C New Cycle 23:16 **RW I2C Direct Address 15:8 **RW I2C Direct Sub-address 7:0 **RW I2C Direct Write Address */ /* 0x68 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 1 (Even) 23:16 **RW I2C Auto Data 1 (Even) 15:8 **RW I2C Auto Sub-address 0 (Even) 7:0 **RW I2C Auto Data 0 (Even) */ /* 0x6c */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 3 (Even) 23:16 **RW I2C Auto Data 3 (Even) 15:8 **RW I2C Auto Sub-address 2 (Even) 7:0 **RW I2C Auto Data 2 (Even) */ /* 0x70 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 5 (Even) 23:16 **RW I2C Auto Data 5 (Even) 15:8 **RW I2C Auto Sub-address 4 (Even) 7:0 **RW I2C Auto Data 4 (Even) */ /* 0x74 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 7 (Even) 23:16 **RW I2C Auto Data 7 (Even) 15:8 **RW I2C Auto Sub-address 6 (Even) 7:0 **RW I2C Auto Data 6 (Even) */ /* 0x78 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 1 (Odd) 23:16 **RW I2C Auto Data 1 (Odd) 15:8 **RW I2C Auto Sub-address 0 (Odd) 7:0 **RW I2C Auto Data 0 (Odd) */ /* 0x7c */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 3 (Odd) 23:16 **RW I2C Auto Data 3 (Odd) 15:8 **RW I2C Auto Sub-address 2 (Odd) 7:0 **RW I2C Auto Data 2 (Odd) */ /* 0x80 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 5 (Odd) 23:16 **RW I2C Auto Data 5 (Odd) 15:8 **RW I2C Auto Sub-address 4 (Odd) 7:0 **RW I2C Auto Data 4 (Odd) */ /* 0x84 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 7 (Odd) 23:16 **RW I2C Auto Data 7 (Odd) 15:8 **RW I2C Auto Sub-address 6 (Odd) 7:0 **RW I2C Auto Data 6 (Odd) */ /* 0x88 */ 0x00000000, /* 23:16 **RW I2C Register Enable (Odd) 7:0 **RW I2C Register Enable (Even) */ /* 0x8c */ 0x00000000, /* 23:2 e*RW DMA End (Even) 1:0 RO 0x0 */ /* 0x90 */ 0x00000000 /* 23:2 e*RW DMA End (Odd) 1:0 RO 0x0 */ }; static u_char saa7196_i2c_default[NUM_SAA7196_I2C_REGS] = { /* SAA7196 I2C bus control */ /* BITS Function */ /* 00 */ 0x50, /* 7:0 Increment Delay */ /* 01 */ 0x30, /* 7:0 Horizontal Sync Begin for 50hz */ /* 02 */ 0x00, /* 7:0 Horizontal Sync Stop for 50hz */ /* 03 */ 0xe8, /* 7:0 Horizontal Sync Clamp Start for 50hz */ /* 04 */ 0xb6, /* 7:0 Horizontal Sync Clamp Stop for 50hz */ /* 05 */ 0xf4, /* 7:0 Horizontal Sync Start after PH1 for 50hz */ /* 06 */ 0x46, /* 7 Input mode =0 CVBS, =1 S-Video 6 Pre filter 5:4 Aperture Bandpass characteristics 3:2 Coring range for high freq 1:0 Aperture bandpass filter weights */ /* 07 */ 0x00, /* 7:0 Hue */ /* 08 */ 0x7f, /* 7:3 Colour-killer threshold QAM (PAL, NTSC) */ /* 09 */ 0x7f, /* 7:3 Colour-killer threshold SECAM */ /* 0a */ 0x7f, /* 7:0 PAL switch sensitivity */ /* 0b */ 0x7f, /* 7:0 SECAM switch sensitivity */ /* 0c */ 0x40, /* 7 Colour-on bit 6:5 AGC filter */ /* 0d */ 0x84, /* 7 VTR/TV mode bit = 1->VTR mode 3 Realtime output mode select bit 2 HREF position select 1 Status byte select 0 SECAM mode bit */ /* 0e */ 0x38, /* 7 Horizontal clock PLL 5 Select interal/external clock source 4 Output enable of Horizontal/Vertical sync 3 Data output YUV enable 2 S-VHS bit 1 GPSW2 0 GPSW1 */ /* 0f */ 0x50, /* 7 Automatic Field detection 6 Field Select 0 = 50hz, 1=60hz 5 SECAM cross-colour reduction 4 Enable sync and clamping pulse 3:1 Luminance delay compensation */ /* 10 */ 0x00, /* 2 Select HREF Position 1:0 Vertical noise reduction */ /* 11 */ 0x2c, /* 7:0 Chrominance gain conrtol for QAM */ /* 12 */ 0x40, /* 7:0 Chrominance saturation control for VRAM port */ /* 13 */ 0x40, /* 7:0 Luminance contract control for VRAM port */ /* 14 */ 0x34, /* 7:0 Horizontal sync begin for 60hz */ #ifdef notdef /* 15 */ 0x0c, /* 7:0 Horizontal sync stop for 60hz */ /* 16 */ 0xfb, /* 7:0 Horizontal clamp begin for 60hz */ /* 17 */ 0xd4, /* 7:0 Horizontal clamp stop for 60hz */ /* 18 */ 0xec, /* 7:0 Horizontal sync start after PH1 for 60hz */ #else 0x0a, 0xf4, 0xce, 0xf4, #endif /* 19 */ 0x80, /* 7:0 Luminance brightness control for VRAM port */ /* 1a */ 0x00, /* 1b */ 0x00, /* 1c */ 0x00, /* 1d */ 0x00, /* 1e */ 0x00, /* 1f */ 0x00, /* 20 */ 0x90, /* 7 ROM table bypass switch 6:5 Set output field mode 4 VRAM port outputs enable 3:2 First pixel position in VRO data 1:0 FIFO output register select */ /* 21 */ 0x80, /* 7:0 [7:0] Pixel number per line on output */ /* 22 */ 0x80, /* 7:0 [7:0] Pixel number per line on input */ /* 23 */ 0x03, /* 7:0 [7:0] Horizontal start position of scaling win*/ /* 24 */ 0x8a, /* 7:5 Horizontal decimation filter 4 [8] Horizontal start position of scaling win 3:2 [9:8] Pixel number per line on input 1:0 [9:8] Pixel number per line on output */ /* 25 */ 0xf0, /* 7:0 [7:0] Line number per output field */ /* 26 */ 0xf0, /* 7:0 [7:0] Line number per input field */ /* 27 */ 0x0f, /* 7:0 [7:0] Vertical start of scaling window */ /* 28 */ 0x80, /* 7 Adaptive filter switch 6:5 Vertical luminance data processing 4 [8] Vertical start of scaling window 3:2 [9:8] Line number per input field 1:0 [9:8] Line number per output field */ /* 29 */ 0x16, /* 7:0 [7:0] Vertical bypass start */ /* 2a */ 0x00, /* 7:0 [7:0] Vertical bypass count */ /* 2b */ 0x00, /* 4 [8] Vertical bypass start 2 [8] Vertical bypass count 0 Polarity, internally detected odd even flag */ /* 2c */ 0x80, /* 7:0 Set lower limit V for colour-keying */ /* 2d */ 0x7f, /* 7:0 Set upper limit V for colour-keying */ /* 2e */ 0x80, /* 7:0 Set lower limit U for colour-keying */ /* 2f */ 0x7f, /* 7:0 Set upper limit U for colour-keying */ /* 30 */ 0xbf /* 7 VRAM bus output format 6 Adaptive geometrical filter 5 Luminance limiting value 4 Monochrome and two's complement output data sel 3 Line quailifier flag 2 Pixel qualifier flag 1 Transparent data transfer 0 Extended formats enable bit */ }; static u_char bt254_default[NUM_BT254_REGS] = { 0x00, /* 24 bpp */ 0xa0, 0xa0, 0xa0, 0x50, 0x50, 0x50, } ; /* * i2c_write: * Returns 0 Succesful completion. * Returns 1 If transfer aborted or timeout occured. * */ static int i2c_print_err = 1; static int i2c_write(meteor_reg_t * mtr, u_char slave, u_char rw, u_char reg, u_char data) { register unsigned long wait_counter = 0x0001ffff; register mreg_t * iic_write_loc = &mtr->base->i2c_write; register int err = 0; /* Write the data the the i2c write register */ *iic_write_loc = SAA7116_IIC_NEW_CYCLE | (((u_long)slave|(u_long)rw) << 16) | ((u_long)reg << 8) | (u_long)data; /* Wait until the i2c cycle is compeleted */ while((*iic_write_loc & SAA7116_IIC_NEW_CYCLE)) { if(!wait_counter) break; wait_counter--; } /* 1ffff should be enough delay time for the i2c cycle to complete */ if(!wait_counter) { if(i2c_print_err) printf("meteor%d: %d i2c %s transfer timeout 0x%x", METEOR_NUM(mtr), slave, rw ? "read" : "write", *iic_write_loc); err=1; } /* Check for error on direct write, clear if any */ if(mtr->base->i2c_read & SAA7116_IIC_DIRECT_TRANSFER_ABORTED){ mtr->base->i2c_read |= SAA7116_IIC_DIRECT_TRANSFER_ABORTED; if(i2c_print_err) printf("meteor%d: 0x%x i2c %s tranfer aborted", METEOR_NUM(mtr), slave, rw ? "read" : "write" ); err= 1; } if(err) { if(i2c_print_err) printf(" - reg=0x%x, value=0x%x.\n", reg, data); } return err; } #undef i2c_print static const char * met_probe (pcici_t tag, pcidi_t type) { static int once; if (!once++) cdevsw_add(&meteor_cdevsw); switch (type) { case SAA7116_PHILIPS_ID: /* meteor */ return("Philips SAA 7116"); }; return ((char *)0); } /* interrupt handling routine complete meteor_read() if using interrupts */ static void meteor_intr(void *arg) { meteor_reg_t *mtr = (meteor_reg_t *) arg; mreg_t *cap = &mtr->base->cap_cntl, *base = &mtr->base->dma1e, *stat = &mtr->base->irq_stat; u_long status = *stat, cap_err = *cap & 0x00000f00, #ifdef METEOR_CHECK_PCI_BUS pci_err = pci_conf_read(mtr->tag, PCI_COMMAND_STATUS_REG), #endif next_base = (u_long)(vtophys(mtr->bigbuf)); /* * Disable future interrupts if a capture mode is not selected. * This can happen when we are in the process of closing or * changing capture modes, otherwise it shouldn't happen. */ if(!(mtr->flags & METEOR_CAP_MASK)) { *cap &= 0x8ff0; /* disable future interrupts */ } #ifdef METEOR_CHECK_PCI_BUS /* * Check for pci bus errors. */ #define METEOR_MASTER_ABORT 0x20000000 #define METEOR_TARGET_ABORT 0x10000000 if(pci_err & METEOR_MASTER_ABORT) { printf("meteor%d: intr: pci bus master dma abort: 0x%x 0x%x.\n", METEOR_NUM(mtr), *base, *(base+3)); pci_conf_write(mtr->tag, PCI_COMMAND_STATUS_REG, pci_err); } if(pci_err & METEOR_TARGET_ABORT) { printf("meteor%d: intr: pci bus target dma abort: 0x%x 0x%x.\n", METEOR_NUM(mtr), *base, *(base+3)); pci_conf_write(mtr->tag, PCI_COMMAND_STATUS_REG, pci_err); } #endif /* * Check for errors. */ if (cap_err) { if (cap_err & 0x300) { if(mtr->fifo_errors % 50 == 0) { printf("meteor%d: capture error", METEOR_NUM(mtr)); printf(": %s FIFO overflow.\n", cap_err&0x0100? "even" : "odd"); } mtr->fifo_errors++ ; /* increment fifo capture errors cnt */ } if (cap_err & 0xc00) { if(mtr->dma_errors % 50 == 0) { printf("meteor%d: capture error", METEOR_NUM(mtr)); printf(": %s DMA address.\n", cap_err&0x0400? "even" : "odd"); } mtr->dma_errors++ ; /* increment DMA capture errors cnt */ } } *cap |= 0x0f30; /* clear error and field done */ /* * In synchronous capture mode we need to know what the address * offset for the next field/frame will be. next_base holds the * value for the even dma buffers (for odd, one must add stride). */ if((mtr->flags & METEOR_SYNCAP) && !mtr->synch_wait && (mtr->current < mtr->frames)) { /* could be !=, but < is safer */ /* next_base is initialized to mtr->bigbuf */ next_base += mtr->frame_size * mtr->current; if(mtr->flags & METEOR_WANT_TS) next_base += sizeof(struct timeval) * mtr->current; } /* * Count the field and clear the field flag. * * In single mode capture, clear the continuous capture mode. * * In synchronous capture mode, if we have room for another field, * adjust DMA buffer pointers. * When we are above the hi water mark (hiwat), mtr->synch_wait will * be set and we will not bump the DMA buffer pointers. Thus, once * we reach the hi water mark, the driver acts like a continuous mode * capture on the mtr->current frame until we hit the low water * mark (lowat). The user had the option of stopping or halting * the capture if this is not the desired effect. */ if (status & 0x1) { /* even field */ mtr->even_fields_captured++; mtr->flags &= ~METEOR_WANT_EVEN; if((mtr->flags & METEOR_SYNCAP) && !mtr->synch_wait) { *base = next_base; /* XXX should add adjustments for YUV_422 & PLANAR */ } /* * If the user requested to be notified via signal, * let them know the field is complete. */ if(mtr->proc && (mtr->signal & METEOR_SIG_MODE_MASK)) psignal(mtr->proc, mtr->signal&(~METEOR_SIG_MODE_MASK)); } if (status & 0x2) { /* odd field */ mtr->odd_fields_captured++; mtr->flags &= ~METEOR_WANT_ODD; if((mtr->flags & METEOR_SYNCAP) && !mtr->synch_wait) { *(base+3) = next_base + *(base+6); /* XXX should add adjustments for YUV_422 & PLANAR */ } /* * If the user requested to be notified via signal, * let them know the field is complete. */ if(mtr->proc && (mtr->signal & METEOR_SIG_MODE_MASK)) psignal(mtr->proc, mtr->signal&(~METEOR_SIG_MODE_MASK)); } /* * If we have a complete frame. */ if(!(mtr->flags & METEOR_WANT_MASK)) { mtr->frames_captured++; /* * post the completion time. */ if(mtr->flags & METEOR_WANT_TS) { struct timeval *ts; if(mtr->alloc_pages * PAGE_SIZE <= (mtr->frame_size + sizeof(struct timeval))) { ts =(struct timeval *)mtr->bigbuf + mtr->frame_size; /* doesn't work in synch mode except for first frame */ /* XXX */ microtime(ts); } } /* * Wake up the user in single capture mode. */ if(mtr->flags & METEOR_SINGLE) wakeup((caddr_t)mtr); /* * If the user requested to be notified via signal, * let them know the frame is complete. */ if(mtr->proc && !(mtr->signal & METEOR_SIG_MODE_MASK)) psignal(mtr->proc, mtr->signal&(~METEOR_SIG_MODE_MASK)); /* * Reset the want flags if in continuous or * synchronous capture mode. */ if(mtr->flags & (METEOR_CONTIN|METEOR_SYNCAP)) { switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { case METEOR_ONLY_ODD_FIELDS: mtr->flags |= METEOR_WANT_ODD; break; case METEOR_ONLY_EVEN_FIELDS: mtr->flags |= METEOR_WANT_EVEN; break; default: mtr->flags |= METEOR_WANT_MASK; break; } } /* * Special handling for synchronous capture mode. */ if(mtr->flags & METEOR_SYNCAP) { struct meteor_mem *mm = mtr->mem; /* * Mark the current frame as active. It is up to * the user to clear this, but we will clear it * for the user for the current frame being captured * if we are within the water marks (see below). */ mm->active |= 1 << (mtr->current - 1); /* * Since the user can muck with these values, we need * to check and see if they are sane. If they don't * pass the sanity check, disable the capture mode. * This is rather rude, but then so was the user. * * Do we really need all of this or should we just * eliminate the possiblity of allowing the * user to change hi and lo water marks while it * is running? XXX */ if(mm->num_active_bufs < 0 || mm->num_active_bufs > mtr->frames || mm->lowat < 1 || mm->lowat >= mtr->frames || mm->hiwat < 1 || mm->hiwat >= mtr->frames || mm->lowat > mm->hiwat ) { *cap &= 0x8ff0; mtr->flags &= ~(METEOR_SYNCAP|METEOR_WANT_MASK); } else { /* * Ok, they are sane, now we want to * check the water marks. */ if(mm->num_active_bufs <= mm->lowat) mtr->synch_wait = 0; if(mm->num_active_bufs >= mm->hiwat) mtr->synch_wait = 1; /* * Clear the active frame bit for this frame * and advance the counters if we are within * the banks of the water marks. */ if(!mtr->synch_wait) { mm->active &= ~(1 << mtr->current); mtr->current++; if(mtr->current > mtr->frames) mtr->current = 1; mm->num_active_bufs++; } } } } *stat |= 0x7; /* clear interrupt status */ return; } static void set_fps(meteor_reg_t *mtr, u_short fps) { struct saa7116_regs *s7116 = mtr->base; unsigned status; unsigned maxfps, mask = 0x1, length = 0; SAA7196_WRITE(mtr, SAA7196_STDC, SAA7196_REG(mtr, SAA7196_STDC) | 0x02); SAA7196_READ(mtr); status = (s7116->i2c_read & 0xff000000L) >> 24; /* * Determine if there is an input signal. Depending on the * frequency we either have a max of 25 fps (50 hz) or 30 fps (60 hz). * If there is no input signal, then we need some defaults. If the * user neglected to specify any defaults, just set to the fps to max. */ if((status & 0x40) == 0) { /* Is there a signal ? */ if(status & 0x20) { maxfps = 30; /* 60 hz system */ } else { maxfps = 25; /* 50 hz system */ } } else { /* We have no signal, check defaults */ #if METEOR_SYSTEM_DEFAULT == METEOR_PAL || METEOR_SYSTEM_DEFAULT == METEOR_SECAM maxfps = 25; #elif METEOR_SYSTEM_DEFAULT == METEOR_NTSC maxfps = 30; #else /* Don't really know what to do, just set max */ maxfps = 30; fps = 30; #endif } /* * A little sanity checking... */ if(fps < 1) fps = 1; if(fps > maxfps) fps = maxfps; /* * Compute the mask/length using the fps. */ if(fps == maxfps) { mask = 0x1; length = 0x0; } else if ((float)fps == maxfps/2.0) { mask = 0x1; length = 0x1; } else if (fps > maxfps/2) { float step, b; mask = (1<fme = s7116->fmo = mask; s7116->fml = (length << 16) | length;; mtr->fps = fps; return; } /* * There is also a problem with range checking on the 7116. * It seems to only work for 22 bits, so the max size we can allocate * is 22 bits long or 4194304 bytes assuming that we put the beginning * of the buffer on a 2^24 bit boundary. The range registers will use * the top 8 bits of the dma start registers along with the bottom 22 * bits of the range register to determine if we go out of range. * This makes getting memory a real kludge. * */ #define RANGE_BOUNDARY (1<<22) static vm_offset_t get_meteor_mem(int unit, unsigned size) { vm_offset_t addr = 0; addr = vm_page_alloc_contig(size, 0x100000, 0xffffffff, 1<<24); if(addr == 0) addr = vm_page_alloc_contig(size, 0x100000, 0xffffffff, PAGE_SIZE); if(addr == 0) { printf("meteor%d: Unable to allocate %d bytes of memory.\n", unit, size); } return addr; } static void bt254_write(meteor_reg_t *mtr, u_char addr, u_char data) { addr &= 0x7; /* sanity? */ mtr->bt254_reg[addr] = data; PCF8574_DATA_WRITE(mtr, data); /* set data */ PCF8574_CTRL_WRITE(mtr, (PCF8574_CTRL_REG(mtr) & ~0x7) | addr); PCF8574_CTRL_WRITE(mtr, PCF8574_CTRL_REG(mtr) & ~0x10); /* WR/ to 0 */ PCF8574_CTRL_WRITE(mtr, PCF8574_CTRL_REG(mtr) | 0x10); /* WR to 1 */ PCF8574_DATA_WRITE(mtr, 0xff); /* clr data */ } static void bt254_init(meteor_reg_t *mtr) { int i; PCF8574_CTRL_WRITE(mtr, 0x7f); PCF8574_DATA_WRITE(mtr, 0xff); /* data port must be 0xff */ PCF8574_CTRL_WRITE(mtr, 0x7f); /* init RGB module for 24bpp, composite input */ for(i=0; ibase->cap_cntl = 0x00000040L; vbase_addr = &mtr->base->dma1e; for (i = 0 ; i < (sizeof(struct saa7116_regs)/sizeof(mreg_t)); i++) *vbase_addr++ = saa7116_pci_default[i]; /* * Check for the Philips SAA7196 */ i2c_print_err = 0; if(i2c_write(mtr, SAA7196_I2C_ADDR, SAA7116_I2C_WRITE, 0, 0xff) == 0) { i2c_print_err = 1; /* * Initialize 7196 */ for (i = 0; i < NUM_SAA7196_I2C_REGS; i++) SAA7196_WRITE(mtr, i, saa7196_i2c_default[i]); /* * Get version number. */ SAA7196_WRITE(mtr, SAA7196_STDC, SAA7196_REG(mtr, SAA7196_STDC) & ~0x02); SAA7196_READ(mtr); printf("meteor%d: rev 0x%x\n", METEOR_NUM(mtr), (unsigned)((mtr->base->i2c_read & 0xff000000L) >> 28)); } else { i2c_print_err = 1; printf("meteor%d: \n", METEOR_NUM(mtr)); } /* * Check for RGB module, initialized if found. */ i2c_print_err = 0; if(i2c_write(mtr,PCF8574_DATA_I2C_ADDR,SAA7116_I2C_WRITE,0,0xff) == 0) { i2c_print_err = 1; printf("meteor%d: \n", METEOR_NUM(mtr)); /* does this have a rev #? */ bt254_init(mtr); /* Set up RGB module */ mtr->flags = METEOR_RGB; } else { i2c_print_err = 1; mtr->flags = 0; } set_fps(mtr, 30); } static void met_attach(pcici_t tag, int unit) { #ifdef METEOR_IRQ u_long old_irq, new_irq; #endif METEOR_IRQ meteor_reg_t *mtr; vm_offset_t buf; u_long latency; if (unit >= NMETEOR) { printf("meteor%d: attach: only %d units configured.\n", unit, NMETEOR); printf("meteor%d: attach: invalid unit number.\n", unit); return ; } /* * Check for Meteor/PPB (PCI-PCI Bridge) * Reprogram IBM Bridge if detected. * New Meteor cards have an IBM PCI-PCI bridge, creating a secondary * PCI bus. The SAA chip is connected to this secondary bus. */ /* If we are not on PCI Bus 0, check for the Bridge */ if ( pci_get_bus_from_tag( tag ) != 0) { pcici_t bridge_tag; /* get tag of parent bridge */ bridge_tag = pci_get_parent_from_tag( tag ); /* Look for IBM 82351, 82352 or 82353 */ if (pci_conf_read(bridge_tag, PCI_ID_REG) == 0x00221014) { if ( bootverbose) printf("meteor%d: PPB device detected, reprogramming IBM bridge.\n", unit); /* disable SERR */ pci_cfgwrite(bridge_tag, 0x05, 0x00, 1); /* set LATENCY */ pci_cfgwrite(bridge_tag, 0x0d, 0x20, 1); /* write posting enable, prefetch enabled --> GRAB direction */ pci_cfgwrite(bridge_tag, 0x42, 0x14, 1); /* set PRTR Primary retry timer register */ pci_cfgwrite(bridge_tag, 0x4c, 0x10, 1); } } mtr = &meteor[unit]; mtr->tag = tag; pci_map_mem(tag, PCI_MAP_REG_START, (vm_offset_t *)&mtr->base, &mtr->phys_base); #ifdef METEOR_IRQ /* from the configuration file */ old_irq = pci_conf_read(tag, PCI_INTERRUPT_REG); pci_conf_write(tag, PCI_INTERRUPT_REG, METEOR_IRQ); new_irq = pci_conf_read(tag, PCI_INTERRUPT_REG); printf("meteor%d: attach: irq changed from %d to %d\n", unit, (old_irq & 0xff), (new_irq & 0xff)); #endif METEOR_IRQ /* setup the interrupt handling routine */ pci_map_int(tag, meteor_intr, (void*) mtr, &net_imask); /* * PCI latency timer. 32 is a good value for 4 bus mastering slots, if * you have more than for, then 16 would probably be a better value. * */ #ifndef METEOR_DEF_LATENCY_VALUE #define METEOR_DEF_LATENCY_VALUE 32 #endif latency = pci_conf_read(tag, PCI_LATENCY_TIMER); latency = (latency >> 8) & 0xff; if(bootverbose) { if(latency) printf("meteor%d: PCI bus latency is", unit); else printf("meteor%d: PCI bus latency was 0 changing to", unit); } if(!latency) { latency = METEOR_DEF_LATENCY_VALUE; pci_conf_write(tag, PCI_LATENCY_TIMER, latency<<8); } if(bootverbose) { printf(" %lu.\n", latency); } meteor_init(mtr); /* set up saa7116, saa7196, and rgb module */ if(METEOR_ALLOC) buf = get_meteor_mem(unit, METEOR_ALLOC); else buf = 0; if(bootverbose) { printf("meteor%d: buffer size %d, addr 0x%x\n", unit, METEOR_ALLOC, vtophys(buf)); } mtr->bigbuf = buf; mtr->alloc_pages = METEOR_ALLOC_PAGES; if(buf != 0) { bzero((caddr_t) buf, METEOR_ALLOC); buf = vtophys(buf); /* 640x480 RGB 16 */ mtr->base->dma1e = buf; mtr->base->dma1o = buf + 0x500; mtr->base->dma_end_e = mtr->base->dma_end_o = buf + METEOR_ALLOC; } /* 1 frame of 640x480 RGB 16 */ mtr->cols = 640; mtr->rows = 480; mtr->depth = 2; /* two bytes per pixel */ mtr->frames = 1; /* one frame */ mtr->flags |= METEOR_INITALIZED | METEOR_AUTOMODE | METEOR_DEV0 | METEOR_RGB16; make_dev(&meteor_cdevsw, unit, 0, 0, 0644, "meteor"); } #define UNIT(x) ((x) & 0x07) #ifdef unused static int meteor_reset(dev_t dev) { int unit = UNIT(minor(dev)); struct saa7116_regs *m; if(unit >= NMETEOR) return ENXIO; m = meteor[unit].base; m->cap_cntl = 0x0; tsleep((caddr_t)m, METPRI, "Mreset", hz/50); m->cap_cntl = 0x8ff0; m->cap_cntl = 0x80c0; m->cap_cntl = 0x8040; tsleep((caddr_t)m, METPRI, "Mreset", hz/10); m->cap_cntl = 0x80c0; return 0; } #endif /*--------------------------------------------------------- ** ** Meteor character device driver routines ** **--------------------------------------------------------- */ int meteor_open(dev_t dev, int flags, int fmt, struct proc *p) { meteor_reg_t *mtr; int unit; int i; unit = UNIT(minor(dev)); if (unit >= NMETEOR) /* unit out of range */ return(ENXIO); mtr = &(meteor[unit]); if (!(mtr->flags & METEOR_INITALIZED)) /* device not found */ return(ENXIO); if (mtr->flags & METEOR_OPEN) /* device is busy */ return(EBUSY); mtr->flags |= METEOR_OPEN; /* * Make sure that the i2c regs are set the same for each open. */ for(i=0; i< NUM_SAA7196_I2C_REGS; i++) { SAA7196_WRITE(mtr, i, saa7196_i2c_default[i]); } mtr->fifo_errors = 0; mtr->dma_errors = 0; mtr->frames_captured = 0; mtr->even_fields_captured = 0; mtr->odd_fields_captured = 0; mtr->proc = (struct proc *)0; set_fps(mtr, 30); #ifdef METEOR_TEST_VIDEO mtr->video.addr = 0; mtr->video.width = 0; mtr->video.banksize = 0; mtr->video.ramsize = 0; #endif return(0); } int meteor_close(dev_t dev, int flags, int fmt, struct proc *p) { meteor_reg_t *mtr; int unit; #ifdef METEOR_DEALLOC_ABOVE int temp; #endif unit = UNIT(minor(dev)); if (unit >= NMETEOR) /* unit out of range */ return(ENXIO); mtr = &(meteor[unit]); mtr->flags &= ~METEOR_OPEN; if(mtr->flags & METEOR_SINGLE) /* this should not happen, the read capture should have completed or in the very least recieved a signal before close is called. */ wakeup((caddr_t)mtr); /* continue read */ /* * Turn off capture mode. */ mtr->base->cap_cntl = 0x8ff0; mtr->flags &= ~(METEOR_CAP_MASK|METEOR_WANT_MASK); mtr->proc = (struct proc *)0; #ifdef METEOR_DEALLOC_PAGES if (mtr->bigbuf != NULL) { kmem_free(kernel_map,mtr->bigbuf,(mtr->alloc_pages*PAGE_SIZE)); mtr->bigbuf = NULL; mtr->alloc_pages = 0; } #else #ifdef METEOR_DEALLOC_ABOVE if (mtr->bigbuf != NULL && mtr->alloc_pages > METEOR_DEALLOC_ABOVE) { temp = METEOR_DEALLOC_ABOVE - mtr->alloc_pages; kmem_free(kernel_map, mtr->bigbuf+((mtr->alloc_pages - temp) * PAGE_SIZE), (temp * PAGE_SIZE)); mtr->alloc_pages = METEOR_DEALLOC_ABOVE; } #endif #endif return(0); } static void start_capture(meteor_reg_t *mtr, unsigned type) { mreg_t *cap = &mtr->base->cap_cntl; mtr->flags |= type; switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { case METEOR_ONLY_EVEN_FIELDS: mtr->flags |= METEOR_WANT_EVEN; if(type == METEOR_SINGLE) *cap = 0x0ff4 | mtr->range_enable; else *cap = 0x0ff1 | mtr->range_enable; break; case METEOR_ONLY_ODD_FIELDS: mtr->flags |= METEOR_WANT_ODD; if(type == METEOR_SINGLE) *cap = 0x0ff8 | mtr->range_enable; else *cap = 0x0ff2 | mtr->range_enable; break; default: mtr->flags |= METEOR_WANT_MASK; if(type == METEOR_SINGLE) *cap = 0x0ffc | mtr->range_enable; else *cap = 0x0ff3 | mtr->range_enable; break; } } int meteor_read(dev_t dev, struct uio *uio, int ioflag) { meteor_reg_t *mtr; int unit; int status; int count; unit = UNIT(minor(dev)); if (unit >= NMETEOR) /* unit out of range */ return(ENXIO); mtr = &(meteor[unit]); if (mtr->bigbuf == 0)/* no frame buffer allocated (ioctl failed) */ return(ENOMEM); if (mtr->flags & METEOR_CAP_MASK) return(EIO); /* already capturing */ count = mtr->rows * mtr->cols * mtr->depth; if (uio->uio_iov->iov_len < count) return(EINVAL); /* Start capture */ start_capture(mtr, METEOR_SINGLE); status=tsleep((caddr_t)mtr, METPRI, "capturing", 0); if (!status) /* successful capture */ status = uiomove((caddr_t)mtr->bigbuf, count, uio); else printf ("meteor%d: read: tsleep error %d\n", unit, status); mtr->flags &= ~(METEOR_SINGLE | METEOR_WANT_MASK); return(status); } int meteor_write(dev_t dev, struct uio *uio, int ioflag) { return(0); } int meteor_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *pr) { int error; int unit; unsigned int temp; meteor_reg_t *mtr; struct meteor_counts *cnt; struct meteor_geomet *geo; struct meteor_mem *mem; struct meteor_capframe *frame; #ifdef METEOR_TEST_VIDEO struct meteor_video *video; #endif vm_offset_t buf; struct saa7116_regs *base; error = 0; if (!arg) return(EINVAL); unit = UNIT(minor(dev)); if (unit >= NMETEOR) /* unit out of range */ return(ENXIO); mtr = &(meteor[unit]); base = mtr->base; switch (cmd) { case METEORSTS: if(*arg) mtr->flags |= METEOR_WANT_TS; else mtr->flags &= ~METEOR_WANT_TS; break; case METEORGTS: if(mtr->flags & METEOR_WANT_TS) *arg = 1; else *arg = 0; break; #ifdef METEOR_TEST_VIDEO case METEORGVIDEO: video = (struct meteor_video *)arg; video->addr = mtr->video.addr; video->width = mtr->video.width; video->banksize = mtr->video.banksize; video->ramsize = mtr->video.ramsize; break; case METEORSVIDEO: video = (struct meteor_video *)arg; mtr->video.addr = video->addr; mtr->video.width = video->width; mtr->video.banksize = video->banksize; mtr->video.ramsize = video->ramsize; break; #endif case METEORSFPS: set_fps(mtr, *(u_short *)arg); break; case METEORGFPS: *(u_short *)arg = mtr->fps; break; case METEORSSIGNAL: mtr->signal = *(int *) arg; if (mtr->signal) { mtr->proc = pr; } else { mtr->proc = (struct proc *)0; } break; case METEORGSIGNAL: *(int *)arg = mtr->signal; break; case METEORSTATUS: /* get 7196 status */ temp = 0; SAA7196_WRITE(mtr, SAA7196_STDC, SAA7196_REG(mtr, SAA7196_STDC) | 0x02); SAA7196_READ(mtr); temp |= (base->i2c_read & 0xff000000L) >> 24; SAA7196_WRITE(mtr, SAA7196_STDC, SAA7196_REG(mtr, SAA7196_STDC) & ~0x02); SAA7196_READ(mtr); temp |= (base->i2c_read & 0xff000000L) >> 16; *(u_short *)arg = temp; break; case METEORSHUE: /* set hue */ SAA7196_WRITE(mtr, SAA7196_HUEC, *(char *)arg); break; case METEORGHUE: /* get hue */ *(char *)arg = SAA7196_REG(mtr, SAA7196_HUEC); break; case METEORSCHCV: /* set chrominance gain */ SAA7196_WRITE(mtr, SAA7196_CGAINR, *(char *)arg); break; case METEORGCHCV: /* get chrominance gain */ *(char *)arg = SAA7196_REG(mtr, SAA7196_CGAINR); break; case METEORSBRIG: /* set brightness */ SAA7196_WRITE(mtr, SAA7196_BRIG, *(char *)arg); break; case METEORGBRIG: /* get brightness */ *(char *)arg = SAA7196_REG(mtr, SAA7196_BRIG); break; case METEORSCSAT: /* set chroma saturation */ SAA7196_WRITE(mtr, SAA7196_CSAT, *(char *)arg); break; case METEORGCSAT: /* get chroma saturation */ *(char *)arg = SAA7196_REG(mtr, SAA7196_CSAT); break; case METEORSCONT: /* set contrast */ SAA7196_WRITE(mtr, SAA7196_CONT, *(char *)arg); break; case METEORGCONT: /* get contrast */ *(char *)arg = SAA7196_REG(mtr, SAA7196_CONT); break; case METEORSBT254: if((mtr->flags & METEOR_RGB) == 0) return EINVAL; temp = *(unsigned short *)arg; bt254_write(mtr, temp & 0xf, (temp & 0x0ff0) >> 4); break; case METEORGBT254: if((mtr->flags & METEOR_RGB) == 0) return EINVAL; temp = *(unsigned short *)arg & 0x7; *(unsigned short *)arg = mtr->bt254_reg[temp] << 4 | temp; break; case METEORSHWS: /* set horizontal window start */ SAA7196_WRITE(mtr, SAA7196_HWS, *(char *)arg); break; case METEORGHWS: /* get horizontal window start */ *(char *)arg = SAA7196_REG(mtr, SAA7196_HWS); break; case METEORSVWS: /* set vertical window start */ SAA7196_WRITE(mtr, SAA7196_VWS, *(char *)arg); break; case METEORGVWS: /* get vertical window start */ *(char *)arg = SAA7196_REG(mtr, SAA7196_VWS); break; case METEORSINPUT: /* set input device */ switch(*(unsigned long *)arg & METEOR_DEV_MASK) { case 0: /* default */ case METEOR_INPUT_DEV0: if(mtr->flags & METEOR_RGB) select_saa7196(mtr); mtr->flags = (mtr->flags & ~METEOR_DEV_MASK) | METEOR_DEV0; SAA7196_WRITE(mtr, 0x0e, (SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x0); SAA7196_WRITE(mtr, 0x06, (SAA7196_REG(mtr, 0x06) & ~0x80)); break; case METEOR_INPUT_DEV1: if(mtr->flags & METEOR_RGB) select_saa7196(mtr); mtr->flags = (mtr->flags & ~METEOR_DEV_MASK) | METEOR_DEV1; SAA7196_WRITE(mtr, 0x0e, (SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x1); SAA7196_WRITE(mtr, 0x06, (SAA7196_REG(mtr, 0x06) & ~0x80)); break; case METEOR_INPUT_DEV2: if(mtr->flags & METEOR_RGB) select_saa7196(mtr); mtr->flags = (mtr->flags & ~METEOR_DEV_MASK) | METEOR_DEV2; SAA7196_WRITE(mtr, 0x0e, (SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x2); SAA7196_WRITE(mtr, 0x06, (SAA7196_REG(mtr, 0x06) & ~0x80)); break; case METEOR_INPUT_DEV3: if(mtr->flags & METEOR_RGB) select_saa7196(mtr); mtr->flags = (mtr->flags & ~METEOR_DEV_MASK) | METEOR_DEV3; SAA7196_WRITE(mtr, 0x0e, (SAA7196_REG(mtr, 0x0e) | 0x3)); SAA7196_WRITE(mtr, 0x06, (SAA7196_REG(mtr, 0x06) & ~0x80) ); break; case METEOR_INPUT_DEV_SVIDEO: if(mtr->flags & METEOR_RGB) select_saa7196(mtr); mtr->flags = (mtr->flags & ~METEOR_DEV_MASK) | METEOR_DEV_SVIDEO; SAA7196_WRITE(mtr, 0x0e, (SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x2); SAA7196_WRITE(mtr, 0x06, (SAA7196_REG(mtr, 0x06) & ~0x80) | 0x80); break; case METEOR_INPUT_DEV_RGB: if((mtr->flags & METEOR_RGB) == 0) return EINVAL; mtr->flags = (mtr->flags & ~METEOR_DEV_MASK) | METEOR_DEV_RGB; SAA7196_WRITE(mtr, 0x0e, (SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x3); SAA7196_WRITE(mtr, 0x06, (SAA7196_REG(mtr, 0x06) & ~0x80)); select_bt254(mtr); SAA7196_WRITE(mtr, 0x0e, /* chn 3 for synch */ (SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x3); break; default: return EINVAL; } break; case METEORGINPUT: /* get input device */ *(u_long *)arg = mtr->flags & METEOR_DEV_MASK; break; case METEORSFMT: /* set input format */ switch(*(unsigned long *)arg & METEOR_FORM_MASK ) { case 0: /* default */ case METEOR_FMT_NTSC: mtr->flags = (mtr->flags & ~METEOR_FORM_MASK) | METEOR_NTSC; SAA7196_WRITE(mtr, SAA7196_STDC, (SAA7196_REG(mtr, SAA7196_STDC) & ~0x01)); SAA7196_WRITE(mtr, 0x0f, (SAA7196_REG(mtr, 0x0f) & ~0xe0) | 0x40); SAA7196_WRITE(mtr, 0x22, 0x80); SAA7196_WRITE(mtr, 0x24, (SAA7196_REG(mtr, 0x24) & ~0x0c) | 0x08); SAA7196_WRITE(mtr, 0x26, 0xf0); SAA7196_WRITE(mtr, 0x28, (SAA7196_REG(mtr, 0x28) & ~0x0c)) ; if(mtr->flags & METEOR_RGB){ bt254_ntsc(mtr, 1); } break; case METEOR_FMT_PAL: mtr->flags = (mtr->flags & ~METEOR_FORM_MASK) | METEOR_PAL; SAA7196_WRITE(mtr, SAA7196_STDC, (SAA7196_REG(mtr, SAA7196_STDC) & ~0x01)); SAA7196_WRITE(mtr, 0x0f, (SAA7196_REG(mtr, 0x0f) & ~0xe0)); SAA7196_WRITE(mtr, 0x22, 0x00); SAA7196_WRITE(mtr, 0x24, (SAA7196_REG(mtr, 0x24) | 0x0c)); SAA7196_WRITE(mtr, 0x26, 0x20); SAA7196_WRITE(mtr, 0x28, (SAA7196_REG(mtr, 0x28) & ~0x0c) | 0x04) ; if(mtr->flags & METEOR_RGB){ bt254_ntsc(mtr, 0); } break; case METEOR_FMT_SECAM: mtr->flags = (mtr->flags & ~METEOR_FORM_MASK) | METEOR_SECAM; SAA7196_WRITE(mtr, SAA7196_STDC, (SAA7196_REG(mtr, SAA7196_STDC) & ~0x01) | 0x1); SAA7196_WRITE(mtr, 0x0f, (SAA7196_REG(mtr, 0x0f) & ~0xe0) | 0x20); SAA7196_WRITE(mtr, 0x22, 0x00); SAA7196_WRITE(mtr, 0x24, (SAA7196_REG(mtr, 0x24) | 0x0c)); SAA7196_WRITE(mtr, 0x26, 0x20); SAA7196_WRITE(mtr, 0x28, (SAA7196_REG(mtr, 0x28) & ~0x0c) | 0x04) ; if(mtr->flags & METEOR_RGB){ bt254_ntsc(mtr, 0); } break; case METEOR_FMT_AUTOMODE: mtr->flags = (mtr->flags & ~METEOR_FORM_MASK) | METEOR_AUTOMODE; SAA7196_WRITE(mtr, SAA7196_STDC, (SAA7196_REG(mtr, SAA7196_STDC) & ~0x01)); SAA7196_WRITE(mtr, 0x0f, (SAA7196_REG(mtr, 0x0f) & ~0xe0) | 0x80); break; default: return EINVAL; } break; case METEORGFMT: /* get input format */ *(u_long *)arg = mtr->flags & METEOR_FORM_MASK; break; case METEORCAPTUR: temp = mtr->flags; switch (*(int *) arg) { case METEOR_CAP_SINGLE: if (mtr->bigbuf==0) /* no frame buffer allocated */ return(ENOMEM); if (temp & METEOR_CAP_MASK) return(EIO); /* already capturing */ start_capture(mtr, METEOR_SINGLE); /* wait for capture to complete */ error=tsleep((caddr_t)mtr, METPRI, "capturing", 0); if(error) printf("meteor%d: ioctl: tsleep error %d\n", unit, error); mtr->flags &= ~(METEOR_SINGLE|METEOR_WANT_MASK); break; case METEOR_CAP_CONTINOUS: if (mtr->bigbuf==0) /* no frame buffer allocated */ return(ENOMEM); if (temp & METEOR_CAP_MASK) return(EIO); /* already capturing */ start_capture(mtr, METEOR_CONTIN); break; case METEOR_CAP_STOP_CONT: if (mtr->flags & METEOR_CONTIN) { /* turn off capture */ base->cap_cntl = 0x8ff0; mtr->flags &= ~(METEOR_CONTIN|METEOR_WANT_MASK); } break; default: error = EINVAL; break; } break; case METEORCAPFRM: frame = (struct meteor_capframe *) arg; if (!frame) return(EINVAL); switch (frame->command) { case METEOR_CAP_N_FRAMES: if (mtr->flags & METEOR_CAP_MASK) return(EIO); if (mtr->flags & (METEOR_YUV_PLANAR | METEOR_YUV_422)) /* XXX */ return(EINVAL); /* should fix intr so we allow these */ if (mtr->bigbuf == 0) return(ENOMEM); if ((mtr->frames < 2) || (frame->lowat < 1 || frame->lowat >= mtr->frames) || (frame->hiwat < 1 || frame->hiwat >= mtr->frames) || (frame->lowat > frame->hiwat)) return(EINVAL); /* meteor_mem structure is on the page after the data */ mem = mtr->mem = (struct meteor_mem *) (mtr->bigbuf + (round_page(mtr->frame_size * mtr->frames))); mtr->current = 1; mtr->synch_wait = 0; mem->num_bufs = mtr->frames; mem->frame_size= mtr->frame_size; /* user and kernel change these */ mem->lowat = frame->lowat; mem->hiwat = frame->hiwat; mem->active = 0; mem->num_active_bufs = 0; /* Start capture */ start_capture(mtr, METEOR_SYNCAP); break; case METEOR_CAP_STOP_FRAMES: if (mtr->flags & METEOR_SYNCAP) { /* turn off capture */ base->cap_cntl = 0x8ff0; mtr->flags &= ~(METEOR_SYNCAP|METEOR_WANT_MASK); } break; case METEOR_HALT_N_FRAMES: if(mtr->flags & METEOR_SYNCAP) { base->cap_cntl = 0x8ff0; mtr->flags &= ~(METEOR_WANT_MASK); } break; case METEOR_CONT_N_FRAMES: if(!(mtr->flags & METEOR_SYNCAP)) { error = EINVAL; break; } start_capture(mtr, METEOR_SYNCAP); break; default: error = EINVAL; break; } break; case METEORSETGEO: geo = (struct meteor_geomet *) arg; /* Either even or odd, if even & odd, then these a zero */ if((geo->oformat & METEOR_GEO_ODD_ONLY) && (geo->oformat & METEOR_GEO_EVEN_ONLY)) { printf("meteor%d: ioctl: Geometry odd or even only.\n", unit); return EINVAL; } /* set/clear even/odd flags */ if(geo->oformat & METEOR_GEO_ODD_ONLY) mtr->flags |= METEOR_ONLY_ODD_FIELDS; else mtr->flags &= ~METEOR_ONLY_ODD_FIELDS; if(geo->oformat & METEOR_GEO_EVEN_ONLY) mtr->flags |= METEOR_ONLY_EVEN_FIELDS; else mtr->flags &= ~METEOR_ONLY_EVEN_FIELDS; /* can't change parameters while capturing */ if (mtr->flags & METEOR_CAP_MASK) return(EBUSY); if ((geo->columns & 0x3fe) != geo->columns) { printf( "meteor%d: ioctl: %d: columns too large or not even.\n", unit, geo->columns); error = EINVAL; } if (((geo->rows & 0x7fe) != geo->rows) || ((geo->oformat & METEOR_GEO_FIELD_MASK) && ((geo->rows & 0x3fe) != geo->rows)) ) { printf( "meteor%d: ioctl: %d: rows too large or not even.\n", unit, geo->rows); error = EINVAL; } if (geo->frames > 32) { printf("meteor%d: ioctl: too many frames.\n", unit); error = EINVAL; } if(error) return error; if ((temp=geo->rows * geo->columns * geo->frames * 2) != 0) { if (geo->oformat & METEOR_GEO_RGB24) temp = temp * 2; /* meteor_mem structure for SYNC Capture */ if (geo->frames > 1) temp += PAGE_SIZE; temp = btoc(temp); if (temp > mtr->alloc_pages #ifdef METEOR_TEST_VIDEO && mtr->video.addr == 0 #endif ) { buf = get_meteor_mem(unit, temp*PAGE_SIZE); if(buf != 0) { kmem_free(kernel_map, mtr->bigbuf, (mtr->alloc_pages * PAGE_SIZE)); mtr->bigbuf = buf; mtr->alloc_pages = temp; if(bootverbose) printf( "meteor%d: ioctl: Allocating %d bytes\n", unit, temp*PAGE_SIZE); } else { error = ENOMEM; } } } if(error) return error; mtr->rows = geo->rows; mtr->cols = geo->columns; mtr->frames = geo->frames; #ifdef METEOR_TEST_VIDEO if(mtr->video.addr) buf = vtophys(mtr->video.addr); else #endif buf = vtophys(mtr->bigbuf); /* set defaults and end of buffer locations */ base->dma1e = buf; base->dma2e = buf; base->dma3e = buf; base->dma1o = buf; base->dma2o = buf; base->dma3o = buf; base->stride1e = 0; base->stride2e = 0; base->stride3e = 0; base->stride1o = 0; base->stride2o = 0; base->stride3o = 0; /* set end of DMA location, even/odd */ base->dma_end_e = base->dma_end_o = buf + mtr->alloc_pages * PAGE_SIZE; /* * Determine if we can use the hardware range detect. */ if(mtr->alloc_pages * PAGE_SIZE < RANGE_BOUNDARY && ((buf & 0xff000000) | base->dma_end_e) == (buf + mtr->alloc_pages * PAGE_SIZE) ) mtr->range_enable = 0x8000; else { mtr->range_enable = 0x0; base->dma_end_e = base->dma_end_o = 0xffffffff; } switch (geo->oformat & METEOR_GEO_OUTPUT_MASK) { case 0: /* default */ case METEOR_GEO_RGB16: mtr->depth = 2; mtr->frame_size = mtr->rows * mtr->cols * mtr->depth; mtr->flags &= ~METEOR_OUTPUT_FMT_MASK; mtr->flags |= METEOR_RGB16; temp = mtr->cols * mtr->depth; /* recal stride and starting point */ switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { case METEOR_ONLY_ODD_FIELDS: base->dma1o = buf; #ifdef METEOR_TEST_VIDEO if(mtr->video.addr && mtr->video.width) base->stride1o = mtr->video.width-temp; #endif SAA7196_WRITE(mtr, 0x20, 0xd0); break; case METEOR_ONLY_EVEN_FIELDS: base->dma1e = buf; #ifdef METEOR_TEST_VIDEO if(mtr->video.addr && mtr->video.width) base->stride1e = mtr->video.width-temp; #endif SAA7196_WRITE(mtr, 0x20, 0xf0); break; default: /* interlaced even/odd */ base->dma1e = buf; base->dma1o = buf + temp; base->stride1e = base->stride1o = temp; #ifdef METEOR_TEST_VIDEO if(mtr->video.addr && mtr->video.width) { base->dma1o = buf + mtr->video.width; base->stride1e = base->stride1o = mtr->video.width - temp + mtr->video.width; } #endif SAA7196_WRITE(mtr, 0x20, 0x90); break; } base->routee = base->routeo = 0xeeeeee01; break; case METEOR_GEO_RGB24: mtr->depth = 4; mtr->frame_size = mtr->rows * mtr->cols * mtr->depth; mtr->flags &= ~METEOR_OUTPUT_FMT_MASK; mtr->flags |= METEOR_RGB24; temp = mtr->cols * mtr->depth; /* recal stride and starting point */ switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { case METEOR_ONLY_ODD_FIELDS: base->dma1o = buf; #ifdef METEOR_TEST_VIDEO if(mtr->video.addr && mtr->video.width) base->stride1o = mtr->video.width-temp; #endif SAA7196_WRITE(mtr, 0x20, 0xd2); break; case METEOR_ONLY_EVEN_FIELDS: base->dma1e = buf; #ifdef METEOR_TEST_VIDEO if(mtr->video.addr && mtr->video.width) base->stride1e = mtr->video.width-temp; #endif SAA7196_WRITE(mtr, 0x20, 0xf2); break; default: /* interlaced even/odd */ base->dma1e = buf; base->dma1o = buf + mtr->cols * mtr->depth; base->stride1e = base->stride1o = mtr->cols * mtr->depth; #ifdef METEOR_TEST_VIDEO if(mtr->video.addr && mtr->video.width) { base->dma1o = buf + mtr->video.width; base->stride1e = base->stride1o = mtr->video.width - temp + mtr->video.width; } #endif SAA7196_WRITE(mtr, 0x20, 0x92); break; } base->routee= base->routeo= 0x39393900; break; case METEOR_GEO_YUV_PLANAR: mtr->depth = 2; temp = mtr->rows * mtr->cols; /* compute frame size */ mtr->frame_size = temp * mtr->depth; mtr->flags &= ~METEOR_OUTPUT_FMT_MASK; mtr->flags |= METEOR_YUV_PLANAR; /* recal stride and starting point */ switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { case METEOR_ONLY_ODD_FIELDS: base->dma1o = buf; /* Y Odd */ base->dma2o = buf + temp; /* U Odd */ temp >>= 1; base->dma3o = base->dma2o + temp; /* V Odd */ SAA7196_WRITE(mtr, 0x20, 0xd1); break; case METEOR_ONLY_EVEN_FIELDS: base->dma1e = buf; /* Y Even */ base->dma2e = buf + temp; /* U Even */ temp >>= 1; base->dma2e= base->dma2e + temp; /* V Even */ SAA7196_WRITE(mtr, 0x20, 0xf1); break; default: /* interlaced even/odd */ base->dma1e = buf; /* Y Even */ base->dma2e = buf + temp; /* U Even */ temp >>= 2; base->dma3e = base->dma2e + temp; /* V Even */ base->dma1o = base->dma1e+mtr->cols;/* Y Odd */ base->dma2o = base->dma3e + temp; /* U Odd */ base->dma3o = base->dma2o + temp; /* V Odd */ base->stride1e = base->stride1o = mtr->cols; SAA7196_WRITE(mtr, 0x20, 0x91); break; } switch (geo->oformat & (METEOR_GEO_YUV_12 | METEOR_GEO_YUV_9)) { case METEOR_GEO_YUV_9: base->routee=base->routeo = 0xaaaaffc3; break; case METEOR_GEO_YUV_12: base->routee=base->routeo = 0xaaaaffc2; break; default: base->routee=base->routeo = 0xaaaaffc1; break; } break; case METEOR_GEO_YUV_422:/* same as planer, different uv order */ mtr->depth = 2; temp = mtr->rows * mtr->cols; /* compute frame size */ mtr->frame_size = temp * mtr->depth; mtr->flags &= ~METEOR_OUTPUT_FMT_MASK; mtr->flags |= METEOR_YUV_422; switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { case METEOR_ONLY_ODD_FIELDS: base->dma1o = buf; base->dma2o = buf + temp; base->dma3o = base->dma2o + (temp >> 1); SAA7196_WRITE(mtr, 0x20, 0xd1); break; case METEOR_ONLY_EVEN_FIELDS: base->dma1e = buf; base->dma2e = buf + temp; base->dma3e = base->dma2e + (temp >> 1); SAA7196_WRITE(mtr, 0x20, 0xf1); break; default: /* interlaced even/odd */ base->dma1e = buf; /* Y even */ base->dma2e = buf + temp; /* U even */ base->dma3e = base->dma2e + (temp >> 1);/* V even */ base->dma1o = base->dma1e+mtr->cols;/* Y odd */ temp = mtr->cols >> 1; base->dma2o = base->dma2e+temp; /* U odd */ base->dma3o = base->dma3e+temp; /* V odd */ base->stride1e = base->stride1o = mtr->cols; /* Y stride */ base->stride2e = base->stride2o = temp; /* U stride */ base->stride3e = base->stride3o = temp; /* V stride */ SAA7196_WRITE(mtr, 0x20, 0x91); break; } switch (geo->oformat & (METEOR_GEO_YUV_12 | METEOR_GEO_YUV_9)) { case METEOR_GEO_YUV_9: base->routee=base->routeo = 0xaaaaffc3; break; case METEOR_GEO_YUV_12: base->routee=base->routeo = 0xaaaaffc2; break; default: base->routee=base->routeo = 0xaaaaffc1; break; } break; case METEOR_GEO_YUV_PACKED: mtr->depth = 2; mtr->frame_size = mtr->rows * mtr->cols * mtr->depth; mtr->flags &= ~METEOR_OUTPUT_FMT_MASK; mtr->flags |= METEOR_YUV_PACKED; /* recal stride and odd starting point */ switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) { case METEOR_ONLY_ODD_FIELDS: base->dma1o = buf; SAA7196_WRITE(mtr, 0x20, 0xd1); break; case METEOR_ONLY_EVEN_FIELDS: base->dma1e = buf; SAA7196_WRITE(mtr, 0x20, 0xf1); break; default: /* interlaced even/odd */ base->dma1e = buf; base->dma1o = buf + mtr->cols * mtr->depth; base->stride1e = base->stride1o = mtr->cols * mtr->depth; SAA7196_WRITE(mtr, 0x20, 0x91); break; } base->routee = base->routeo = 0xeeeeee41; break; default: error = EINVAL; /* invalid argument */ printf("meteor%d: ioctl: invalid output format\n",unit); break; } /* set cols */ SAA7196_WRITE(mtr, 0x21, mtr->cols & 0xff); SAA7196_WRITE(mtr, 0x24, ((SAA7196_REG(mtr, 0x24) & ~0x03) | ((mtr->cols >> 8) & 0x03))); /* set rows */ if(mtr->flags & METEOR_ONLY_FIELDS_MASK) { SAA7196_WRITE(mtr, 0x25, ((mtr->rows) & 0xff)); SAA7196_WRITE(mtr, 0x28, ((SAA7196_REG(mtr, 0x28) & ~0x03) | ((mtr->rows >> 8) & 0x03))); } else { /* Interlaced */ SAA7196_WRITE(mtr, 0x25, ((mtr->rows >> 1) & 0xff)); SAA7196_WRITE(mtr, 0x28, ((SAA7196_REG(mtr, 0x28) & ~0x03) | ((mtr->rows >> 9) & 0x03))); } /* set signed/unsigned chrominance */ SAA7196_WRITE(mtr, 0x30, (SAA7196_REG(mtr, 0x30) & ~0x10) | ((geo->oformat&METEOR_GEO_UNSIGNED)?0:0x10)); break; case METEORGETGEO: geo = (struct meteor_geomet *) arg; geo->rows = mtr->rows; geo->columns = mtr->cols; geo->frames = mtr->frames; geo->oformat = (mtr->flags & METEOR_OUTPUT_FMT_MASK) | (mtr->flags & METEOR_ONLY_FIELDS_MASK) | (SAA7196_REG(mtr, 0x30) & 0x10 ? 0:METEOR_GEO_UNSIGNED); switch(base->routee & 0xff) { case 0xc3: geo->oformat |= METEOR_GEO_YUV_9; break; case 0xc2: geo->oformat |= METEOR_GEO_YUV_12; break; default: break; } break; case METEORSCOUNT: /* (re)set error counts */ cnt = (struct meteor_counts *) arg; mtr->fifo_errors = cnt->fifo_errors; mtr->dma_errors = cnt->dma_errors; mtr->frames_captured = cnt->frames_captured; mtr->even_fields_captured = cnt->even_fields_captured; mtr->odd_fields_captured = cnt->odd_fields_captured; break; case METEORGCOUNT: /* get error counts */ cnt = (struct meteor_counts *) arg; cnt->fifo_errors = mtr->fifo_errors; cnt->dma_errors = mtr->dma_errors; cnt->frames_captured = mtr->frames_captured; cnt->even_fields_captured = mtr->even_fields_captured; cnt->odd_fields_captured = mtr->odd_fields_captured; break; default: printf("meteor%d: ioctl: invalid ioctl request\n", unit); error = ENOTTY; break; } return(error); } int meteor_mmap(dev_t dev, vm_offset_t offset, int nprot) { int unit; meteor_reg_t *mtr; unit = UNIT(minor(dev)); if (unit >= NMETEOR) /* at this point could this happen? */ return(-1); mtr = &(meteor[unit]); if(nprot & PROT_EXEC) return -1; if(offset >= mtr->alloc_pages * PAGE_SIZE) return -1; return i386_btop(vtophys(mtr->bigbuf) + offset); }