/* BT848 1.3-ALPHA Driver for Brooktree's Bt848 based cards. The Brooktree BT848 Driver driver is based upon Mark Tinguely and Jim Lowe's driver for the Matrox Meteor PCI card . The Philips SAA 7116 and SAA 7196 are very different chipsets than the BT848. For starters, the BT848 is a one chipset solution and it incorporates a RISC engine to control the DMA transfers -- that is it the actual dma process is control by a program which resides in the hosts memory also the register definitions between the Philips chipsets and the Bt848 are very different. The original copyright notice by Mark and Jim is included mostly to honor their fantastic work in the Matrox Meteor driver! Enjoy, Amancio */ /* * 1. Redistributions of source code must retain the * Copyright (c) 1997 Amancio Hasty * 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 Amancio Hasty * 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. */ /* * 1. Redistributions of source code must retain the * 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. */ /* Change History: 1.0 1/24/97 First Alpha release 1.1 2/20/97 Added video ioctl so we can do PCI To PCI data transfers. This is for capturing data directly to a vga frame buffer which has a linear frame buffer. Minor code clean-up. 1.3 2/23/97 Fixed system lock-up reported by Randall Hopper . This problem seems somehow to be exhibited only in his system. I changed the setting of INT_MASK for CAP_CONTINUOUS to be exactly the same as CAP_SINGLE apparently setting bit 23 cleared the system lock up. version 1.1 of the driver has been reported to work with STB's WinTv, Hauppage's Wincast/Tv and last but not least with the Intel Smart Video Recorder. 1.4 3/9/97 fsmp@freefall.org Merged code to support tuners on STB and WinCast cards. Modifications to the contrast and chroma ioctls. Textual cleanup. 1.5 3/15/97 fsmp@freefall.org new bt848 specific versions of hue/bright/ contrast/satu/satv. Amancio's patch to fix "screen freeze" problem. 1.6 3/19/97 fsmp@freefall.org new table-driven frequency lookup. removed disable_intr()/enable_intr() calls from i2c. misc. cleanup. 1.7 3/19/97 fsmp@freefall.org added audio support submitted by: Michael Petry 1.8 3/20/97 fsmp@freefall.org extended audio support. card auto-detection. major cleanup, order of routines, declarations, etc. */ #include "bktr.h" #if NBKTR > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /* DEVFS */ #include #include #include #include #include #include /* * This is for start-up convenience only, NOT mandatory. * XXX: we need to support additional sets of frequencies. * * this selects the set of frequencies used by the tuner. * in your kernel config file set one of: options DEFAULT_CHNLSET=1 # CHNLSET_NABCST options DEFAULT_CHNLSET=2 # CHNLSET_CABLEIRC * * alternately, in this file, you can select one of: * #define DEFAULT_CHNLSET CHNLSET_NABCST #define DEFAULT_CHNLSET CHNLSET_CABLEIRC */ #if !defined( DEFAULT_CHNLSET ) #define DEFAULT_CHNLSET CHNLSET_NABCST #endif /* * the recognized cards. * used as indexes of several tables. */ #define CARD_UNKNOWN 0 #define CARD_MIRO 1 #define CARD_HAUPPAUGE 2 #define CARD_STB 3 #define CARD_INTEL 4 /* * XXX: hack to allow multiple programs to open the device, * ie., a tv client and a remote control * we need to make this a MINOR UNIT type thing someday... */ #define MULTIPLE_OPENS /* XXX Fixme: remove me??? */ #define ORIGINAL_DELAYS_NOT /* XXX Fixme: anomolies still exist in the i2c code */ #define EXTRA_START #define FUNNY_HI #define REALLY_ACK /* XXX attempt to hold sync on marginal signals, experimental */ #define LOW_SYNC_NOT /* srart of audio support */ #define AUDIO_SUPPORT #include "pci.h" #if NPCI > 0 #include #include #endif #include #include /* extensions to ioctl_meteor.h */ #include #define METPRI (PZERO+8)|PCATCH static void bktr_intr __P((void *arg)); static bt_enable_cnt; static u_long btl_status_prev; /* * 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 BROOKTREE_ALLOC_PAGES #define BROOKTREE_ALLOC_PAGES 217*4 #endif #define BROOKTREE_ALLOC (BROOKTREE_ALLOC_PAGES * PAGE_SIZE) static bktr_reg_t brooktree[NBKTR]; #define BROOKTRE_NUM(mtr) ((bktr - &brooktree[0])/sizeof(bktr_reg_t)) #define BKTRPRI (PZERO+8)|PCATCH static char* bktr_probe( pcici_t tag, pcidi_t type ); static void bktr_attach( pcici_t tag, int unit ); static u_long bktr_count; static struct pci_device bktr_device = { "bktr", bktr_probe, bktr_attach, &bktr_count }; DATA_SET (pcidevice_set, bktr_device); static d_open_t bktr_open; static d_close_t bktr_close; static d_read_t bktr_read; static d_write_t bktr_write; static d_ioctl_t bktr_ioctl; static d_mmap_t bktr_mmap; #define CDEV_MAJOR 79 static struct cdevsw bktr_cdevsw = { bktr_open, bktr_close, bktr_read, bktr_write, bktr_ioctl, nostop, nullreset, nodevtotty, seltrue, bktr_mmap, NULL, "bktr", NULL, -1 }; /* * misc. support routines. */ struct CARDTYPE { char* name; u_char tuner; u_char dbx; u_char audiomuxs[4]; /* tuner, external, internal/unused, mute */ }; static struct CARDTYPE card_types[]; static int probe_card( bktr_reg_t *bktr, int verbose ); static vm_offset_t get_bktr_mem( int unit, unsigned size ); /* * bt848 RISC programming routines. */ static int dump_bt848( volatile u_char *bt848 ); static void yuvpack_prog( bktr_reg_t * bktr, char i_flag, int cols, int rows, int interlace) ; static void yuv422_prog( bktr_reg_t * bktr, char i_flag, int cols, int rows, int interlace); static void rgb_prog( bktr_reg_t * bktr, char i_flag, int cols, int rows, int pixel_width, int interlace) ; static void build_dma_prog( bktr_reg_t * bktr, char i_flag); /* * video & video capture specific routines. */ static void start_capture( bktr_reg_t *bktr, unsigned type ); static void set_fps( bktr_reg_t *bktr, u_short fps ); /* * tuner specific functions. */ static int tv_channel( bktr_reg_t* bktr, int channel ); static int tv_freq( bktr_reg_t* bktr, int frequency ); static int tuner_status( bktr_reg_t* bktr ); #if defined( AUDIO_SUPPORT ) /* * audio specific functions. */ static int set_audio( bktr_reg_t * bktr, int mode ); static int set_BTSC( bktr_reg_t * bktr, int control ); #endif /* AUDIO_SUPPORT */ /* * the boot time probe routine. */ static char* bktr_probe( pcici_t tag, pcidi_t type ) { switch (type) { case BROOKTREE_848_ID: return("BrookTree 848"); }; return ((char *)0); } /* * interrupt handling routine complete bktr_read() if using interrupts. */ static void bktr_intr( void *arg ) { bktr_reg_t *bktr = (bktr_reg_t *) arg; volatile u_long *btl_reg, t_pc; volatile u_char *bt848, *bt_reg, s_status; volatile u_short *bts_reg; u_long bktr_status, *bktr_pc; #if 0 /* XXX: what is this for??? */ u_long next_base = (u_long)(vtophys(bktr->bigbuf)), stat; #endif bt848 = bktr->base; bt_reg = (u_char *) &bt848; s_status = *bt_reg; *bt_reg = 0; if (!(bktr->flags & METEOR_OPEN)) { bts_reg = (u_short *) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 0; btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 0; } btl_reg = (u_long *) &bt848[BKTR_INT_STAT]; bktr_status = *btl_reg ; *btl_reg = *btl_reg; *btl_reg = 0; if (*btl_reg & (1 << 25)) *btl_reg |= 1 << 8; bktr_pc = (u_long *) &bt848[BKTR_RISC_COUNT]; t_pc = *bktr_pc; /* printf(" STATUS %x %x %x \n", s_status, bktr_status, t_pc); */ if (!((bktr_status & 0x800) || (bktr_status & 1 << 19 ))) { btl_status_prev = bktr_status; /* return; */ } /* if risc was disabled re-start process again */ if (!(bktr_status & (1 << 27)) || ((bktr_status & 0xff000) != 0) ) { btl_reg = (u_long *) &bt848[BKTR_INT_STAT]; *btl_reg = *btl_reg; bts_reg = (u_short *) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 0; btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 0; btl_reg = (u_long *) &bt848[BKTR_RISC_STRT_ADD] ; *btl_reg = vtophys(bktr->dma_prog); bts_reg = (u_short *) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 1; *bts_reg = bktr->capcontrol; btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 1 << 23 | 1 << 11 | 2 | 1; bt848[BKTR_CAP_CTL] = bktr->bktr_cap_ctl; return; } btl_reg = (u_long *) &bt848[BKTR_CAP_CTL]; if (!(bktr_status & (1 << 11))) return; bktr_pc = (u_long *) &bt848[BKTR_RISC_COUNT]; /*printf("intr status %x %x %x\n", bktr_status, s_status, *bktr_pc);*/ /* * 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 (!(bktr->flags & METEOR_CAP_MASK)) *btl_reg = 0; /* * If we have a complete frame. */ if (!(bktr->flags & METEOR_WANT_MASK)) { bktr->frames_captured++; /* * post the completion time. */ if (bktr->flags & METEOR_WANT_TS) { struct timeval *ts; if ((u_int) bktr->alloc_pages * PAGE_SIZE <= (bktr->frame_size + sizeof(struct timeval))) { ts =(struct timeval *)bktr->bigbuf + bktr->frame_size; /* doesn't work in synch mode except * for first frame */ /* XXX */ microtime(ts); } } /* * Wake up the user in single capture mode. */ if (bktr->flags & METEOR_SINGLE) { if (!(bktr_status & (1 << 24))) return; /* stop dma */ btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 0; bts_reg = (u_short *) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 1; /* disable risc and fifo */ wakeup((caddr_t)bktr); } /* * If the user requested to be notified via signal, * let them know the frame is complete. */ if (bktr->proc && !(bktr->signal & METEOR_SIG_MODE_MASK)) psignal(bktr->proc, bktr->signal&(~METEOR_SIG_MODE_MASK)); /* * Reset the want flags if in continuous or * synchronous capture mode. */ if (bktr->flags & (METEOR_CONTIN|METEOR_SYNCAP)) { switch(bktr->flags & METEOR_ONLY_FIELDS_MASK) { case METEOR_ONLY_ODD_FIELDS: bktr->flags |= METEOR_WANT_ODD; break; case METEOR_ONLY_EVEN_FIELDS: bktr->flags |= METEOR_WANT_EVEN; break; default: bktr->flags |= METEOR_WANT_MASK; break; } } } return; } /* * what should we do here? */ static void bktr_init ( bktr_reg_t *bktr ) { return; } /* * the attach routine. */ static void bktr_attach( pcici_t tag, int unit ) { bktr_reg_t *bktr; volatile u_char *bt848; volatile u_long *btl_reg; #ifdef BROOKTREE_IRQ u_long old_irq, new_irq; #endif u_char *test; vm_offset_t buf; u_long latency; u_long fun; bt_enable_cnt = 0; bktr = &brooktree[unit]; if (unit >= NBKTR) { printf("brooktree%d: attach: only %d units configured.\n", unit, NBKTR); printf("brooktree%d: attach: invalid unit number.\n", unit); return ; } bktr->tag = tag; pci_map_mem(tag, PCI_MAP_REG_START, (vm_offset_t *) &bktr->base, &bktr->phys_base); fun = pci_conf_read(tag, PCI_COMMAND_STATUS_REG); #ifdef BROOKTREE_IRQ /* from the configuration file */ old_irq = pci_conf_read(tag, PCI_INTERRUPT_REG); pci_conf_write(tag, PCI_INTERRUPT_REG, BROOKTREE_IRQ); new_irq = pci_conf_read(tag, PCI_INTERRUPT_REG); printf("bktr%d: attach: irq changed from %d to %d\n", unit, (old_irq & 0xff), (new_irq & 0xff)); #endif /* setup the interrupt handling routine */ pci_map_int(tag, bktr_intr, (void*) bktr, &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 BROOKTREE_DEF_LATENCY_VALUE #define BROOKTREE_DEF_LATENCY_VALUE 10 #endif latency = pci_conf_read(tag, PCI_LATENCY_TIMER); latency = (latency >> 8) & 0xff; if ( bootverbose ) { if (latency) printf("brooktree%d: PCI bus latency is", unit); else printf("brooktree%d: PCI bus latency was 0 changing to", unit); } if ( !latency ) { latency = BROOKTREE_DEF_LATENCY_VALUE; pci_conf_write(tag, PCI_LATENCY_TIMER, latency<<8); } if ( bootverbose ) { printf(" %d.\n", latency); } /* bktr_init(bktr); set up the bt848 */ /* allocate space for dma program */ bktr->dma_prog = get_bktr_mem(unit, 8); bktr->odd_dma_prog = get_bktr_mem(unit, 8); if ( BROOKTREE_ALLOC ) buf = get_bktr_mem(unit, BROOKTREE_ALLOC); else buf = 0; if ( bootverbose ) { printf("bktr%d: buffer size %d, addr 0x%x\n", unit, BROOKTREE_ALLOC, vtophys(buf)); } bktr->bigbuf = buf; bktr->alloc_pages = BROOKTREE_ALLOC_PAGES; if ( buf != 0 ) { bzero((caddr_t) buf, BROOKTREE_ALLOC); buf = vtophys(buf); #ifdef amancio /* 640x480 RGB 16 */ amancio : setup dma risc program bktr->base->dma1e = buf; bktr->base->dma1o = buf + 0x500; bktr->base->dma_end_e = bktr->base->dma_end_o = buf + METEOR_ALLOC; end of setup up dma risc program /* 1 frame of 640x480 RGB 16 */ bktr->flags |= METEOR_INITALIZED | METEOR_AUTOMODE | METEOR_DEV0 | METEOR_RGB16; #endif /* amancio */ bktr->flags = METEOR_INITALIZED | METEOR_AUTOMODE | METEOR_DEV0 | METEOR_RGB16; bktr->dma_prog_loaded = 0; bktr->cols = 640; bktr->rows = 480; bktr->depth = 2; /* two bytes per pixel */ bktr->frames = 1; /* one frame */ bt848 = bktr->base; btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 0; bt848[BKTR_GPIO_DMA_CTL] = 0; } /* defaults for the tuner section of the card */ bktr->tuner.frequency = 0; bktr->tuner.channel = 0; bktr->tuner.chnlset = DEFAULT_CHNLSET; probe_card( bktr, 1 ); #ifdef DEVFS bktr->devfs_token = devfs_add_devswf(&bktr_cdevsw, unit, DV_CHR, 0, 0, 0644, "brooktree"); #endif /* DEVFS */ } #define UNIT(x) ((x) & 0x07) /*--------------------------------------------------------- ** ** BrookTree 848 character device driver routines ** **--------------------------------------------------------- */ /* * */ int bktr_open( dev_t dev, int flags, int fmt, struct proc *p ) { bktr_reg_t *bktr; int unit; int i; volatile u_char *bt848; volatile u_char *bt_reg; volatile u_long *btl_reg; unit = UNIT(minor(dev)); if (unit >= NBKTR) /* unit out of range */ return(ENXIO); bktr = &(brooktree[unit]); if (!(bktr->flags & METEOR_INITALIZED)) /* device not found */ return(ENXIO); #if defined( MULTIPLE_OPENS ) if (bktr->flags & METEOR_OPEN) /* device already open */ return 0; #else if (bktr->flags & METEOR_OPEN) /* device is busy */ return(EBUSY); #endif /* MULTIPLE_OPENS */ bktr->flags |= METEOR_OPEN; bt848 = bktr->base; #if 0 dump_bt848( bt848 ); #endif /* 0 */ *bt848 = 0x3; /* bt848[ 0 ] */ *bt848 = 0xc0; #if defined( LOW_SYNC ) bt848[BKTR_ADC] = 0xa1; #else bt848[BKTR_ADC] = 0x81; #endif /* LOW_SYNC */ bt848[BKTR_IFORM] = 0x69; bktr->flags = (bktr->flags & ~METEOR_DEV_MASK) | METEOR_DEV0; bt848[BKTR_COLOR_CTL] = 0x20; bt848[BKTR_E_HSCALE_LO] = 0xaa; bt848[BKTR_O_HSCALE_LO] = 0xaa; bt848[BKTR_E_DELAY_LO] = 0x72; bt848[BKTR_O_DELAY_LO] = 0x72; bt848[BKTR_E_SCLOOP] = 0; bt848[BKTR_O_SCLOOP] = 0; bt848[BKTR_VBI_PACK_SIZE] = 0; bt848[BKTR_VBI_PACK_DEL] = 0; bzero((u_char *) bktr->bigbuf, 640*480*4); bktr->fifo_errors = 0; bktr->dma_errors = 0; bktr->frames_captured = 0; bktr->even_fields_captured = 0; bktr->odd_fields_captured = 0; bktr->proc = (struct proc *)0; set_fps(bktr, 30); bktr->video.addr = 0; bktr->video.width = 0; bktr->video.banksize = 0; bktr->video.ramsize = 0; btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 1 << 23; #if defined( AUDIO_SUPPORT ) set_audio( bktr, AUDIO_UNMUTE ); if ( card_types[ bktr->card_type ].dbx ) set_BTSC( bktr, 0 ); /* enable the stereo chip */ #endif /* AUDIO_SUPPORT */ return(0); } /* * */ int bktr_close( dev_t dev, int flags, int fmt, struct proc *p ) { bktr_reg_t *bktr; int unit; volatile u_char *bt848; volatile u_long *btl_reg; u_short *bts_reg; #ifdef METEOR_DEALLOC_ABOVE int temp; #endif unit = UNIT(minor(dev)); if (unit >= NBKTR) /* unit out of range */ return(ENXIO); bktr = &(brooktree[unit]); bktr->flags &= ~METEOR_OPEN; bktr->flags &= ~(METEOR_SINGLE | METEOR_WANT_MASK); bktr->flags &= ~(METEOR_CAP_MASK|METEOR_WANT_MASK); bt848 = bktr->base; bts_reg = (u_short *) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 0; bt848[BKTR_CAP_CTL] = 0; bktr->dma_prog_loaded = 0; bt848[BKTR_TDEC] = 0; btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 0; bt848[BKTR_SRESET] = 0xf; btl_reg = (u_long *) &bt848[BKTR_INT_STAT] ; *btl_reg = 0xffffffff; #if defined( AUDIO_SUPPORT ) /* mute the audio by switching the mux */ set_audio( bktr, AUDIO_MUTE ); #endif /* AUDIO_SUPPORT */ return(0); } /* * */ int bktr_read( dev_t dev, struct uio *uio, int ioflag ) { bktr_reg_t *bktr; int unit; int status; int count; volatile u_char *bt848; u_short *bts_reg; unit = UNIT(minor(dev)); if (unit >= NBKTR) /* unit out of range */ return(ENXIO); bktr = &(brooktree[unit]); if (bktr->bigbuf == 0) /* no frame buffer allocated (ioctl failed) */ return(ENOMEM); if (bktr->flags & METEOR_CAP_MASK) return(EIO); /* already capturing */ bt848 = (u_char *) bktr->base; count = bktr->rows * bktr->cols * bktr->depth; if ((int) uio->uio_iov->iov_len < count) return(EINVAL); bktr->flags &= ~(METEOR_CAP_MASK|METEOR_WANT_MASK); /* Start capture */ bts_reg = (u_short *) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 0x1; *bts_reg = 0x3; status=tsleep((caddr_t)bktr, METPRI, "capturing", 0); if (!status) /* successful capture */ status = uiomove((caddr_t)bktr->bigbuf, count, uio); else printf ("meteor%d: read: tsleep error %d\n", unit, status); bktr->flags &= ~(METEOR_SINGLE | METEOR_WANT_MASK); return(status); } /* * */ int bktr_write( dev_t dev, struct uio *uio, int ioflag ) { return(0); } /* * */ int bktr_ioctl( dev_t dev, int cmd, caddr_t arg, int flag, struct proc *pr ) { bktr_reg_t *bktr; int unit; int status; int count; int tmp_int; volatile u_char *bt848, c_temp; volatile u_short *bts_reg, s_temp; volatile u_long *btl_reg; unsigned int temp, temp1; unsigned int error; struct meteor_geomet *geo; struct meteor_counts *cnt; struct meteor_video *video; vm_offset_t buf; unit = UNIT(minor(dev)); if (unit >= NBKTR) /* unit out of range */ return(ENXIO); bktr = &(brooktree[unit]); if (bktr->bigbuf == 0) /* no frame buffer allocated (ioctl failed) */ return(ENOMEM); bt848 = bktr->base; switch (cmd) { case TVTUNER_SETCHNL: #if defined( AUDIO_SUPPORT ) set_audio( bktr, AUDIO_MUTE ); #endif /* AUDIO_SUPPORT */ temp = tv_channel( bktr, (int)*(unsigned long *)arg ); #if defined( AUDIO_SUPPORT ) set_audio( bktr, AUDIO_UNMUTE ); #endif /* AUDIO_SUPPORT */ if ( temp < 0 ) return EINVAL; *(unsigned long *)arg = temp; break; case TVTUNER_GETCHNL: *(unsigned long *)arg = bktr->tuner.channel; break; case TVTUNER_SETTYPE: temp = *(unsigned long *)arg; if ( (temp < CHNLSET_MIN) || (temp > CHNLSET_MAX) ) return EINVAL; bktr->tuner.chnlset = temp; break; case TVTUNER_GETTYPE: *(unsigned long *)arg = bktr->tuner.chnlset; break; case TVTUNER_GETSTATUS: temp = tuner_status( bktr ); *(unsigned long *)arg = temp & 0xff; break; case TVTUNER_SETFREQ: #if defined( AUDIO_SUPPORT_XXX ) set_audio( bktr, AUDIO_MUTE ); #endif /* AUDIO_SUPPORT */ temp = tv_freq( bktr, (int)*(unsigned long *)arg ); #if defined( AUDIO_SUPPORT_XXX ) set_audio( bktr, AUDIO_UNMUTE ); #endif /* AUDIO_SUPPORT */ if ( temp < 0 ) return EINVAL; *(unsigned long *)arg = temp; break; case TVTUNER_GETFREQ: *(unsigned long *)arg = bktr->tuner.frequency; break; case METEORSTATUS: /* get 7196 status */ c_temp = bt848[0]; temp = 0; if (!(c_temp & 0x40)) temp |= METEOR_STATUS_HCLK; if (!(c_temp & 0x10)) temp |= METEOR_STATUS_FIDT; *(u_short *)arg = temp; break; case METEORSINPUT: /* set input device */ switch(*(unsigned long *)arg & METEOR_DEV_MASK) { /* this is the RCA video input */ case 0: /* default */ case METEOR_INPUT_DEV0: bktr->flags = (bktr->flags & ~METEOR_DEV_MASK) | METEOR_DEV0; bt848[BKTR_IFORM] &= ~0x60; bt848[BKTR_IFORM] |= 0x60; bt848[BKTR_E_CONTROL] &= ~0x40; bt848[BKTR_O_CONTROL] &= ~0x40; #if defined( AUDIO_SUPPORT ) set_audio( bktr, AUDIO_MUTE ); set_audio( bktr, AUDIO_EXTERN ); set_audio( bktr, AUDIO_UNMUTE ); #endif /* AUDIO_SUPPORT */ break; /* this is the tuner input */ case METEOR_INPUT_DEV1: bktr->flags = (bktr->flags & ~METEOR_DEV_MASK) | METEOR_DEV1; bt848[BKTR_IFORM] &= ~0x60; bt848[BKTR_IFORM] |= 0x40; bt848[BKTR_E_CONTROL] &= ~0x40; bt848[BKTR_O_CONTROL] &= ~0x40; #if defined( AUDIO_SUPPORT ) set_audio( bktr, AUDIO_MUTE ); set_audio( bktr, AUDIO_TUNER ); set_audio( bktr, AUDIO_UNMUTE ); #endif /* AUDIO_SUPPORT */ break; /* this is the S-VHS input */ case METEOR_INPUT_DEV2: case METEOR_INPUT_DEV_SVIDEO: bktr->flags = (bktr->flags & ~METEOR_DEV_MASK) | METEOR_DEV2; bt848[BKTR_IFORM] &= ~0x60; bt848[BKTR_IFORM] |= 0x20; bt848[BKTR_E_CONTROL] |= 0x40; bt848[BKTR_O_CONTROL] |= 0x40; #if defined( AUDIO_SUPPORT ) set_audio( bktr, AUDIO_MUTE ); set_audio( bktr, AUDIO_EXTERN ); set_audio( bktr, AUDIO_UNMUTE ); #endif /* AUDIO_SUPPORT */ break; default: return EINVAL; } break; case METEORGINPUT: /* get input device */ *(u_long *)arg = bktr->flags & METEOR_DEV_MASK; break; case METEORSFMT: /* set input format */ switch(*(unsigned long *)arg & METEOR_FORM_MASK ) { case 0: /* default */ case METEOR_FMT_NTSC: bktr->flags = (bktr->flags & ~METEOR_FORM_MASK) | METEOR_NTSC; bt848[BKTR_IFORM] &= ~0x3; bt848[BKTR_IFORM] |= 1; break; case METEOR_FMT_AUTOMODE: bktr->flags = (bktr->flags & ~METEOR_FORM_MASK) | METEOR_AUTOMODE; bt848[BKTR_IFORM] &= ~0x3; break; default: return EINVAL; } break; case METEORGFMT: /* get input format */ *(u_long *)arg = bktr->flags & METEOR_FORM_MASK; break; case METEORSCOUNT: /* (re)set error counts */ cnt = (struct meteor_counts *) arg; bktr->fifo_errors = cnt->fifo_errors; bktr->dma_errors = cnt->dma_errors; bktr->frames_captured = cnt->frames_captured; bktr->even_fields_captured = cnt->even_fields_captured; bktr->odd_fields_captured = cnt->odd_fields_captured; break; case METEORGCOUNT: /* get error counts */ cnt = (struct meteor_counts *) arg; cnt->fifo_errors = bktr->fifo_errors; cnt->dma_errors = bktr->dma_errors; cnt->frames_captured = bktr->frames_captured; cnt->even_fields_captured = bktr->even_fields_captured; cnt->odd_fields_captured = bktr->odd_fields_captured; break; case METEORGVIDEO: video = (struct meteor_video *)arg; video->addr = bktr->video.addr; video->width = bktr->video.width; video->banksize = bktr->video.banksize; video->ramsize = bktr->video.ramsize; break; case METEORSVIDEO: video = (struct meteor_video *)arg; bktr->video.addr = video->addr; bktr->video.width = video->width; bktr->video.banksize = video->banksize; bktr->video.ramsize = video->ramsize; break; case METEORSFPS: set_fps(bktr, *(u_short *)arg); break; case METEORGFPS: *(u_short *)arg = bktr->fps; break; case METEORSHUE: /* set hue */ bt848[BKTR_HUE] = (*(u_char *) arg) & 0xff; break; case METEORGHUE: /* get hue */ *(u_char *)arg = bt848[BKTR_HUE]; break; case METEORSBRIG: /* set brightness */ bt848[BKTR_BRIGHT] = *(u_char *)arg & 0xff; break; case METEORGBRIG: /* get brightness */ *(u_char *)arg = bt848[BKTR_BRIGHT]; break; case METEORSCSAT: /* set chroma saturation */ temp = (int)*(u_char *)arg; bt848[BKTR_SAT_U_LO] = bt848[BKTR_SAT_V_LO] = (temp << 1) & 0xff; bt848[BKTR_E_CONTROL] &= ~0x3; /* clear U/V MSBs */ bt848[BKTR_O_CONTROL] &= ~0x3; /* clear U/V MSBs */ if ( temp & 0x80 ) { bt848[BKTR_E_CONTROL] |= 0x3; bt848[BKTR_O_CONTROL] |= 0x3; } break; case METEORGCSAT: /* get chroma saturation */ temp = (bt848[BKTR_SAT_V_LO] >> 1) & 0xff; if ( bt848[BKTR_E_CONTROL] & 0x01 ) temp |= 0x80; *(u_char *)arg = (u_char)temp; break; case METEORSCONT: /* set contrast */ temp = (int)*(u_char *)arg & 0xff; temp <<= 1; bt848[BKTR_CONTRAST_LO] = temp & 0xff; bt848[BKTR_E_CONTROL] &= ~0x4; bt848[BKTR_O_CONTROL] &= ~0x4; bt848[BKTR_E_CONTROL] |= ((temp & 0x100) >> 6 ) & 0x4 ; bt848[BKTR_O_CONTROL] |= ((temp & 0x100) >> 6 ) & 0x4 ; break; case METEORGCONT: /* get contrast */ temp = (int)bt848[BKTR_CONTRAST_LO] & 0xff; temp |= ((int)bt848[BKTR_O_CONTROL] & 0x04) << 6; *(u_char *)arg = (u_char)((temp >> 1) & 0xff); break; /* hue is a 2's compliment number, -90' to +89.3' in 0.7' steps */ case BT848_SHUE: /* set hue */ bt848[BKTR_HUE] = (u_char)(*(int*)arg & 0xff); break; case BT848_GHUE: /* get hue */ *(int*)arg = bt848[BKTR_HUE] & 0xff; break; /* brightness is a 2's compliment #, -50 to +%49.6% in 0.39% steps */ case BT848_SBRIG: /* set brightness */ bt848[BKTR_BRIGHT] = (u_char)(*(int *)arg & 0xff); break; case BT848_GBRIG: /* get brightness */ *(int *)arg = bt848[BKTR_BRIGHT] & 0xff; break; /* */ case BT848_SCSAT: /* set chroma saturation */ tmp_int = *(int*)arg; temp = bt848[BKTR_E_CONTROL] & 0xfc; temp1 = bt848[BKTR_O_CONTROL] & 0xfc; if ( tmp_int & 0x100 ) { temp |= 0x03; temp1 |= 0x03; } bt848[BKTR_SAT_U_LO] = (u_char)(tmp_int & 0xff); bt848[BKTR_SAT_V_LO] = (u_char)(tmp_int & 0xff); bt848[BKTR_E_CONTROL] = temp; bt848[BKTR_O_CONTROL] = temp1; break; case BT848_GCSAT: /* get chroma saturation */ tmp_int = (int)bt848[BKTR_SAT_V_LO] & 0xff; if ( bt848[BKTR_E_CONTROL] & 0x01 ) tmp_int |= 0x0100; *(int*)arg = tmp_int; break; /* */ case BT848_SVSAT: /* set chroma V saturation */ tmp_int = *(int*)arg; temp = bt848[BKTR_E_CONTROL] & 0xfe; temp1 = bt848[BKTR_O_CONTROL] & 0xfe; if ( tmp_int & 0x100 ) { temp |= 0x01; temp1 |= 0x01; } bt848[BKTR_SAT_V_LO] = (u_char)(tmp_int & 0xff); bt848[BKTR_E_CONTROL] = temp; bt848[BKTR_O_CONTROL] = temp1; break; case BT848_GVSAT: /* get chroma V saturation */ tmp_int = (int)bt848[BKTR_SAT_V_LO] & 0xff; if ( bt848[BKTR_E_CONTROL] & 0x01 ) tmp_int |= 0x0100; *(int*)arg = tmp_int; break; /* */ case BT848_SUSAT: /* set chroma U saturation */ tmp_int = *(int*)arg; temp = bt848[BKTR_E_CONTROL] & 0xfd; temp1 = bt848[BKTR_O_CONTROL] & 0xfd; if ( tmp_int & 0x100 ) { temp |= 0x02; temp1 |= 0x02; } bt848[BKTR_SAT_U_LO] = (u_char)(tmp_int & 0xff); bt848[BKTR_E_CONTROL] = temp; bt848[BKTR_O_CONTROL] = temp1; break; case BT848_GUSAT: /* get chroma U saturation */ tmp_int = (int)bt848[BKTR_SAT_U_LO] & 0xff; if ( bt848[BKTR_E_CONTROL] & 0x02 ) tmp_int |= 0x0100; *(int*)arg = tmp_int; break; /* */ case BT848_SCONT: /* set contrast */ tmp_int = *(int*)arg; temp = bt848[BKTR_E_CONTROL] & 0xfb; temp1 = bt848[BKTR_O_CONTROL] & 0xfb; if ( tmp_int & 0x100 ) { temp |= 0x04; temp1 |= 0x04; } bt848[BKTR_CONTRAST_LO] = (u_char)(tmp_int & 0xff); bt848[BKTR_E_CONTROL] = temp; bt848[BKTR_O_CONTROL] = temp1; break; case BT848_GCONT: /* get contrast */ tmp_int = (int)bt848[BKTR_CONTRAST_LO] & 0xff; if ( bt848[BKTR_E_CONTROL] & 0x04 ) tmp_int |= 0x0100; *(int*)arg = tmp_int; break; case BT848_SCBARS: /* set colorbar output */ bt848[BKTR_COLOR_CTL] |= 0x40; break; case BT848_CCBARS: /* clear colorbar output */ bt848[BKTR_COLOR_CTL] &= ~0x40; break; #if defined( AUDIO_SUPPORT ) case BT848_SAUDIO: /* set audio channel */ if ( set_audio( bktr, *(int*)arg ) < 0 ) return EIO; break; case BT848_GAUDIO: /* get audio channel */ temp = bktr->audio_mux_select; if ( bktr->audio_mute_state == TRUE ) temp |= AUDIO_MUTE; *(int*)arg = temp; break; case BT848_SBTSC: /* set audio channel */ if ( set_BTSC( bktr, *(int*)arg ) < 0 ) return EIO; break; #endif /* AUDIO_SUPPORT */ case METEORSSIGNAL: bktr->signal = *(int *) arg; bktr->proc = pr; break; case METEORGSIGNAL: *(int *)arg = bktr->signal; break; case METEORCAPTUR: temp = bktr->flags; switch (*(int *) arg) { case METEOR_CAP_SINGLE: if (bktr->bigbuf==0) /* no frame buffer allocated */ return(ENOMEM); /* if (temp & METEOR_CAP_MASK) return(EIO); already capturing */ start_capture(bktr, METEOR_SINGLE); bktr->flags |= METEOR_SINGLE; bktr->flags &= ~METEOR_WANT_MASK; /* wait for capture to complete */ btl_reg = (u_long *) &bt848[BKTR_INT_STAT]; *btl_reg = 0xffffffff; btl_reg = (u_long *) &bt848[BKTR_GPIO_OUT_EN]; *btl_reg = 1; bts_reg = (u_short *) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 0x1; *bts_reg = bktr->capcontrol; btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 1 << 23 | 1 << 11 | 2 | 1; error=tsleep((caddr_t)bktr, METPRI, "capturing", hz); if (error) { btl_reg = (u_long *) &bt848[BKTR_RISC_COUNT]; printf("bktr%d: ioctl: tsleep error %d %x\n", unit, error, *btl_reg); } bktr->flags &= ~(METEOR_SINGLE|METEOR_WANT_MASK); btl_reg = (u_long *) &bt848[BKTR_INT_STAT]; break; case METEOR_CAP_CONTINOUS: if (bktr->bigbuf==0) /* no frame buffer allocated */ return(ENOMEM); if (temp & METEOR_CAP_MASK) return(EIO); /* already capturing */ start_capture(bktr, METEOR_CONTIN); btl_reg = (u_long *) &bt848[BKTR_INT_STAT]; *btl_reg = *btl_reg; bts_reg = (u_short *) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 1; *bts_reg = bktr->capcontrol; btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 1 << 23 | 1 << 11 | 2 | 1; #if 0 dump_bt848( bt848 ); #endif /* 0 */ break; case METEOR_CAP_STOP_CONT: if (bktr->flags & METEOR_CONTIN) { /* turn off capture */ btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 0; bts_reg = (u_short *)&bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 0; bktr->flags &= ~(METEOR_CONTIN|METEOR_WANT_MASK); } } break; case METEORSETGEO: geo = (struct meteor_geomet *) arg; error = 0; /* 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("bktr%d: ioctl: Geometry odd or even only.\n", unit); return EINVAL; } /* set/clear even/odd flags */ if (geo->oformat & METEOR_GEO_ODD_ONLY) bktr->flags |= METEOR_ONLY_ODD_FIELDS; else bktr->flags &= ~METEOR_ONLY_ODD_FIELDS; if (geo->oformat & METEOR_GEO_EVEN_ONLY) bktr->flags |= METEOR_ONLY_EVEN_FIELDS; else bktr->flags &= ~METEOR_ONLY_EVEN_FIELDS; /* can't change parameters while capturing */ /* XXX: if (bktr->flags & METEOR_CAP_MASK) return(EBUSY); */ if ((geo->columns & 0x3fe) != geo->columns) { printf( "bktr%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( "bktr%d: ioctl: %d: rows too large or not even.\n", unit, geo->rows); error = EINVAL; } if (geo->frames > 32) { printf("bktr%d: ioctl: too many frames.\n", unit); error = EINVAL; } if (error) return error; bktr->dma_prog_loaded = 0; bts_reg = (u_short *) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 0; btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 0; if (temp=geo->rows * geo->columns * geo->frames * 2) { 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 ((int) temp > bktr->alloc_pages && bktr->video.addr == 0) { buf = get_bktr_mem(unit, temp*PAGE_SIZE); if (buf != 0) { kmem_free(kernel_map, bktr->bigbuf, (bktr->alloc_pages * PAGE_SIZE)); bktr->bigbuf = buf; bktr->alloc_pages = temp; if (bootverbose) printf( "meteor%d: ioctl: Allocating %d bytes\n", unit, temp*PAGE_SIZE); } else { error = ENOMEM; } } } if (error) return error; bktr->rows = geo->rows; bktr->cols = geo->columns; bktr->frames = geo->frames; /* horizontal scale */ /* temp = ((910.0/( (float) bktr->cols *1.21875)) - 1.0) * 4096.0;*/ /* temp = ((910.0/( (float) bktr->cols *1.212)) - 1.0) * 4096.0; */ temp = ((910.0/( (float) bktr->cols *1.21875)) - 1.0) * 4096.0; /* temp = ((754.0/(float) bktr->cols) - 1 ) * 4096.0;*/ bt848[BKTR_E_HSCALE_LO] = temp & 0xff; bt848[BKTR_O_HSCALE_LO] = temp & 0xff; bt848[BKTR_E_HSCALE_HI] = ( temp >> 8 ) & 0xff; bt848[BKTR_O_HSCALE_HI] = ( temp >> 8 ) & 0xff; /* horizontal active */ temp = bktr->cols; bt848[BKTR_E_HACTIVE_LO] = temp & 0xff; bt848[BKTR_O_HACTIVE_LO] = temp & 0xff; bt848[BKTR_EVEN_CROP] &= ~0x3; bt848[BKTR_ODD_CROP] &= ~0x3; bt848[BKTR_EVEN_CROP] |= (temp >> 8 ) & 0x3; bt848[BKTR_ODD_CROP] |= (temp >> 8 ) & 0x3; /* horizontal delay */ temp = ((135.0/754.0) * (float) bktr->cols) ; temp = temp + 2; temp = temp & 0x3fe; bt848[BKTR_E_DELAY_LO] = temp & 0xff; bt848[BKTR_O_DELAY_LO] = temp & 0xff; bt848[BKTR_EVEN_CROP] &= ~0xc; bt848[BKTR_ODD_CROP] &= ~0xc; bt848[BKTR_EVEN_CROP] |= (temp >> 6) & 0xc; bt848[BKTR_ODD_CROP] |= (temp >> 6) & 0xc; /* vscale */ if (geo->oformat & METEOR_GEO_ODD_ONLY || geo->oformat & METEOR_GEO_EVEN_ONLY) { tmp_int = 65536.0 - (((240.0/(float) bktr->rows) - 1.0) * 512.0); } else { tmp_int = 65536 - (((480.0/(float) bktr->rows) - 1.0) * 512); } tmp_int &= 0x1fff; /* Vertical scaling */ bt848[BKTR_E_VSCALE_LO] = tmp_int & 0xff; bt848[BKTR_O_VSCALE_LO] = tmp_int & 0xff; bt848[BKTR_E_VSCALE_HI] &= ~0x1f; bt848[BKTR_O_VSCALE_HI] &= ~0x1f; bt848[BKTR_E_VSCALE_HI] |= (tmp_int >> 8) & 0x1f; bt848[BKTR_O_VSCALE_HI] |= (tmp_int >> 8) & 0x1f; bktr->format = METEOR_GEO_YUV_422; switch (geo->oformat & METEOR_GEO_OUTPUT_MASK) { case 0: /* default */ case METEOR_GEO_RGB16: bktr->format = METEOR_GEO_RGB16; bktr->depth = 2; break; case METEOR_GEO_RGB24: bktr->format = METEOR_GEO_RGB24; bktr->depth = 4; break; case METEOR_GEO_YUV_422: bktr->format = METEOR_GEO_YUV_422; break; case METEOR_GEO_YUV_PACKED: bktr->format = METEOR_GEO_YUV_PACKED; break; } /* if (geo->oformat & METEOR_GEO_YUV_12 ) bktr->format |= METEOR_GEO_YUV_12; else if (geo->oformat & METEOR_GEO_YUV_9 ) bktr->format |= METEOR_GEO_YUV_9; */ if (bktr->flags & METEOR_CAP_MASK) { if (bktr->flags & (METEOR_CONTIN|METEOR_SYNCAP)) { switch(bktr->flags & METEOR_ONLY_FIELDS_MASK) { case METEOR_ONLY_ODD_FIELDS: bktr->flags |= METEOR_WANT_ODD; break; case METEOR_ONLY_EVEN_FIELDS: bktr->flags |= METEOR_WANT_EVEN; break; default: bktr->flags |= METEOR_WANT_MASK; break; } start_capture(bktr, METEOR_CONTIN); btl_reg = (u_long *) &bt848[BKTR_INT_STAT]; *btl_reg = *btl_reg; bts_reg = (u_short *)&bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 0x1; *bts_reg = bktr->capcontrol; btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 1 << 23 | 2 | 1; } } break; /* end of METEORSETGEO */ default: return ENODEV; } return 0; } /* * */ int bktr_mmap( dev_t dev, int offset, int nprot ) { int unit; bktr_reg_t *bktr; unit = UNIT(minor(dev)); if (unit >= NBKTR) /* at this point could this happen? */ return(-1); bktr = &(brooktree[unit]); if (nprot & PROT_EXEC) return -1; if (offset >= bktr->alloc_pages * PAGE_SIZE) return -1; return i386_btop(vtophys(bktr->bigbuf) + offset); } /****************************************************************************** * bt848 RISC programming routines: */ /* * */ static int dump_bt848( volatile u_char *bt848 ) { u_long *bt_long; u_short *bt_short; int r[60]={ 4, 8, 0xc, 0x8c, 0x10, 0x90, 0x14, 0x94, 0x18, 0x98, 0x1c, 0x9c, 0x20, 0xa0, 0x24, 0xa4, 0x28, 0x2c, 0xac, 0x30, 0x34, 0x38, 0x3c, 0x40, 0xc0, 0x48, 0x4c, 0xcc, 0x50, 0xd0, 0xd4, 0x60, 0x64, 0x68, 0x6c, 0xec, 0xd8, 0xdc, 0xe0, 0xe4, 0, 0, 0, 0 }; int i; for (i = 0; i < 40; i+=4) { printf(" Reg:value : \t%x:%x \t%x:%x \t %x:%x \t %x:%x\n", r[i], bt848[r[i]], r[i+1], bt848[r[i+1]], r[i+2], bt848[r[i+2]], r[i+3], bt848[r[i+3]]); } bt_long = (u_long *) &bt848[BKTR_INT_STAT]; printf(" Reg 100 %x \n", *bt_long); bt_long = (u_long *) &bt848[BKTR_INT_MASK]; printf(" Reg 104 %x \n", *bt_long); bt_long = (u_long *) &bt848[BKTR_GPIO_DMA_CTL]; printf(" Reg 10C %x \n", *bt_long); return 0; } /* * build write instruction */ #define BKTR_FM1 0x6 #define BKTR_FM3 0xe #define BKTR_VRE 0x4 #define BKTR_VRO 0xC #define BKTR_PXV 0x0 #define BKTR_EOL 0x1 #define BKTR_SOL 0x2 #define OP_WRITE 0x1 << 28 #define OP_WRITEC 0x5 << 28 #define OP_JUMP 0x7 << 28 #define OP_SYNC 0x8 << 28 #define OP_WRITE123 0x9 << 28 #define OP_WRITES123 0xb << 28 #define OP_SOL 1 << 27 #define OP_EOL 1 << 26 static void rgb_prog( bktr_reg_t * bktr, char i_flag, int cols, int rows, int pixel_width, int interlace ) { int i; int byte_count; volatile unsigned int inst; volatile unsigned int inst2; volatile unsigned int inst3; volatile u_long target_buffer, buffer; volatile u_char *bt848, *bt_reg; volatile u_short *bts_reg; volatile u_long pitch; volatile u_long *dma_prog, *btl_reg, *t_test; int b, c; bt848 = bktr->base; /* color format : rgb32 */ if (bktr->depth == 4) bt848[BKTR_COLOR_FMT] = 0; else bt848[BKTR_COLOR_FMT] = 0x33; bt848[BKTR_COLOR_CTL] = 0x40; bt848[BKTR_COLOR_CTL] = 0x10; #if 0 bt848[0x10] = 0x1C; bt848[0x90] = 0x1C; #endif bt848[BKTR_VBI_PACK_SIZE] = 0; bt848[BKTR_VBI_PACK_DEL] = 0; bt848[BKTR_ADC] = 0x81; bt848[BKTR_COLOR_CTL] = 0x20; bt848[BKTR_E_VSCALE_HI] |= 0xc0; bt848[BKTR_O_VSCALE_HI] |= 0xc0; bktr->capcontrol = 3 << 2 | 3; dma_prog = (u_long *) bktr->dma_prog; /* Construct Write */ bt_enable_cnt = 0; b = (cols * pixel_width ) / 2; /* write, sol, eol */ inst = OP_WRITE | OP_SOL | bt_enable_cnt << 12 | (b); inst2 = OP_WRITE | bt_enable_cnt << 12 | (cols * pixel_width/2); /* write , sol, eol */ inst3 = OP_WRITE | OP_EOL | bt_enable_cnt << 12 | (b); if (bktr->video.addr) { target_buffer = (u_long) bktr->video.addr; pitch = bktr->video.width; } else { target_buffer = (u_long) vtophys(bktr->bigbuf); pitch = cols*pixel_width; } buffer = target_buffer; /* contruct sync : for video packet format */ *dma_prog++ = OP_SYNC | 0xC << 24 | 1 << 15 | BKTR_FM1; /* sync, mode indicator packed data */ *dma_prog++ = 0; /* NULL WORD */ for (i = 0; i < (rows/interlace); i++) { *dma_prog++ = inst; *dma_prog++ = target_buffer; *dma_prog++ = inst3; *dma_prog++ = target_buffer + b; target_buffer += interlace*pitch; } switch (i_flag) { case 1: /* sync vre */ *dma_prog++ = OP_SYNC | 0xC << 24 | 1 << 24 | BKTR_VRE; *dma_prog++ = 0; /* NULL WORD */ *dma_prog++ = OP_JUMP | 0xC << 24; *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); return; case 2: /* sync vre */ *dma_prog++ = OP_SYNC | 1 << 24 | 1 << 20 | BKTR_VRO; *dma_prog++ = 0; /* NULL WORD */ *dma_prog++ = OP_JUMP; *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); return; case 3: /* sync vre */ *dma_prog++ = OP_SYNC | 0xC << 24 | 1 << 24 | 1 << 15 | BKTR_VRO; *dma_prog++ = 0; /* NULL WORD */ *dma_prog++ = OP_JUMP | 0xc << 24 ; *dma_prog = (u_long ) vtophys(bktr->odd_dma_prog); break; } if (interlace == 2) { target_buffer = (u_long) buffer + pitch; dma_prog = (u_long *) bktr->odd_dma_prog; /* sync vre IRQ bit */ *dma_prog++ = OP_SYNC | 0xc << 24 | 1 << 15 | BKTR_FM1; *dma_prog++ = 0; /* NULL WORD */ for (i = 0; i < (rows/interlace); i++) { *dma_prog++ = inst; *dma_prog++ = target_buffer; *dma_prog++ = inst3; *dma_prog++ = target_buffer + b; target_buffer += interlace * pitch; } } /* sync vre IRQ bit */ *dma_prog++ = OP_SYNC | 0xc << 24 | 1 << 24 | 1 << 15 | BKTR_VRE; *dma_prog++ = 0; /* NULL WORD */ *dma_prog++ = OP_JUMP | 0xc << 24; *dma_prog++ = (u_long ) vtophys(bktr->dma_prog) ; *dma_prog++ = 0; /* NULL WORD */ } /* * */ static void yuvpack_prog( bktr_reg_t *bktr, char i_flag, int cols, int rows, int interlace ) { int i; int byte_count; volatile unsigned int inst; volatile unsigned int inst2; volatile unsigned int inst3; volatile u_long target_buffer, buffer; volatile u_char *bt848, *bt_reg; volatile u_short *bts_reg; volatile u_long *dma_prog, *btl_reg; int b; bt848 = bktr->base; /* color format : yuvpack */ bt848[BKTR_COLOR_FMT] = 0x44; bt848[BKTR_E_SCLOOP] |= 0x40; /* enable chroma comb */ bt848[BKTR_O_SCLOOP] |= 0x40; bt848[BKTR_COLOR_CTL] = 0x30; bt848[BKTR_ADC] = 0x81; bktr->capcontrol = 1 << 6 | 1 << 4 | 1 << 2 | 3; bktr->capcontrol = 1 << 5 | 1 << 4 | 1 << 2 | 3; dma_prog = (u_long *) bktr->dma_prog; /* Construct Write */ bt_enable_cnt = 0; /* write , sol, eol */ inst = OP_WRITE | OP_SOL | 0xf << 16 | bt_enable_cnt << 12 | (cols*2); /* write , sol, eol */ inst3 = OP_WRITE | OP_EOL | 0xf << 16 | bt_enable_cnt << 12 | (cols); inst2 = OP_WRITE | bt_enable_cnt << 12 | (cols ); if (bktr->video.addr) target_buffer = (u_long) bktr->video.addr; else target_buffer = (u_long) vtophys(bktr->bigbuf); buffer = target_buffer; /* contruct sync : for video packet format */ /* sync, mode indicator packed data */ *dma_prog++ = OP_SYNC | 1 << 24 | 1 << 15 | BKTR_FM1; *dma_prog++ = 0; /* NULL WORD */ b = cols; for (i = 0; i < (rows/interlace); i++) { *dma_prog++ = inst; *dma_prog++ = target_buffer; *dma_prog++ = inst3; *dma_prog++ = target_buffer + b; target_buffer += interlace*(cols * 2); } switch (i_flag) { case 1: /* sync vre */ *dma_prog++ = OP_SYNC | 1 << 24 | BKTR_VRE; *dma_prog++ = 0; /* NULL WORD */ *dma_prog++ = OP_JUMP; *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); return; case 2: /* sync vre */ *dma_prog++ = OP_SYNC | 1 << 24 | 1 << 20 | BKTR_VRO; *dma_prog++ = 0; /* NULL WORD */ *dma_prog++ = OP_JUMP; *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); return; case 3: /* sync vre */ *dma_prog++ = OP_SYNC | 1 << 24 | 0xf << 16 | BKTR_VRE; *dma_prog++ = 0; /* NULL WORD */ *dma_prog++ = OP_JUMP ; *dma_prog = (u_long ) vtophys(bktr->odd_dma_prog); break; } if (interlace == 2) { target_buffer = (u_long) buffer + cols*2; dma_prog = (u_long * ) bktr->odd_dma_prog; /* sync vre */ *dma_prog++ = OP_SYNC | 1 << 24 | 0xf << 16 | 1 << 15 | BKTR_FM1; *dma_prog++ = 0; /* NULL WORD */ for (i = 0; i < (rows/interlace) ; i++) { *dma_prog++ = inst; *dma_prog++ = target_buffer; *dma_prog++ = inst3; *dma_prog++ = target_buffer + b; target_buffer += interlace * ( cols*2); } } /* sync vre IRQ bit */ *dma_prog++ = OP_SYNC | 1 << 24 | 0xf << 16 | BKTR_VRO; *dma_prog++ = 0; /* NULL WORD */ *dma_prog++ = OP_JUMP | 0xf << 16; *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); *dma_prog++ = OP_JUMP; *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); *dma_prog++ = 0; /* NULL WORD */ } /* * */ static void yuv422_prog( bktr_reg_t * bktr, char i_flag, int cols, int rows, int interlace ){ int i, j; int byte_count; volatile unsigned int inst; volatile unsigned int inst2; volatile unsigned int instskip, instskip2, instskip3; volatile unsigned int inst3; volatile u_long target_buffer, t1, buffer; volatile u_char *bt848, *bt_reg; volatile u_short *bts_reg; volatile u_long *dma_prog, *btl_reg; int b, b1; bt848 = bktr->base; dma_prog = (u_long *) bktr->dma_prog; bktr->capcontrol = 1 << 6 | 1 << 4 | 3; bt848[BKTR_ADC] = 0x81 ; bt848[BKTR_OFORM] = 0x00; bt848[BKTR_E_CONTROL] |= 0x20; /* disable luma decimation */ bt848[BKTR_O_CONTROL] |= 0x20; bt848[BKTR_E_SCLOOP] |= 0x40; /* chroma agc enable */ bt848[BKTR_O_SCLOOP] |= 0x40; bt848[BKTR_E_VSCALE_HI] |= 0xc0; /* luma comb and comb enable */ bt848[BKTR_O_VSCALE_HI] |= 0xc0; bt848[BKTR_COLOR_FMT] = 0x88; bt848[BKTR_COLOR_CTL] = 0x10; /* disable gamma correction */ bt_enable_cnt = 0; /* Construct Write */ inst = OP_WRITE123 | OP_SOL | OP_EOL | bt_enable_cnt << 12 | (cols); inst2 = OP_WRITES123 | OP_SOL | OP_EOL | bt_enable_cnt << 12 | (cols); if (bktr->video.addr) target_buffer = (u_long) bktr->video.addr; else target_buffer = (u_long) vtophys(bktr->bigbuf); buffer = target_buffer; t1 = target_buffer; /* contruct sync : for video packet format */ *dma_prog++ = OP_SYNC | 0xC << 24 | 1 << 15 | BKTR_FM3; /*sync, mode indicator packed data*/ *dma_prog++ = 0; /* NULL WORD */ for (i = 0; i < (rows/interlace ); i++) { *dma_prog++ = inst; *dma_prog++ = cols/2 | cols/2 << 16; *dma_prog++ = target_buffer; *dma_prog++ = t1 + (cols*rows) + i*cols/2 * interlace; *dma_prog++ = t1 + (cols*rows) + (cols*rows/2) + i*cols/2 * interlace; target_buffer += interlace*cols; } switch (i_flag) { case 1: *dma_prog++ = OP_SYNC | 0xC << 24 | 1 << 24 | BKTR_VRE; /*sync vre*/ *dma_prog++ = 0; /* NULL WORD */ *dma_prog++ = OP_JUMP | 0xc << 24; *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); return; break; case 2: *dma_prog++ = OP_SYNC | 0xC << 24 | 1 << 24 | BKTR_VRO; /*sync vre*/ *dma_prog++ = 0; /* NULL WORD */ *dma_prog++ = OP_JUMP; *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); return; break; case 3: *dma_prog++ = OP_SYNC | 0xc << 24 | 1 << 15 | BKTR_VRO; *dma_prog++ = 0; /* NULL WORD */ *dma_prog++ = OP_JUMP | 0xc << 24 ; *dma_prog = (u_long ) vtophys(bktr->odd_dma_prog); break; } if (interlace == 2) { dma_prog = (u_long * ) bktr->odd_dma_prog; target_buffer = (u_long) buffer + cols; t1 = target_buffer + cols/2; *dma_prog++ = OP_SYNC | 0xc << 24 | 1 << 24 | 1 << 15 | BKTR_FM3; *dma_prog++ = 0; /* NULL WORD */ for (i = 0; i < (rows/interlace ) ; i++) { *dma_prog++ = inst; *dma_prog++ = cols/2 | cols/2 << 16; *dma_prog++ = target_buffer; *dma_prog++ = t1 + (cols*rows) + i*cols/2 * interlace; *dma_prog++ = t1 + (cols*rows) + (cols*rows/2) + i*cols/2 * interlace; target_buffer += interlace*cols; } } *dma_prog++ = OP_SYNC | 0xC << 24 | 1 << 24 | BKTR_VRE; *dma_prog++ = 0; /* NULL WORD */ *dma_prog++ = OP_JUMP | 0xC << 24;; *dma_prog++ = (u_long ) vtophys(bktr->dma_prog) ; *dma_prog++ = 0; /* NULL WORD */ } /* * */ static void build_dma_prog( bktr_reg_t * bktr, char i_flag ) { int i; int pixel_width, rows, cols, byte_count, interlace; volatile unsigned int inst; volatile unsigned int inst2; volatile unsigned int inst3; volatile u_long target_buffer; volatile u_char *bt848, *bt_reg; volatile u_short *bts_reg; volatile u_long *dma_prog, *btl_reg; int b; bt848 = bktr->base; btl_reg = (u_long *) &bt848[BKTR_INT_MASK] ; *btl_reg = 0; bts_reg = (u_short * ) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg &= ~3; /* capture control */ switch (i_flag) { case 1: bktr->bktr_cap_ctl = 0x11; bt848[BKTR_CAP_CTL] = 0x11; bt848[BKTR_E_VSCALE_HI] &= ~0x20; bt848[BKTR_O_VSCALE_HI] &= ~0x20; interlace = 1; break; case 2: bktr->bktr_cap_ctl = 0x12; bt848[BKTR_CAP_CTL] = 0x12; bt848[BKTR_E_VSCALE_HI] &= ~0x20; bt848[BKTR_O_VSCALE_HI] &= ~0x20; interlace = 1; break; default: bktr->bktr_cap_ctl = 0x13; bt848[BKTR_CAP_CTL] = 0x13; bt848[BKTR_E_VSCALE_HI] |= 0x20; bt848[BKTR_O_VSCALE_HI] |= 0x20; interlace = 2; break; } btl_reg = (u_long *) &bt848[BKTR_RISC_STRT_ADD] ; *btl_reg = vtophys(bktr->dma_prog); pixel_width = bktr->depth; rows = bktr->rows; cols = bktr->cols; if (bktr->format == METEOR_GEO_RGB24 || bktr->format == METEOR_GEO_RGB16) { rgb_prog(bktr, i_flag, cols, rows, pixel_width, interlace); return; } if (bktr->format == METEOR_GEO_YUV_422 ){ yuv422_prog(bktr, i_flag, cols, rows, interlace); return; } if (bktr->format == METEOR_GEO_YUV_PACKED ){ yuvpack_prog(bktr, i_flag, cols, rows, interlace); return; } return; } /****************************************************************************** * video & video capture specific routines: */ /* * */ static void start_capture( bktr_reg_t *bktr, unsigned type ) { volatile u_char *bt848, *bt_reg, i_flag; volatile u_short *bts_reg; volatile u_long *btl_reg; bt848 = (u_char *) bktr->base; *bt848 = 0; btl_reg = (u_long *) &bt848[BKTR_INT_STAT]; *btl_reg = *btl_reg; bktr->flags |= type; switch(bktr->flags & METEOR_ONLY_FIELDS_MASK) { case METEOR_ONLY_EVEN_FIELDS: bktr->flags |= METEOR_WANT_EVEN; i_flag = 1; break; case METEOR_ONLY_ODD_FIELDS: bktr->flags |= METEOR_WANT_ODD; i_flag = 2; break; default: bktr->flags |= METEOR_WANT_MASK; i_flag = 3; break; } if (!bktr->dma_prog_loaded) { build_dma_prog(bktr, i_flag); bktr->dma_prog_loaded = 1; } /*XXX switch(bktr->flags & METEOR_ONLY_FIELDS_MASK) { default: *bts_reg |= 0xb; } */ btl_reg = (u_long *) &bt848[BKTR_RISC_STRT_ADD]; *btl_reg = vtophys(bktr->dma_prog); /*XXX bts_reg = (u_short *) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 0x3; */ } /* * */ static void set_fps( bktr_reg_t *bktr, u_short fps ) { volatile u_char *bt848, *bt_reg; volatile u_long *btl_reg; volatile u_short *bts_reg; bt848 = (u_char *) bktr->base; bts_reg = (u_short *) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 0; btl_reg = (u_long *) &bt848[BKTR_INT_STAT]; *btl_reg = 0xffffffff; bktr->fps = fps; if ( fps == 30 ) { bt848[BKTR_TDEC] = 0; return; } else { bt848[BKTR_TDEC] = (int) (((float) fps / 30.0) * 60.0) & 0x3f; bt848[BKTR_TDEC] |= 0x80; } if ( bktr->flags & METEOR_CAP_MASK ) { btl_reg = (u_long *) &bt848[BKTR_INT_STAT]; *btl_reg = 0xffffffff; btl_reg = (u_long *) &bt848[BKTR_RISC_STRT_ADD]; *btl_reg = vtophys(bktr->dma_prog); bts_reg = (u_short *) &bt848[BKTR_GPIO_DMA_CTL]; *bts_reg = 1; *bts_reg = bktr->capcontrol; btl_reg = (u_long *) &bt848[BKTR_INT_MASK]; *btl_reg = 1 << 11 | 2 | 1; } 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_bktr_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; } /****************************************************************************** * i2c primitives: */ /* delays for the I2C bus transactions */ #define NDELAY 0 #if defined ( ORIGINAL_DELAYS ) #define SDELAY 2 #define LDELAY 20 #else #define SDELAY 10 #define LDELAY 40 #endif /* ORIGINAL_DELAYS */ /* macros to show the details more clearly */ typedef volatile u_long* i2c_regptr_t; /* * primitives for the I2C clock phases */ static inline void DataLo_ClockLo( i2c_regptr_t bti2c, int delay ) { *bti2c = 0; if ( delay ) DELAY( delay ); } static inline void DataHi_ClockLo( i2c_regptr_t bti2c, int delay ) { *bti2c = 1; if ( delay ) DELAY( delay ); } static inline void DataLo_ClockHi( i2c_regptr_t bti2c, int delay ) { *bti2c = 2; if ( delay ) DELAY( delay ); } static inline void DataHi_ClockHi( i2c_regptr_t bti2c, int delay ) { *bti2c = 3; if ( delay ) DELAY( delay ); } static inline int DataRead( i2c_regptr_t bti2c ) { return ( *bti2c & 1 ); } /* forward reference */ static int i2cWrite( i2c_regptr_t, u_char ); /* * start an I2C bus transaction */ static int i2cStart( i2c_regptr_t bti2c, int address ) { #if defined( EXTRA_START ) /* ensure the proper starting state */ DataHi_ClockLo( bti2c, LDELAY ); /* release data */ DataHi_ClockHi( bti2c, LDELAY ); /* release clock */ #endif /* EXTRA_START */ DataLo_ClockHi( bti2c, LDELAY ); /* lower data */ DataLo_ClockLo( bti2c, LDELAY ); /* lower clock */ /* send the address of the device */ return i2cWrite( bti2c, address ); } /* * stop an I2C bus transaction */ static void i2cStop( i2c_regptr_t bti2c ) { DELAY( LDELAY ); DataLo_ClockLo( bti2c, LDELAY ); /* lower clock & data */ DataLo_ClockHi( bti2c, LDELAY ); /* release clock */ DataHi_ClockHi( bti2c, LDELAY ); /* release data */ } /* * place a '1' bit on the I2C bus */ static void i2cHi( i2c_regptr_t bti2c ) { DataHi_ClockLo( bti2c, LDELAY ); /* assert HI data */ DataHi_ClockHi( bti2c, LDELAY ); /* strobe clock */ DataHi_ClockLo( bti2c, LDELAY ); /* release clock */ } /* * place a '0' bit on the I2C bus */ static void i2cLo( i2c_regptr_t bti2c ) { DataLo_ClockLo( bti2c, LDELAY ); /* assert LO data */ DataLo_ClockHi( bti2c, LDELAY ); /* strobe clock */ DataLo_ClockLo( bti2c, LDELAY ); /* release clock */ } /* * give an 'ACK' to the slave */ static void i2cGrantAck( i2c_regptr_t bti2c ) { DataLo_ClockLo( bti2c, LDELAY ); /* assert LO data */ DataLo_ClockHi( bti2c, LDELAY ); /* strobe clock */ DataLo_ClockLo( bti2c, LDELAY ); /* remove clock */ DataHi_ClockLo( bti2c, NDELAY ); /* float data */ } /* * get an 'ACK' from the slave */ static int i2cAck( i2c_regptr_t bti2c ) { int acknowledge; DataHi_ClockLo( bti2c, LDELAY ); /* float data */ DataHi_ClockHi( bti2c, LDELAY ); /* strobe clock */ acknowledge = DataRead( bti2c ); /* read ACK bit */ DataHi_ClockLo( bti2c, LDELAY ); /* release clock */ return acknowledge; } /* * read a byte from the I2C bus */ static int i2cRead( i2c_regptr_t bti2c, int ack ) { int x; int byte; DataHi_ClockLo( bti2c, SDELAY ); /* float data */ for ( byte = 0, x = 7; x >= 0; --x ) { DataHi_ClockHi( bti2c, SDELAY ); /* strobe clock */ if ( DataRead( bti2c ) ) /* read data */ byte |= (1<= 0; --x ) (byte & (1<base[ BKTR_I2C_CONTROL ] /****************************************************************************** * card probe */ /* guaranteed address for any TSA5522 (PLL on all(?) tuners) */ #define TSA5522_WADDR 0xc2 #define TSA5522_RADDR 0xc3 /* address of BTSC/SAP decoder chip */ #define TDA9850_WADDR 0xb6 #define TDA9850_RADDR 0xb7 /* EEProm (128 * 8) on an STB card */ #define X24C01_WADDR 0xae #define X24C01_RADDR 0xaf /* EEProm (256 * 8) on a Hauppauge card */ #define PFC8582_WADDR 0xa0 #define PFC8582_RADDR 0xa1 /* * the data for each type of card */ #define NO_TUNER 0 #define TEMIC_TUNER 1 #define PHILIPS_TUNER 2 /* * Note: * these entried MUST be kept in the order defined by the CARD_XXX defines! */ struct CARDTYPE card_types[] = { /* CARD_UNKNOWN */ { "Unknown", NO_TUNER, 0, { 0, 0, 0, 0 } }, /* CARD_MIRO */ { "Miro TV", NO_TUNER, /** TEMIC_TUNER ??? */ 0, { 0x02, 0x01, 0x00, 0x00 } }, /* XXX ??? */ /* CARD_HAUPPAUGE */ { "Hauppauge WinCast/TV", PHILIPS_TUNER, 0, { 0x00, 0x02, 0x01, 0x01 } }, /* CARD_STB */ { "STB TV/PCI", TEMIC_TUNER, 0, { 0x00, 0x01, 0x02, 0x02 } }, /* CARD_INTEL */ { "Intel Smart Video III", NO_TUNER, 0, { 0, 0, 0, 0 } } }; /* * If probe_card() fails to detect the proper card on boot you can * override it by setting the following define to the card you are using: * #define OVERRIDE_CARD * * where is one of the card defines in the above array. */ #define PRESENT 0 #define ABSENT 1 static int probe_card( bktr_reg_t *bktr, int verbose ) { i2c_regptr_t bti2c; int status; #if defined( OVERRIDE_CARD ) bktr->card_type = OVERRIDE_CARD; goto end; #endif /* get the i2c register address */ bti2c = I2C_REGADDR(); /* look for a tuner */ status = i2cStart( bti2c, TSA5522_WADDR ); i2cStop( bti2c ); if ( status == ABSENT ) { bktr->card_type = CARD_INTEL; goto checkDBX; } /* look for a hauppauge card */ status = i2cStart( bti2c, PFC8582_WADDR ); i2cStop( bti2c ); if ( status == PRESENT ) { bktr->card_type = CARD_HAUPPAUGE; goto checkTuner; } /* look for an STB card */ status = i2cStart( bti2c, X24C01_WADDR ); i2cStop( bti2c ); if ( status == PRESENT ) { bktr->card_type = CARD_STB; goto checkTuner; } /* XXX FIXME: (how do I) look for a Miro card */ bktr->card_type = CARD_MIRO; checkTuner: /** * XXX FIXME: how can we differentiate TEMIC vs. PHILIPS tuners ??? */ status = i2cStart( bti2c, 0xc0 ); i2cStop( bti2c ); if ( status == PRESENT ) { card_types[ bktr->card_type ].tuner = TEMIC_TUNER; goto checkDBX; } status = i2cStart( bti2c, 0xc6 ); i2cStop( bti2c ); if ( status == PRESENT ) { card_types[ bktr->card_type ].tuner = PHILIPS_TUNER; goto checkDBX; } card_types[ bktr->card_type ].tuner = NO_TUNER; checkDBX: /* * probe for BTSC (dbx) chips. */ status = i2cStart( bti2c, TDA9850_WADDR ); i2cStop( bti2c ); if ( status == PRESENT ) card_types[ bktr->card_type ].dbx = 1; end: if ( verbose ) { printf( "%s", card_types[ bktr->card_type ].name ); if ( card_types[ bktr->card_type ].tuner ) printf( ", %s tuner", card_types[ bktr->card_type ].tuner == TEMIC_TUNER ? "Temic" : "Philips" ); if ( card_types[ bktr->card_type ].dbx ) printf( ", dbx stereo" ); printf( "\n" ); } return bktr->card_type; } #undef ABSENT #undef PRESENT #define TSA5522_BANDA band_addrs[card_types[bktr->card_type].tuner-1][0] #define TSA5522_BANDB band_addrs[card_types[bktr->card_type].tuner-1][1] #define TSA5522_BANDC band_addrs[card_types[bktr->card_type].tuner-1][2] u_char band_addrs[][3] = { /* BANDA BANDB BANDC */ { 0x02, 0x04, 0x01 }, /* TEMIC */ { 0xa0, 0x90, 0x30 } /* PHILIPS */ }; /****************************************************************************** * tuner specific routines: */ /* * bit 7: CONTROL BYTE = 1 * bit 6: CP = 0 moderate speed tuning, better FM * bit 5: T2 = 0 normal operation * bit 4: T1 = 0 normal operation * bit 3: T0 = 1 normal operation * bit 2: RSA = 1 62.5kHz * bit 1: RSB = 1 62.5kHz * bit 0: OS = 0 normal operation * * FIXME: create defines for the above bitfields. */ #if 0 #define TSA5522_CONTROL 0xce #else #define TSA5522_CONTROL 0x8e #endif /* scaling factor for frequencies expressed as ints */ #define FREQFACTOR 16 /* * Format: * entry 0: MAX legal channel * entry 1: IF frequency * expressed as fi{mHz} * 16, * eg 45.75mHz == 45.75 * 16 = 732 * entry 2: [place holder/future] * entry 3: base of channel record 0 * entry 3 + (x*3): base of channel record 'x' * entry LAST: NULL channel entry marking end of records * * Record: * int 0: base channel * int 1: frequency of base channel, * expressed as fb{mHz} * 16, * int 2: offset frequency between channels, * expressed as fo{mHz} * 16, */ /* * North American Broadcast Channels: * * 2: 55.25 mHz - 4: 67.25 mHz * 5: 77.25 mHz - 6: 83.25 mHz * 7: 175.25 mHz - 13: 211.25 mHz * 14: 471.25 mHz - 83: 885.25 mHz * * IF freq: 45.75 mHz */ #define OFFSET 6.00 int nabcst[] = { 83, (int)( 45.75 * FREQFACTOR), 0, 14, (int)(471.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), 7, (int)(175.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), 5, (int)( 77.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), 2, (int)( 55.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), 0 }; #undef OFFSET /* * North American Cable Channels, IRC: * * 2: 55.25 mHz - 4: 67.25 mHz * 5: 77.25 mHz - 6: 83.25 mHz * 7: 175.25 mHz - 13: 211.25 mHz * 14: 121.25 mHz - 22: 169.25 mHz * 23: 217.25 mHz - 94: 643.25 mHz * 95: 91.25 mHz - 99: 115.25 mHz * * IF freq: 45.75 mHz */ #define OFFSET 6.00 int irccable[] = { 99, (int)( 45.75 * FREQFACTOR), 0, 95, (int)( 91.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), 23, (int)(217.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), 14, (int)(121.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), 7, (int)(175.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), 5, (int)( 77.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), 2, (int)( 55.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), 0 }; #undef OFFSET /* * North American Cable Channels, HRC: * */ int hrccable[] = { 0, 0, 0, 0 }; /* * Western European channels: * */ int weurope[] = { 0, 0, 0, 0 }; int* freqTable[] = { NULL, nabcst, irccable, hrccable, weurope }; #define TBL_CHNL freqTable[ bktr->tuner.chnlset ][ x ] #define TBL_BASE_FREQ freqTable[ bktr->tuner.chnlset ][ x + 1 ] #define TBL_OFFSET freqTable[ bktr->tuner.chnlset ][ x + 2 ] static int frequency_lookup( bktr_reg_t* bktr, int channel ) { int x; /* check for "> MAX channel" */ x = 0; if ( channel > TBL_CHNL ) return -1; /* search the table for data */ for ( x = 3; TBL_CHNL; x += 3 ) { if ( channel >= TBL_CHNL ) { return (TBL_BASE_FREQ + ((channel-TBL_CHNL) * TBL_OFFSET)); } } /* not found, must be below the MIN channel */ return -1; } #undef TBL_OFFSET #undef TBL_BASE_FREQ #undef TBL_CHNL #define TBL_IF freqTable[ bktr->tuner.chnlset ][ 1 ] /* * set the frequency of the tuner */ static int tv_freq( bktr_reg_t* bktr, int frequency ) { i2c_regptr_t bti2c; u_char band; int N; int order; /* * select the band based on frequency * FIXME: do the cross-over points need to be set on a * tuner by tuner basis? */ if ( frequency < (160 * FREQFACTOR) ) band = TSA5522_BANDA; else if ( frequency < (454 * FREQFACTOR) ) band = TSA5522_BANDB; else band = TSA5522_BANDC; /* * N = 16 * { fRF(pc) + fIF(pc) } * where: * pc is picture carrier, fRF & fIF are in mHz * * frequency was passed in as mHz * 16 */ N = frequency + TBL_IF; /* get the i2c register address */ bti2c = I2C_REGADDR(); /* send the data to the TSA5522 */ i2cStart( bti2c, TSA5522_WADDR ); /* the data sheet wants the order set according to direction */ if ( frequency > bktr->tuner.frequency ) { i2cWrite( bti2c, (N >> 8) & 0x7f ); /* divisor MSB */ i2cWrite( bti2c, N & 0xff ); /* divisor LSB */ i2cWrite( bti2c, TSA5522_CONTROL ); /* control bits */ i2cWrite( bti2c, band ); /* band select */ } else { i2cWrite( bti2c, TSA5522_CONTROL ); /* control bits */ i2cWrite( bti2c, band ); /* band select */ i2cWrite( bti2c, (N >> 8) & 0x7f ); /* divisor MSB */ i2cWrite( bti2c, N & 0xff ); /* divisor LSB */ } i2cStop( bti2c ); bktr->tuner.frequency = frequency; return 0; } #undef TBL_IF /* * set the channel of the tuner */ static int tv_channel( bktr_reg_t* bktr, int channel ) { int frequency; /* calculate the frequency according to tuner type */ if ( (frequency = frequency_lookup( bktr, channel )) < 0 ) return -1; /* set the new frequency */ if ( tv_freq( bktr, frequency ) < 0 ) return -1; /* OK to update records */ bktr->tuner.channel = channel; return channel; } /* * get the status of the tuner */ static int tuner_status( bktr_reg_t* bktr ) { i2c_regptr_t bti2c; int status; /* get the i2c register address */ bti2c = I2C_REGADDR(); /* send the request to the TSA5522 */ i2cStart( bti2c, TSA5522_RADDR ); status = i2cRead( bti2c, 0 ); /* no ACK */ i2cStop( bti2c ); return status; } #if defined( AUDIO_SUPPORT ) /****************************************************************************** * audio specific routines: */ /* * */ #define AUDIOMUX_DISCOVER_NOT static int set_audio( bktr_reg_t *bktr, int cmd ) { volatile u_char *bt848; volatile u_char temp; volatile u_char idx; #if defined( AUDIOMUX_DISCOVER ) if ( cmd >= 200 ) cmd -= 200; else #endif /* AUDIOMUX_DISCOVER */ switch (cmd) { case AUDIO_TUNER: bktr->audio_mux_select = 0; break; case AUDIO_EXTERN: bktr->audio_mux_select = 1; break; case AUDIO_INTERN: bktr->audio_mux_select = 2; break; case AUDIO_MUTE: bktr->audio_mute_state = TRUE; /* set mute */ break; case AUDIO_UNMUTE: bktr->audio_mute_state = FALSE; /* clear mute */ break; default: printf("bktr: audio cmd error %02x\n", cmd); return -1; } bt848 = bktr->base; /* * Leave the upper bits of the GPIO port alone in case they control * something like the dbx or teletext chips. This doesn't guarantee * success, but follows the rule of least astonishment. */ bt848[BKTR_GPIO_OUT_EN] = 0x07; /* drive low 3 bits */ bt848[BKTR_GPIO_REG_INP] = 0xf8; /* read state of others */ #if defined( AUDIOMUX_DISCOVER ) printf("cmd: %d\n", cmd ); temp = bt848[BKTR_GPIO_DATA] & ~7; bt848[BKTR_GPIO_DATA] = temp | (cmd & 0xff); return 0; #endif /* AUDIOMUX_DISCOVER */ if ( bktr->audio_mute_state == TRUE ) idx = 3; else idx = bktr->audio_mux_select; temp = bt848[BKTR_GPIO_DATA] & ~7; /* mask off lower three bits */ bt848[BKTR_GPIO_DATA] = temp | card_types[bktr->card_type].audiomuxs[idx]; return 0; } /* * */ #define CON1ADDR 0x04 #define CON2ADDR 0x05 #define CON3ADDR 0x06 #define CON4ADDR 0x07 static int set_BTSC( bktr_reg_t *bktr, int control ) { i2c_regptr_t bti2c; /* get the i2c register address */ bti2c = I2C_REGADDR(); /* send the data to the TDA9850 BTSC */ if ( i2cStart( bti2c, TDA9850_WADDR ) ) goto fubar; if ( i2cWrite( bti2c, CON3ADDR ) ) goto fubar; if ( i2cWrite( bti2c, control ) ) goto fubar; i2cStop( bti2c ); return 0; fubar: i2cStop( bti2c ); return -1; } #endif /* AUDIO_SUPPORT */ /****************************************************************************** * magic: */ static bktr_devsw_installed = 0; static void bktr_drvinit( void *unused ) { dev_t dev; if ( ! bktr_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&bktr_cdevsw, NULL); bktr_devsw_installed = 1; } } SYSINIT(bktrdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,bktr_drvinit,NULL) #endif /* NBKTR > 0 */