diff options
Diffstat (limited to 'sys/dev/pci')
-rw-r--r-- | sys/dev/pci/bktr/bktr_audio.c | 634 | ||||
-rw-r--r-- | sys/dev/pci/bktr/bktr_audio.h | 85 | ||||
-rw-r--r-- | sys/dev/pci/bktr/bktr_card.c | 1268 | ||||
-rw-r--r-- | sys/dev/pci/bktr/bktr_card.h | 88 | ||||
-rw-r--r-- | sys/dev/pci/bktr/bktr_core.c | 4264 | ||||
-rw-r--r-- | sys/dev/pci/bktr/bktr_core.h | 97 | ||||
-rw-r--r-- | sys/dev/pci/bktr/bktr_os.c | 1703 | ||||
-rw-r--r-- | sys/dev/pci/bktr/bktr_os.h | 73 | ||||
-rw-r--r-- | sys/dev/pci/bktr/bktr_reg.h | 729 | ||||
-rw-r--r-- | sys/dev/pci/bktr/bktr_tuner.c | 1017 | ||||
-rw-r--r-- | sys/dev/pci/bktr/bktr_tuner.h | 104 |
11 files changed, 10062 insertions, 0 deletions
diff --git a/sys/dev/pci/bktr/bktr_audio.c b/sys/dev/pci/bktr/bktr_audio.c new file mode 100644 index 00000000000..1f437c4e527 --- /dev/null +++ b/sys/dev/pci/bktr/bktr_audio.c @@ -0,0 +1,634 @@ +/* $OpenBSD: bktr_audio.c,v 1.1 2001/03/28 03:27:09 fgsch Exp $ */ +/* $FreeBSD: src/sys/dev/bktr/bktr_audio.c,v 1.8 2000/10/31 13:09:56 roger Exp $ */ +/* + * This is part of the Driver for Video Capture Cards (Frame grabbers) + * and TV Tuner cards using the Brooktree Bt848, Bt848A, Bt849A, Bt878, Bt879 + * chipset. + * Copyright Roger Hardiman and Amancio Hasty. + * + * bktr_audio : This deals with controlling the audio on TV cards, + * controlling the Audio Multiplexer (audio source selector). + * controlling any MSP34xx stereo audio decoders. + * controlling any DPL35xx dolby surroud sound audio decoders. + * initialising TDA98xx audio devices. + * + */ + +/* + * 1. Redistributions of source code must retain the + * Copyright (c) 1997 Amancio Hasty, 1999 Roger Hardiman + * 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 and + * Roger Hardiman + * 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/vnode.h> + +#ifdef __FreeBSD__ + +#if (__FreeBSD_version < 500000) +#include <machine/clock.h> /* for DELAY */ +#endif + +#include <pci/pcivar.h> + +#if (__FreeBSD_version >=300000) +#include <machine/bus_memio.h> /* for bus space */ +#include <machine/bus.h> +#include <sys/bus.h> +#endif +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <sys/proc.h> +#include <dev/ic/bt8xx.h> /* NetBSD location of .h files */ +#include <dev/pci/bktr/bktr_reg.h> +#include <dev/pci/bktr/bktr_core.h> +#include <dev/pci/bktr/bktr_tuner.h> +#include <dev/pci/bktr/bktr_card.h> +#include <dev/pci/bktr/bktr_audio.h> +#else +#include <machine/ioctl_meteor.h> /* Traditional location of .h files */ +#include <machine/ioctl_bt848.h> /* extensions to ioctl_meteor.h */ +#include <dev/bktr/bktr_reg.h> +#include <dev/bktr/bktr_core.h> +#include <dev/bktr/bktr_tuner.h> +#include <dev/bktr/bktr_card.h> +#include <dev/bktr/bktr_audio.h> +#endif + +/* + * Prototypes for the GV_BCTV specific functions. + */ +void set_bctv_audio( bktr_ptr_t bktr ); +void bctv_gpio_write( bktr_ptr_t bktr, int port, int val ); +/*int bctv_gpio_read( bktr_ptr_t bktr, int port );*/ /* Not used */ + + + +/* + * init_audio_devices + * Reset any MSP34xx or TDA98xx audio devices. + */ +void init_audio_devices( bktr_ptr_t bktr ) { + + /* enable stereo if appropriate on TDA audio chip */ + if ( bktr->card.dbx ) + init_BTSC( bktr ); + + /* reset the MSP34xx stereo audio chip */ + if ( bktr->card.msp3400c ) + msp_dpl_reset( bktr, bktr->msp_addr ); + + /* reset the DPL35xx dolby audio chip */ + if ( bktr->card.dpl3518a ) + msp_dpl_reset( bktr, bktr->dpl_addr ); + +} + + +/* + * + */ +#define AUDIOMUX_DISCOVER_NOT +int +set_audio( bktr_ptr_t bktr, int cmd ) +{ + u_long temp; + volatile u_char idx; + +#if defined( AUDIOMUX_DISCOVER ) + if ( cmd >= 200 ) + cmd -= 200; + else +#endif /* AUDIOMUX_DISCOVER */ + + /* check for existance of audio MUXes */ + if ( !bktr->card.audiomuxs[ 4 ] ) + return( -1 ); + + switch (cmd) { + case AUDIO_TUNER: +#ifdef BKTR_REVERSEMUTE + bktr->audio_mux_select = 3; +#else + bktr->audio_mux_select = 0; +#endif + + if (bktr->reverse_mute ) + bktr->audio_mux_select = 0; + else + bktr->audio_mux_select = 3; + + 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("%s: audio cmd error %02x\n", bktr_name(bktr), + cmd); + return( -1 ); + } + + + /* Most cards have a simple audio multiplexer to select the + * audio source. The I/O_GV card has a more advanced multiplexer + * and requires special handling. + */ + if ( bktr->bt848_card == CARD_IO_GV ) { + set_bctv_audio( bktr ); + return( 0 ); + } + + /* Proceed with the simpler audio multiplexer code for the majority + * of Bt848 cards. + */ + + /* + * 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. + */ + + if ( bktr->audio_mute_state == TRUE ) { +#ifdef BKTR_REVERSEMUTE + idx = 0; +#else + idx = 3; +#endif + + if (bktr->reverse_mute ) + idx = 3; + else + idx = 0; + + } + else + idx = bktr->audio_mux_select; + + + temp = INL(bktr, BKTR_GPIO_DATA) & ~bktr->card.gpio_mux_bits; +#if defined( AUDIOMUX_DISCOVER ) + OUTL(bktr, BKTR_GPIO_DATA, temp | (cmd & 0xff)); + printf("%s: cmd: %d audio mux %x temp %x \n", bktr_name(bktr), + cmd, bktr->card.audiomuxs[ idx ], temp ); +#else + OUTL(bktr, BKTR_GPIO_DATA, temp | bktr->card.audiomuxs[ idx ]); +#endif /* AUDIOMUX_DISCOVER */ + + + + /* Some new Hauppauge cards do not have an audio mux */ + /* Instead we use the MSP34xx chip to select TV audio, Line-In */ + /* FM Radio and Mute */ + /* Examples of this are the Hauppauge 44xxx MSP34xx models */ + /* It is ok to drive both the mux and the MSP34xx chip. */ + /* If there is no mux, the MSP does the switching of the audio source */ + /* If there is a mux, it does the switching of the audio source */ + + if ((bktr->card.msp3400c) && (bktr->audio_mux_present == 0)) { + + if (bktr->audio_mute_state == TRUE ) { + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0000, 0x0000); /* volume to MUTE */ + } else { + if(bktr->audio_mux_select == 0) { /* TV Tuner */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0000, 0x7300); /* 0 db volume */ + if (bktr->msp_source_selected != 0) msp_autodetect(bktr); /* setup TV audio mode */ + bktr->msp_source_selected = 0; + } + if(bktr->audio_mux_select == 1) { /* Line In */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0000, 0x7300); /* 0 db volume */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x000d, 0x1900); /* scart prescale */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0008, 0x0220); /* SCART | STEREO */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0013, 0x0000); /* DSP In = SC1_IN_L/R */ + bktr->msp_source_selected = 1; + } + + if(bktr->audio_mux_select == 2) { /* FM Radio */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0000, 0x7300); /* 0 db volume */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x000d, 0x1900); /* scart prescale */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0008, 0x0220); /* SCART | STEREO */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0013, 0x0200); /* DSP In = SC2_IN_L/R */ + bktr->msp_source_selected = 2; + } + } + } + + + return( 0 ); +} + + +/* + * + */ +void +temp_mute( bktr_ptr_t bktr, int flag ) +{ + static int muteState = FALSE; + + if ( flag == TRUE ) { + muteState = bktr->audio_mute_state; + set_audio( bktr, AUDIO_MUTE ); /* prevent 'click' */ + } + else { + tsleep( BKTR_SLEEP, PZERO, "tuning", hz/8 ); + if ( muteState == FALSE ) + set_audio( bktr, AUDIO_UNMUTE ); + } +} + +/* address of BTSC/SAP decoder chip */ +#define TDA9850_WADDR 0xb6 +#define TDA9850_RADDR 0xb7 + + +/* registers in the TDA9850 BTSC/dbx chip */ +#define CON1ADDR 0x04 +#define CON2ADDR 0x05 +#define CON3ADDR 0x06 +#define CON4ADDR 0x07 +#define ALI1ADDR 0x08 +#define ALI2ADDR 0x09 +#define ALI3ADDR 0x0a + +/* + * initialise the dbx chip + * taken from the Linux bttv driver TDA9850 initialisation code + */ +void +init_BTSC( bktr_ptr_t bktr ) +{ + i2cWrite(bktr, TDA9850_WADDR, CON1ADDR, 0x08); /* noise threshold st */ + i2cWrite(bktr, TDA9850_WADDR, CON2ADDR, 0x08); /* noise threshold sap */ + i2cWrite(bktr, TDA9850_WADDR, CON3ADDR, 0x40); /* stereo mode */ + i2cWrite(bktr, TDA9850_WADDR, CON4ADDR, 0x07); /* 0 dB input gain? */ + i2cWrite(bktr, TDA9850_WADDR, ALI1ADDR, 0x10); /* wideband alignment? */ + i2cWrite(bktr, TDA9850_WADDR, ALI2ADDR, 0x10); /* spectral alignment? */ + i2cWrite(bktr, TDA9850_WADDR, ALI3ADDR, 0x03); +} + +/* + * setup the dbx chip + * XXX FIXME: alot of work to be done here, this merely unmutes it. + */ +int +set_BTSC( bktr_ptr_t bktr, int control ) +{ + return( i2cWrite( bktr, TDA9850_WADDR, CON3ADDR, control ) ); +} + +/* + * CARD_GV_BCTV specific functions. + */ + +#define BCTV_AUDIO_MAIN 0x10 /* main audio program */ +#define BCTV_AUDIO_SUB 0x20 /* sub audio program */ +#define BCTV_AUDIO_BOTH 0x30 /* main(L) + sub(R) program */ + +#define BCTV_GPIO_REG0 1 +#define BCTV_GPIO_REG1 3 + +#define BCTV_GR0_AUDIO_MODE 3 +#define BCTV_GR0_AUDIO_MAIN 0 /* main program */ +#define BCTV_GR0_AUDIO_SUB 3 /* sub program */ +#define BCTV_GR0_AUDIO_BOTH 1 /* main(L) + sub(R) */ +#define BCTV_GR0_AUDIO_MUTE 4 /* audio mute */ +#define BCTV_GR0_AUDIO_MONO 8 /* force mono */ + +void +set_bctv_audio( bktr_ptr_t bktr ) +{ + int data; + + switch (bktr->audio_mux_select) { + case 1: /* external */ + case 2: /* internal */ + bctv_gpio_write(bktr, BCTV_GPIO_REG1, 0); + break; + default: /* tuner */ + bctv_gpio_write(bktr, BCTV_GPIO_REG1, 1); + break; + } +/* switch (bktr->audio_sap_select) { */ + switch (BCTV_AUDIO_BOTH) { + case BCTV_AUDIO_SUB: + data = BCTV_GR0_AUDIO_SUB; + break; + case BCTV_AUDIO_BOTH: + data = BCTV_GR0_AUDIO_BOTH; + break; + case BCTV_AUDIO_MAIN: + default: + data = BCTV_GR0_AUDIO_MAIN; + break; + } + if (bktr->audio_mute_state == TRUE) + data |= BCTV_GR0_AUDIO_MUTE; + + bctv_gpio_write(bktr, BCTV_GPIO_REG0, data); + + return; +} + +/* gpio_data bit assignment */ +#define BCTV_GPIO_ADDR_MASK 0x000300 +#define BCTV_GPIO_WE 0x000400 +#define BCTV_GPIO_OE 0x000800 +#define BCTV_GPIO_VAL_MASK 0x00f000 + +#define BCTV_GPIO_PORT_MASK 3 +#define BCTV_GPIO_ADDR_SHIFT 8 +#define BCTV_GPIO_VAL_SHIFT 12 + +/* gpio_out_en value for read/write */ +#define BCTV_GPIO_OUT_RMASK 0x000f00 +#define BCTV_GPIO_OUT_WMASK 0x00ff00 + +#define BCTV_BITS 100 + +void +bctv_gpio_write( bktr_ptr_t bktr, int port, int val ) +{ + u_long data, outbits; + + port &= BCTV_GPIO_PORT_MASK; + switch (port) { + case 1: + case 3: + data = ((val << BCTV_GPIO_VAL_SHIFT) & BCTV_GPIO_VAL_MASK) | + ((port << BCTV_GPIO_ADDR_SHIFT) & BCTV_GPIO_ADDR_MASK) | + BCTV_GPIO_WE | BCTV_GPIO_OE; + outbits = BCTV_GPIO_OUT_WMASK; + break; + default: + return; + } + OUTL(bktr, BKTR_GPIO_OUT_EN, 0); + OUTL(bktr, BKTR_GPIO_DATA, data); + OUTL(bktr, BKTR_GPIO_OUT_EN, outbits); + DELAY(BCTV_BITS); + OUTL(bktr, BKTR_GPIO_DATA, data & ~BCTV_GPIO_WE); + DELAY(BCTV_BITS); + OUTL(bktr, BKTR_GPIO_DATA, data); + DELAY(BCTV_BITS); + OUTL(bktr, BKTR_GPIO_DATA, ~0); + OUTL(bktr, BKTR_GPIO_OUT_EN, 0); +} + +/* Not yet used +int +bctv_gpio_read( bktr_ptr_t bktr, int port ) +{ + u_long data, outbits, ret; + + port &= BCTV_GPIO_PORT_MASK; + switch (port) { + case 1: + case 3: + data = ((port << BCTV_GPIO_ADDR_SHIFT) & BCTV_GPIO_ADDR_MASK) | + BCTV_GPIO_WE | BCTV_GPIO_OE; + outbits = BCTV_GPIO_OUT_RMASK; + break; + default: + return( -1 ); + } + OUTL(bktr, BKTR_GPIO_OUT_EN, 0); + OUTL(bktr, BKTR_GPIO_DATA, data); + OUTL(bktr, BKTR_GPIO_OUT_EN, outbits); + DELAY(BCTV_BITS); + OUTL(bktr, BKTR_GPIO_DATA, data & ~BCTV_GPIO_OE); + DELAY(BCTV_BITS); + ret = INL(bktr, BKTR_GPIO_DATA); + DELAY(BCTV_BITS); + OUTL(bktr, BKTR_GPIO_DATA, data); + DELAY(BCTV_BITS); + OUTL(bktr, BKTR_GPIO_DATA, ~0); + OUTL(bktr, BKTR_GPIO_OUT_EN, 0); + return( (ret & BCTV_GPIO_VAL_MASK) >> BCTV_GPIO_VAL_SHIFT ); +} +*/ + +/* + * setup the MSP34xx Stereo Audio Chip + * This uses the Auto Configuration Option on MSP3410D and MSP3415D chips + * and DBX mode selection for MSP3430G chips. + * For MSP3400C support, the full programming sequence is required and is + * not yet supported. + */ + +/* Read the MSP version string */ +void msp_read_id( bktr_ptr_t bktr ){ + int rev1=0, rev2=0; + rev1 = msp_dpl_read(bktr, bktr->msp_addr, 0x12, 0x001e); + rev2 = msp_dpl_read(bktr, bktr->msp_addr, 0x12, 0x001f); + + sprintf(bktr->msp_version_string, "34%02d%c-%c%d", + (rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f); + +} + + +/* Configure the MSP chip to Auto-detect the audio format. + * For the MSP3430G, we use fast autodetect mode + * For the MSP3410/3415 there are two schemes for this + * a) Fast autodetection - the chip is put into autodetect mode, and the function + * returns immediatly. This works in most cases and is the Default Mode. + * b) Slow mode. The function sets the MSP3410/3415 chip, then waits for feedback from + * the chip and re-programs it if needed. + */ +void msp_autodetect( bktr_ptr_t bktr ) { + int auto_detect, loops; + int stereo; + + /* MSP3430G - countries with mono and DBX stereo */ + if (strncmp("3430G", bktr->msp_version_string, 5) == 0){ + + msp_dpl_write(bktr, bktr->msp_addr, 0x10, 0x0030,0x2003);/* Enable Auto format detection */ + msp_dpl_write(bktr, bktr->msp_addr, 0x10, 0x0020,0x0020);/* Standard Select Reg. = BTSC-Stereo*/ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x000E,0x2403);/* darned if I know */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0008,0x0320);/* Source select = (St or A) */ + /* & Ch. Matrix = St */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0000,0x7300);/* Set volume to 0db gain */ + } + + + /* MSP3415D SPECIAL CASE Use the Tuner's Mono audio ouput for the MSP */ + /* (for Hauppauge 44xxx card with Tuner Type 0x2a) */ + else if ( ( (strncmp("3415D", bktr->msp_version_string, 5) == 0) + &&(bktr->msp_use_mono_source == 1) + ) + || (bktr->slow_msp_audio == 2) ){ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0000, 0x7300); /* 0 db volume */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x000d, 0x1900); /* scart prescale */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0008, 0x0220); /* SCART | STEREO */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0013, 0x0100); /* DSP In = MONO IN */ + } + + + /* MSP3410/MSP3415 - countries with mono, stereo using 2 FM channels and NICAM */ + /* FAST sound scheme */ + else if (bktr->slow_msp_audio == 0) { + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0000,0x7300);/* Set volume to 0db gain */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0008,0x0000);/* Spkr Source = default(FM/AM) */ + msp_dpl_write(bktr, bktr->msp_addr, 0x10, 0x0020,0x0001);/* Enable Auto format detection */ + msp_dpl_write(bktr, bktr->msp_addr, 0x10, 0x0021,0x0001);/* Auto selection of NICAM/MONO mode */ + } + + + /* MSP3410/MSP3415 - European Countries where the fast MSP3410/3415 programming fails */ + /* SLOW sound scheme */ + else if ( bktr->slow_msp_audio == 1) { + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0000,0x7300);/* Set volume to 0db gain */ + msp_dpl_write(bktr, bktr->msp_addr, 0x10, 0x0020,0x0001);/* Enable Auto format detection */ + + /* wait for 0.5s max for terrestrial sound autodetection */ + loops = 10; + do { + DELAY(100000); + auto_detect = msp_dpl_read(bktr, bktr->msp_addr, 0x10, 0x007e); + loops++; + } while (auto_detect > 0xff && loops < 50); + if (bootverbose)printf ("%s: Result of autodetect after %dms: %d\n", + bktr_name(bktr), loops*10, auto_detect); + + /* Now set the audio baseband processing */ + switch (auto_detect) { + case 0: /* no TV sound standard detected */ + break; + case 2: /* M Dual FM */ + break; + case 3: /* B/G Dual FM; German stereo */ + /* Read the stereo detection value from DSP reg 0x0018 */ + DELAY(20000); + stereo = msp_dpl_read(bktr, bktr->msp_addr, 0x12, 0x0018); + if (bootverbose)printf ("%s: Stereo reg 0x18 a: %d\n", + bktr_name(bktr), stereo); + DELAY(20000); + stereo = msp_dpl_read(bktr, bktr->msp_addr, 0x12, 0x0018); + if (bootverbose)printf ("%s: Stereo reg 0x18 b: %d\n", + bktr_name(bktr), stereo); + DELAY(20000); + stereo = msp_dpl_read(bktr, bktr->msp_addr, 0x12, 0x0018); + if (bootverbose)printf ("%s: Stereo reg 0x18 c: %d\n", + bktr_name(bktr), stereo); + if (stereo > 0x0100 && stereo < 0x8000) { /* Seems to be stereo */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0008,0x0020);/* Loudspeaker set stereo*/ + /* + set spatial effect strength to 50% enlargement + set spatial effect mode b, stereo basewidth enlargment only + */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0005,0x3f28); + } else if (stereo > 0x8000) { /* bilingual mode */ + if (bootverbose) printf ("%s: Bilingual mode detected\n", + bktr_name(bktr)); + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0008,0x0000);/* Loudspeaker */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0005,0x0000);/* all spatial effects off */ + } else { /* must be mono */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0008,0x0030);/* Loudspeaker */ + /* + set spatial effect strength to 50% enlargement + set spatial effect mode a, stereo basewidth enlargment + and pseudo stereo effect with automatic high-pass filter + */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0005,0x3f08); + } +#if 0 + /* The reset value for Channel matrix mode is FM/AM and SOUNDA/LEFT */ + /* We would like STEREO instead val: 0x0020 */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0008,0x0020);/* Loudspeaker */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0009,0x0020);/* Headphone */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x000a,0x0020);/* SCART1 */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0041,0x0020);/* SCART2 */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x000b,0x0020);/* I2S */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x000c,0x0020);/* Quasi-Peak Detector Source */ + msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x000e,0x0001); +#endif + break; + case 8: /* B/G FM NICAM */ + msp_dpl_write(bktr, bktr->msp_addr, 0x10, 0x0021,0x0001);/* Auto selection of NICAM/MONO mode */ + break; + case 9: /* L_AM NICAM or D/K*/ + case 10: /* i-FM NICAM */ + break; + default: + if (bootverbose) printf ("%s: Unknown autodetection result value: %d\n", + bktr_name(bktr), auto_detect); + } + + } + + + /* uncomment the following line to enable the MSP34xx 1Khz Tone Generator */ + /* turn your speaker volume down low before trying this */ + /* msp_dpl_write(bktr, bktr->msp_addr, 0x12, 0x0014, 0x7f40); */ +} + +/* Read the DPL version string */ +void dpl_read_id( bktr_ptr_t bktr ){ + int rev1=0, rev2=0; + rev1 = msp_dpl_read(bktr, bktr->dpl_addr, 0x12, 0x001e); + rev2 = msp_dpl_read(bktr, bktr->dpl_addr, 0x12, 0x001f); + + sprintf(bktr->dpl_version_string, "34%02d%c-%c%d", + ((rev2>>8)&0xff)-1, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f); +} + +/* Configure the DPL chip to Auto-detect the audio format */ +void dpl_autodetect( bktr_ptr_t bktr ) { + + /* The following are empiric values tried from the DPL35xx data sheet */ + msp_dpl_write(bktr, bktr->dpl_addr, 0x12, 0x000c,0x0320); /* quasi peak detector source dolby + lr 0x03xx; quasi peak detector matrix + stereo 0xXX20 */ + msp_dpl_write(bktr, bktr->dpl_addr, 0x12, 0x0040,0x0060); /* Surround decoder mode; + ADAPTIVE/3D-PANORAMA, that means two + speakers and no center speaker, all + channels L/R/C/S mixed to L and R */ + msp_dpl_write(bktr, bktr->dpl_addr, 0x12, 0x0041,0x0620); /* surround source matrix;I2S2/STEREO*/ + msp_dpl_write(bktr, bktr->dpl_addr, 0x12, 0x0042,0x1F00); /* surround delay 31ms max */ + msp_dpl_write(bktr, bktr->dpl_addr, 0x12, 0x0043,0x0000); /* automatic surround input balance */ + msp_dpl_write(bktr, bktr->dpl_addr, 0x12, 0x0044,0x4000); /* surround spatial effect 50% + recommended*/ + msp_dpl_write(bktr, bktr->dpl_addr, 0x12, 0x0045,0x5400); /* surround panorama effect 66% + recommended with PANORAMA mode + in 0x0040 set to panorama */ +} + diff --git a/sys/dev/pci/bktr/bktr_audio.h b/sys/dev/pci/bktr/bktr_audio.h new file mode 100644 index 00000000000..6ecae8ca919 --- /dev/null +++ b/sys/dev/pci/bktr/bktr_audio.h @@ -0,0 +1,85 @@ +/* $OpenBSD: bktr_audio.h,v 1.1 2001/03/28 03:27:09 fgsch Exp $ */ +/* $FreeBSD: src/sys/dev/bktr/bktr_audio.h,v 1.2 1999/10/28 13:58:14 roger Exp $ */ + +/* + * This is part of the Driver for Video Capture Cards (Frame grabbers) + * and TV Tuner cards using the Brooktree Bt848, Bt848A, Bt849A, Bt878, Bt879 + * chipset. + * Copyright Roger Hardiman and Amancio Hasty. + * + * bktr_audio : This deals with controlling the audio on TV cards, + * controlling the Audio Multiplexer (audio source selector). + * controlling any MSP34xx stereo audio decoders. + * controlling any DPL35xx dolby surroud sound audio decoders. + * initialising TDA98xx audio devices. + * + */ + +/* + * 1. Redistributions of source code must retain the + * Copyright (c) 1997 Amancio Hasty, 1999 Roger Hardiman + * 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 and + * Roger Hardiman + * 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. + */ + +/* + * Select Audio source, and allow muting + */ +int set_audio( bktr_ptr_t bktr, int mode ); +void temp_mute( bktr_ptr_t bktr, int flag ); + + +/* + * Initialise any MSP or TDA devices + */ +void init_audio_devices( bktr_ptr_t bktr ); + + +/* + * MSP34xx Audio Chip functions. + */ +void msp_autodetect( bktr_ptr_t bktr ); +void msp_read_id( bktr_ptr_t bktr ); + + +/* + * DPL35xx Audio Chip functions. + */ +void dpl_autodetect( bktr_ptr_t bktr ); +void dpl_read_id( bktr_ptr_t bktr ); + + +/* + * TDA98xx Audio Chip functions. + */ +void init_BTSC( bktr_ptr_t bktr ); +int set_BTSC( bktr_ptr_t bktr, int control ); + + + diff --git a/sys/dev/pci/bktr/bktr_card.c b/sys/dev/pci/bktr/bktr_card.c new file mode 100644 index 00000000000..843aaf49330 --- /dev/null +++ b/sys/dev/pci/bktr/bktr_card.c @@ -0,0 +1,1268 @@ +/* $OpenBSD: bktr_card.c,v 1.1 2001/03/28 03:27:09 fgsch Exp $ */ +/* $FreeBSD: src/sys/dev/bktr/bktr_card.c,v 1.16 2000/10/31 13:09:56 roger Exp $ */ + +/* + * This is part of the Driver for Video Capture Cards (Frame grabbers) + * and TV Tuner cards using the Brooktree Bt848, Bt848A, Bt849A, Bt878, Bt879 + * chipset. + * Copyright Roger Hardiman and Amancio Hasty. + * + * bktr_card : This deals with identifying TV cards. + * trying to find the card make and model of card. + * trying to find the type of tuner fitted. + * reading the configuration EEPROM. + * locating i2c devices. + * + */ + +/* + * 1. Redistributions of source code must retain the + * Copyright (c) 1997 Amancio Hasty, 1999 Roger Hardiman + * 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 and + * Roger Hardiman + * 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. + */ + +#ifndef __OpenBSD__ +#include "opt_bktr.h" /* Include any kernel config options */ +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/vnode.h> + +#ifdef __FreeBSD__ + +#if (__FreeBSD_version < 500000) +#include <machine/clock.h> /* for DELAY */ +#endif + +#include <pci/pcivar.h> + +#if (__FreeBSD_version >=300000) +#include <machine/bus_memio.h> /* for bus space */ +#include <machine/bus.h> +#include <sys/bus.h> +#endif +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <dev/ic/bt8xx.h> /* NetBSD location for .h files */ +#include <dev/pci/bktr/bktr_reg.h> +#include <dev/pci/bktr/bktr_core.h> +#include <dev/pci/bktr/bktr_tuner.h> +#include <dev/pci/bktr/bktr_card.h> +#include <dev/pci/bktr/bktr_audio.h> +#else +#include <machine/ioctl_meteor.h> /* Traditional location for .h files */ +#include <machine/ioctl_bt848.h> /* extensions to ioctl_meteor.h */ +#include <dev/bktr/bktr_reg.h> +#include <dev/bktr/bktr_core.h> +#include <dev/bktr/bktr_tuner.h> +#include <dev/bktr/bktr_card.h> +#include <dev/bktr/bktr_audio.h> +#endif + +/* Include the PCI Vendor definitions */ +#ifdef __NetBSD__ +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcireg.h> +#endif + +/* Various defines */ +#define HAUP_REMOTE_INT_WADDR 0x30 +#define HAUP_REMOTE_INT_RADDR 0x31 + +#define HAUP_REMOTE_EXT_WADDR 0x34 +#define HAUP_REMOTE_EXT_RADDR 0x35 + +/* address of BTSC/SAP decoder chip */ +#define TDA9850_WADDR 0xb6 +#define TDA9850_RADDR 0xb7 + +/* address of MSP3400C chip */ +#define MSP3400C_WADDR 0x80 +#define MSP3400C_RADDR 0x81 + +/* address of DPL3518A chip */ +#define DPL3518A_WADDR 0x84 +#define DPL3518A_RADDR 0x85 + +/* EEProm (128 * 8) on an STB card */ +#define X24C01_WADDR 0xae +#define X24C01_RADDR 0xaf + + +/* EEProm (256 * 8) on a Hauppauge card */ +/* and on most BT878s cards to store the sub-system vendor id */ +#define PFC8582_WADDR 0xa0 +#define PFC8582_RADDR 0xa1 + +#if BKTR_SYSTEM_DEFAULT == BROOKTREE_PAL +#define DEFAULT_TUNER PHILIPS_PALI +#else +#define DEFAULT_TUNER PHILIPS_NTSC +#endif + + + + +/* + * the data for each type of card + * + * Note: + * these entried MUST be kept in the order defined by the CARD_XXX defines! + */ +static const struct CARDTYPE cards[] = { + + { CARD_UNKNOWN, /* the card id */ + "Unknown", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, /* dbx unknown */ + 0, + 0, + 0, /* EEProm unknown */ + 0, /* EEProm unknown */ + { 0, 0, 0, 0, 0 }, + 0 }, /* GPIO mask */ + + { CARD_MIRO, /* the card id */ + "Pinnacle/Miro TV", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, /* dbx unknown */ + 0, + 0, + 0, /* EEProm unknown */ + 0, /* size unknown */ + { 0x02, 0x01, 0x00, 0x0a, 1 }, /* audio MUX values */ + 0x0f }, /* GPIO mask */ + + { CARD_HAUPPAUGE, /* the card id */ + "Hauppauge WinCast/TV", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, /* dbx is optional */ + 0, + 0, + PFC8582_WADDR, /* EEProm type */ + (u_char)(256 / EEPROMBLOCKSIZE), /* 256 bytes */ + { 0x00, 0x02, 0x01, 0x04, 1 }, /* audio MUX values */ + 0x0f }, /* GPIO mask */ + + { CARD_STB, /* the card id */ + "STB TV/PCI", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, /* dbx is optional */ + 0, + 0, + X24C01_WADDR, /* EEProm type */ + (u_char)(128 / EEPROMBLOCKSIZE), /* 128 bytes */ + { 0x00, 0x01, 0x02, 0x02, 1 }, /* audio MUX values */ + 0x0f }, /* GPIO mask */ + + { CARD_INTEL, /* the card id */ + "Intel Smart Video III/VideoLogic Captivator PCI", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, + 0, + 0, + 0, + 0, + { 0, 0, 0, 0, 0 }, /* audio MUX values */ + 0x00 }, /* GPIO mask */ + + { CARD_IMS_TURBO, /* the card id */ + "IMS TV Turbo", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, /* dbx is optional */ + 0, + 0, + PFC8582_WADDR, /* EEProm type */ + (u_char)(256 / EEPROMBLOCKSIZE), /* 256 bytes */ + { 0x01, 0x02, 0x01, 0x00, 1 }, /* audio MUX values */ + 0x0f }, /* GPIO mask */ + + { CARD_AVER_MEDIA, /* the card id */ + "AVer Media TV/FM", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, /* dbx is optional */ + 0, + 0, + 0, /* EEProm type */ + 0, /* EEProm size */ + { 0x0c, 0x08, 0x04, 0x00, 1 }, /* audio MUX values */ + 0x1f }, /* GPIO mask */ + + { CARD_OSPREY, /* the card id */ + "MMAC Osprey", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, /* dbx is optional */ + 0, + 0, + PFC8582_WADDR, /* EEProm type */ + (u_char)(256 / EEPROMBLOCKSIZE), /* 256 bytes */ + { 0x00, 0x00, 0x00, 0x00, 0 }, /* audio MUX values */ + 0 }, /* GPIO mask */ + + { CARD_NEC_PK, /* the card id */ + "NEC PK-UG-X017", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, /* dbx is optional */ + 0, + 0, + 0, /* EEProm type */ + 0, /* EEProm size */ + { 0x01, 0x02, 0x01, 0x00, 1 }, /* audio MUX values */ + 0x0f }, /* GPIO mask */ + + { CARD_IO_GV, /* the card id */ + "I/O DATA GV-BCTV2/PCI", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, /* dbx is optional */ + 0, + 0, + 0, /* EEProm type */ + 0, /* EEProm size */ + { 0x00, 0x00, 0x00, 0x00, 1 }, /* Has special MUX handler */ + 0x0f }, /* GPIO mask */ + + { CARD_FLYVIDEO, /* the card id */ + "FlyVideo", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, /* dbx is optional */ + 0, /* msp34xx is optional */ + 0, /* dpl3518a is optional */ + 0xac, /* EEProm type */ + (u_char)(256 / EEPROMBLOCKSIZE), /* 256 bytes */ + { 0x000, 0x800, 0x400, 0x8dff00, 1 },/* audio MUX values */ + 0x8dff00 }, /* GPIO mask */ + + { CARD_ZOLTRIX, /* the card id */ + "Zoltrix", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, /* dbx is optional */ + 0, /* msp34xx is optional */ + 0, /* dpl3518a is optional */ + 0, /* EEProm type */ + 0, /* EEProm size */ + { 0x04, 0x01, 0x00, 0x0a, 1 }, /* audio MUX values */ + 0x0f }, /* GPIO mask */ + + { CARD_KISS, /* the card id */ + "KISS TV/FM PCI", /* the 'name' */ + NULL, /* the tuner */ + 0, /* the tuner i2c address */ + 0, /* dbx is optional */ + 0, /* msp34xx is optional */ + 0, /* dpl3518a is optional */ + 0, /* EEProm type */ + 0, /* EEProm size */ + { 0x0c, 0x00, 0x0b, 0x0b, 1 }, /* audio MUX values */ + 0x0f }, /* GPIO mask */ + + { CARD_VIDEO_HIGHWAY_XTREME, /* the card id */ + "Video Highway Xtreme", /* the 'name' */ + NULL, /* the tuner */ + 0, + 0, + 0, + 0, + 0, /* EEProm type */ + 0, /* EEProm size */ + { 0x00, 0x02, 0x01, 0x04, 1 }, /* audio MUX values */ + 0x0f }, /* GPIO mask */ + + { CARD_ASKEY_DYNALINK_MAGIC_TVIEW, /* the card id */ + "Askey/Dynalink Magic TView", /* the 'name' */ + NULL, /* the tuner */ + 0, + 0, + 0, + 0, + 0, /* EEProm type */ + 0, /* EEProm size */ + { 0x400, 0xE00, 0x400, 0xC00, 1 }, /* audio MUX values */ + 0xE00 }, /* GPIO mask */ + + { CARD_LEADTEK, /* the card id */ + "Leadtek Winfast TV 2000", /* the 'name' */ + NULL, /* the tuner */ + 0, + 0, + 0, + 0, + 0, /* EEProm type */ + 0, /* EEProm size */ + /* Tuner, Extern, Intern, Mute, Enabled */ + { 0x621000, 0x621000, 0x621000, 0xE21000, 1 }, /* audio MUX values */ + 0xfff000 }, /* GPIO mask */ + + { CARD_TERRATVPLUS, /* the card id */ + "TerraTVplus", /* the 'name' */ + NULL, /* the tuner */ + 0, + 0, + 0, + 0, + 0, /* EEProm type */ + 0, /* EEProm size */ + { 0x20000, 0x00000, 0x30000, 0x40000, 1 }, /* audio MUX values*/ + 0x70000 }, /* GPIO mask */ + +}; + +struct bt848_card_sig bt848_card_signature[1]= { + /* IMS TURBO TV : card 5 */ + { 5,9, {00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 02, 00, 00, 00}} + + +}; + + +/* + * Write to the configuration EEPROM on the card. + * This is dangerous and will mess up your card. Therefore it is not + * implemented. + */ +int +writeEEProm( bktr_ptr_t bktr, int offset, int count, u_char *data ) +{ + return( -1 ); +} + +/* + * Read the contents of the configuration EEPROM on the card. + * (This is not fitted to all makes of card. All Hauppauge cards have them + * and so do newer Bt878 based cards. + */ +int +readEEProm( bktr_ptr_t bktr, int offset, int count, u_char *data ) +{ + int x; + int addr; + int max; + int byte; + + /* get the address of the EEProm */ + addr = (int)(bktr->card.eepromAddr & 0xff); + if ( addr == 0 ) + return( -1 ); + + max = (int)(bktr->card.eepromSize * EEPROMBLOCKSIZE); + if ( (offset + count) > max ) + return( -1 ); + + /* set the start address */ + if ( i2cWrite( bktr, addr, offset, -1 ) == -1 ) + return( -1 ); + + /* the read cycle */ + for ( x = 0; x < count; ++x ) { + if ( (byte = i2cRead( bktr, (addr | 1) )) == -1 ) + return( -1 ); + data[ x ] = byte; + } + + return( 0 ); +} + + +#define ABSENT (-1) + +/* + * get a signature of the card + * read all 128 possible i2c read addresses from 0x01 thru 0xff + * build a bit array with a 1 bit for each i2c device that responds + * + * XXX FIXME: use offset & count args + */ +int +signCard( bktr_ptr_t bktr, int offset, int count, u_char* sig ) +{ + int x; + + for ( x = 0; x < 16; ++x ) + sig[ x ] = 0; + + for ( x = 0; x < count; ++x ) { + if ( i2cRead( bktr, (2 * x) + 1 ) != ABSENT ) { + sig[ x / 8 ] |= (1 << (x % 8) ); + } + } + + return( 0 ); +} + + +/* + * check_for_i2c_devices. + * Some BT848 cards have no tuner and no additional i2c devices + * eg stereo decoder. These are used for video conferencing or capture from + * a video camera. (eg VideoLogic Captivator PCI, Intel SmartCapture card). + * + * Determine if there are any i2c devices present. There are none present if + * a) reading from all 128 devices returns ABSENT (-1) for each one + * (eg VideoLogic Captivator PCI with BT848) + * b) reading from all 128 devices returns 0 for each one + * (eg VideoLogic Captivator PCI rev. 2F with BT848A) + */ +static int check_for_i2c_devices( bktr_ptr_t bktr ){ + int x, temp_read; + int i2c_all_0 = 1; + int i2c_all_absent = 1; + for ( x = 0; x < 128; ++x ) { + temp_read = i2cRead( bktr, (2 * x) + 1 ); + if (temp_read != 0) i2c_all_0 = 0; + if (temp_read != ABSENT) i2c_all_absent = 0; + } + + if ((i2c_all_0) || (i2c_all_absent)) return 0; + else return 1; +} + + +/* + * Temic/Philips datasheets say tuners can be at i2c addresses 0xc0, 0xc2, + * 0xc4 or 0xc6, settable by links on the tuner. + * Determine the actual address used on the TV card by probing read addresses. + */ +static int locate_tuner_address( bktr_ptr_t bktr) { + if (i2cRead( bktr, 0xc1) != ABSENT) return 0xc0; + if (i2cRead( bktr, 0xc3) != ABSENT) return 0xc2; + if (i2cRead( bktr, 0xc5) != ABSENT) return 0xc4; + if (i2cRead( bktr, 0xc7) != ABSENT) return 0xc6; + return -1; /* no tuner found */ +} + + +/* + * Search for a configuration EEPROM on the i2c bus by looking at i2c addresses + * where EEPROMs are usually found. + * On some cards, the EEPROM appears in several locations, but all in the + * range 0xa0 to 0xae. + */ +static int locate_eeprom_address( bktr_ptr_t bktr) { + if (i2cRead( bktr, 0xa0) != ABSENT) return 0xa0; + if (i2cRead( bktr, 0xac) != ABSENT) return 0xac; + if (i2cRead( bktr, 0xae) != ABSENT) return 0xae; + return -1; /* no eeprom found */ +} + + +/* + * determine the card brand/model + * BKTR_OVERRIDE_CARD, BKTR_OVERRIDE_TUNER, BKTR_OVERRIDE_DBX and + * BKTR_OVERRIDE_MSP can be used to select a specific device, + * regardless of the autodetection and i2c device checks. + * + * The scheme used for probing cards faces these problems: + * It is impossible to work out which type of tuner is actually fitted, + * (the driver cannot tell if the Tuner is PAL or NTSC, Temic or Philips) + * It is impossible to determine what audio-mux hardware is connected. + * It is impossible to determine if there is extra hardware connected to the + * GPIO pins (eg radio chips or MSP34xx reset logic) + * + * However some makes of card (eg Hauppauge) come with a configuration eeprom + * which tells us the make of the card. Most eeproms also tell us the + * tuner type and other features of the the cards. + * + * The current probe code works as follows + * A) If the card uses a Bt878/879: + * 1) Read the sub-system vendor id from the configuration EEPROM. + * Select the required tuner, audio mux arrangement and any other + * onboard features. If this fails, move to step B. + * B) If it card uses a Bt848, 848A, 849A or an unknown Bt878/879: + * 1) Look for I2C devices. If there are none fitted, it is an Intel or + * VideoLogic cards. + * 2) Look for a configuration EEPROM. + * 2a) If there is one at I2C address 0xa0 it may be + * a Hauppauge or an Osprey. Check the EEPROM contents to determine which + * one it is. For Hauppauge, select the tuner type and audio hardware. + * 2b) If there is an EEPROM at I2C address 0xa8 it will be an STB card. + * We still have to guess on the tuner type. + * + * C) If we do not know the card type from (A) or (B), guess at the tuner + * type based on the I2C address of the tuner. + * + * D) After determining the Tuner Type, we probe the i2c bus for other + * devices at known locations, eg IR-Remote Control, MSP34xx and TDA + * stereo chips. + */ + + +/* + * These are the sub-system vendor ID codes stored in the + * configuration EEPROM used on Bt878/879 cards. They should match the + * number assigned to the company by the PCI Special Interest Group + */ +#ifndef __NetBSD__ +#define PCI_VENDOR_HAUPPAUGE 0x0070 +#define PCI_VENDOR_AVERMEDIA 0x1461 +#define PCI_VENDOR_STB 0x10B4 +#define PCI_VENDOR_ASKEY 0x144F +#endif +/* Following not confirmed with http://members.hyperlink.net.au/~chart, + so not added to NetBSD's pcidevs */ +#define PCI_VENDOR_LEADTEK_ALT 0x6606 +#define PCI_VENDOR_FLYVIDEO 0x1851 +#define PCI_VENDOR_FLYVIDEO_2 0x1852 +#define PCI_VENDOR_PINNACLE_ALT 0xBD11 + + +void +probeCard( bktr_ptr_t bktr, int verbose, int unit ) +{ + int card, i,j, card_found; + int status; + u_char probe_signature[128], *probe_temp; + int any_i2c_devices; + u_char eeprom[256]; + int tuner_i2c_address = -1; + int eeprom_i2c_address = -1; + + /* Select all GPIO bits as inputs */ + OUTL(bktr, BKTR_GPIO_OUT_EN, 0); + if (bootverbose) + printf("%s: GPIO is 0x%08x\n", bktr_name(bktr), + INL(bktr, BKTR_GPIO_DATA)); + +#ifdef HAUPPAUGE_MSP_RESET + /* Reset the MSP34xx audio chip. This resolves bootup card + * detection problems with old Bt848 based Hauppauge cards with + * MSP34xx stereo audio chips. This must be user enabled because + * at this point the probe function does not know the card type. */ + OUTL(bktr, BKTR_GPIO_OUT_EN, INL(bktr, BKTR_GPIO_OUT_EN) | (1<<5)); + OUTL(bktr, BKTR_GPIO_DATA, INL(bktr, BKTR_GPIO_DATA) | (1<<5)); /* write '1' */ + DELAY(2500); /* wait 2.5ms */ + OUTL(bktr, BKTR_GPIO_DATA, INL(bktr, BKTR_GPIO_DATA) & ~(1<<5)); /* write '0' */ + DELAY(2500); /* wait 2.5ms */ + OUTL(bktr, BKTR_GPIO_DATA, INL(bktr, BKTR_GPIO_DATA) | (1<<5)); /* write '1' */ + DELAY(2500); /* wait 2.5ms */ +#endif + + /* Check for the presence of i2c devices */ + any_i2c_devices = check_for_i2c_devices( bktr ); + + + /* Check for a user specified override on the card selection */ +#if defined( BKTR_OVERRIDE_CARD ) + bktr->card = cards[ (card = BKTR_OVERRIDE_CARD) ]; + goto checkEEPROM; +#endif + if (bktr->bt848_card != -1 ) { + bktr->card = cards[ (card = bktr->bt848_card) ]; + goto checkEEPROM; + } + + + /* No override, so try and determine the make of the card */ + + /* On BT878/879 cards, read the sub-system vendor id */ + /* This identifies the manufacturer of the card and the model */ + /* In theory this can be read from PCI registers but this does not */ + /* appear to work on the FlyVideo 98. Hauppauge also warned that */ + /* the PCI registers are sometimes not loaded correctly. */ + /* Therefore, I will read the sub-system vendor ID from the EEPROM */ + /* (just like the Bt878 does during power up initialisation) */ + + if ((bktr->id==BROOKTREE_878) || (bktr->id==BROOKTREE_879)) { + /* Try and locate the EEPROM */ + eeprom_i2c_address = locate_eeprom_address( bktr ); + if (eeprom_i2c_address != -1) { + + unsigned int subsystem_vendor_id; /* vendors PCI-SIG ID */ + unsigned int subsystem_id; /* board model number */ + unsigned int byte_252, byte_253, byte_254, byte_255; + + bktr->card = cards[ (card = CARD_UNKNOWN) ]; + bktr->card.eepromAddr = eeprom_i2c_address; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + + readEEProm(bktr, 0, 256, (u_char *) &eeprom ); + byte_252 = (unsigned int)eeprom[252]; + byte_253 = (unsigned int)eeprom[253]; + byte_254 = (unsigned int)eeprom[254]; + byte_255 = (unsigned int)eeprom[255]; + + subsystem_id = (byte_252 << 8) | byte_253; + subsystem_vendor_id = (byte_254 << 8) | byte_255; + + if ( bootverbose ) + printf("%s: subsystem 0x%04x 0x%04x\n", bktr_name(bktr), + subsystem_vendor_id, subsystem_id); + + if (subsystem_vendor_id == PCI_VENDOR_AVERMEDIA) { + bktr->card = cards[ (card = CARD_AVER_MEDIA) ]; + bktr->card.eepromAddr = eeprom_i2c_address; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + goto checkTuner; + } + + if (subsystem_vendor_id == PCI_VENDOR_HAUPPAUGE) { + bktr->card = cards[ (card = CARD_HAUPPAUGE) ]; + bktr->card.eepromAddr = eeprom_i2c_address; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + goto checkTuner; + } + + if ((subsystem_vendor_id == PCI_VENDOR_FLYVIDEO) + || (subsystem_vendor_id == PCI_VENDOR_FLYVIDEO_2) ) { + bktr->card = cards[ (card = CARD_FLYVIDEO) ]; + bktr->card.eepromAddr = eeprom_i2c_address; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + goto checkTuner; + } + + if (subsystem_vendor_id == PCI_VENDOR_STB) { + bktr->card = cards[ (card = CARD_STB) ]; + bktr->card.eepromAddr = eeprom_i2c_address; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + goto checkTuner; + } + + if (subsystem_vendor_id == PCI_VENDOR_ASKEY) { + bktr->card = cards[ (card = CARD_ASKEY_DYNALINK_MAGIC_TVIEW) ]; + bktr->card.eepromAddr = eeprom_i2c_address; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + goto checkTuner; + } + + if (subsystem_vendor_id == PCI_VENDOR_LEADTEK_ALT) { + bktr->card = cards[ (card = CARD_LEADTEK) ]; + bktr->card.eepromAddr = eeprom_i2c_address; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + goto checkTuner; + } + + if (subsystem_vendor_id == PCI_VENDOR_PINNACLE_ALT) { + bktr->card = cards[ (card = CARD_MIRO) ]; + bktr->card.eepromAddr = eeprom_i2c_address; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + goto checkTuner; + } + + /* Vendor is unknown. We will use the standard probe code */ + /* which may not give best results */ + printf("%s: Warning - card vendor 0x%04x (model 0x%04x) unknown.\n", + bktr_name(bktr), subsystem_vendor_id, subsystem_id); + } + else + { + printf("%s: Card has no configuration EEPROM. Cannot determine card make.\n", + bktr_name(bktr)); + } + } /* end of bt878/bt879 card detection code */ + + /* If we get to this point, we must have a Bt848/848A/849A card */ + /* or a Bt878/879 with an unknown subsystem vendor id */ + /* Try and determine the make of card by clever i2c probing */ + + /* Check for i2c devices. If none, move on */ + if (!any_i2c_devices) { + bktr->card = cards[ (card = CARD_INTEL) ]; + bktr->card.eepromAddr = 0; + bktr->card.eepromSize = 0; + goto checkTuner; + } + + /* Look for Hauppauge, STB and Osprey cards by the presence */ + /* of an EEPROM */ + /* Note: Bt878 based cards also use EEPROMs so we can only do this */ + /* test on BT848/848A and 849A based cards. */ + if ((bktr->id==BROOKTREE_848) || + (bktr->id==BROOKTREE_848A) || + (bktr->id==BROOKTREE_849A)) { + + /* At i2c address 0xa0, look for Hauppauge and Osprey cards */ + if ( (status = i2cRead( bktr, PFC8582_RADDR )) != ABSENT ) { + + /* Read the eeprom contents */ + bktr->card = cards[ (card = CARD_UNKNOWN) ]; + bktr->card.eepromAddr = PFC8582_WADDR; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + readEEProm(bktr, 0, 128, (u_char *) &eeprom ); + + /* For Hauppauge, check the EEPROM begins with 0x84 */ + if (eeprom[0] == 0x84) { + bktr->card = cards[ (card = CARD_HAUPPAUGE) ]; + bktr->card.eepromAddr = PFC8582_WADDR; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + goto checkTuner; + } + + /* For Osprey, check the EEPROM begins with "MMAC" */ + if ( (eeprom[0] == 'M') &&(eeprom[1] == 'M') + &&(eeprom[2] == 'A') &&(eeprom[3] == 'C')) { + bktr->card = cards[ (card = CARD_OSPREY) ]; + bktr->card.eepromAddr = PFC8582_WADDR; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + goto checkTuner; + } + printf("%s: Warning: Unknown card type. EEPROM data not recognised\n", + bktr_name(bktr)); + printf("%s: %x %x %x %x\n", bktr_name(bktr), + eeprom[0],eeprom[1],eeprom[2],eeprom[3]); + } + + /* look for an STB card */ + if ( (status = i2cRead( bktr, X24C01_RADDR )) != ABSENT ) { + bktr->card = cards[ (card = CARD_STB) ]; + bktr->card.eepromAddr = X24C01_WADDR; + bktr->card.eepromSize = (u_char)(128 / EEPROMBLOCKSIZE); + goto checkTuner; + } + + } + + signCard( bktr, 1, 128, (u_char *) &probe_signature ); + + if (bootverbose) { + printf("%s: card signature: ", bktr_name(bktr)); + for (j = 0; j < Bt848_MAX_SIGN; j++) { + printf(" %02x ", probe_signature[j]); + } + printf("\n\n"); + } + for (i = 0; + i < (sizeof bt848_card_signature)/ sizeof (struct bt848_card_sig); + i++ ) { + + card_found = 1; + probe_temp = (u_char *) &bt848_card_signature[i].signature; + + for (j = 0; j < Bt848_MAX_SIGN; j++) { + if ((probe_temp[j] & 0xf) != (probe_signature[j] & 0xf)) { + card_found = 0; + break; + } + + } + if (card_found) { + bktr->card = cards[ card = bt848_card_signature[i].card]; + eeprom_i2c_address = locate_eeprom_address( bktr ); + if (eeprom_i2c_address != -1) { + bktr->card.eepromAddr = eeprom_i2c_address; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + } else { + bktr->card.eepromAddr = 0; + bktr->card.eepromSize = 0; + } + tuner_i2c_address = locate_tuner_address( bktr ); + select_tuner( bktr, bt848_card_signature[i].tuner ); + goto checkDBX; + } + } + + /* We do not know the card type. Default to Miro */ + bktr->card = cards[ (card = CARD_MIRO) ]; + + +checkEEPROM: + /* look for a configuration eeprom */ + eeprom_i2c_address = locate_eeprom_address( bktr ); + if (eeprom_i2c_address != -1) { + bktr->card.eepromAddr = eeprom_i2c_address; + bktr->card.eepromSize = (u_char)(256 / EEPROMBLOCKSIZE); + } else { + bktr->card.eepromAddr = 0; + bktr->card.eepromSize = 0; + } + + +checkTuner: + + /* look for a tuner */ + tuner_i2c_address = locate_tuner_address( bktr ); + if ( tuner_i2c_address == -1 ) { + select_tuner( bktr, NO_TUNER ); + goto checkDBX; + } + +#if defined( BKTR_OVERRIDE_TUNER ) + select_tuner( bktr, BKTR_OVERRIDE_TUNER ); + goto checkDBX; +#endif + if (bktr->bt848_tuner != -1 ) { + select_tuner( bktr, bktr->bt848_tuner & 0xff ); + goto checkDBX; + } + + /* Check for i2c devices */ + if (!any_i2c_devices) { + select_tuner( bktr, NO_TUNER ); + goto checkDBX; + } + + /* differentiate type of tuner */ + + switch (card) { + case CARD_MIRO: + switch (((INL(bktr, BKTR_GPIO_DATA) >> 10)-1)&7) { + case 0: select_tuner( bktr, TEMIC_PAL ); break; + case 1: select_tuner( bktr, PHILIPS_PAL ); break; + case 2: select_tuner( bktr, PHILIPS_NTSC ); break; + case 3: select_tuner( bktr, PHILIPS_SECAM ); break; + case 4: select_tuner( bktr, NO_TUNER ); break; + case 5: select_tuner( bktr, PHILIPS_PALI ); break; + case 6: select_tuner( bktr, TEMIC_NTSC ); break; + case 7: select_tuner( bktr, TEMIC_PALI ); break; + } + goto checkDBX; + break; + + case CARD_HAUPPAUGE: + /* Hauppauge kindly supplied the following Tuner Table */ + /* FIXME: I think the tuners the driver selects for types */ + /* 0x08 and 0x15 may be incorrect but no one has complained. */ + /* Old Temic tuners had their own API, but newer Temic tuners */ + /* have the same API as Philips tuners */ + /* + ID Tuner Model Format We select Format + 0x00 NONE + 0x01 EXTERNAL + 0x02 OTHER + 0x03 Philips FI1216 BG + 0x04 Philips FI1216MF BGLL' PHILIPS_SECAM + 0x05 Philips FI1236 MN PHILIPS_NTSC + 0x06 Philips FI1246 I PHILIPS_PALI + 0x07 Philips FI1256 DK + 0x08 Philips FI1216 MK2 BG PHILIPS_PALI + 0x09 Philips FI1216MF MK2 BGLL' PHILIPS_SECAM + 0x0a Philips FI1236 MK2 MN PHILIPS_NTSC + 0x0b Philips FI1246 MK2 I PHILIPS_PALI + 0x0c Philips FI1256 MK2 DK + 0x0d Temic 4032FY5 NTSC TEMIC_NTSC + 0x0e Temic 4002FH5 BG TEMIC_PAL + 0x0f Temic 4062FY5 I TEMIC_PALI + 0x10 Philips FR1216 MK2 BG + 0x11 Philips FR1216MF MK2 BGLL' PHILIPS_FR1236_SECAM + 0x12 Philips FR1236 MK2 MN PHILIPS_FR1236_NTSC + 0x13 Philips FR1246 MK2 I + 0x14 Philips FR1256 MK2 DK + 0x15 Philips FM1216 BG PHILIPS_FR1216_PAL + 0x16 Philips FM1216MF BGLL' PHILIPS_FR1236_SECAM + 0x17 Philips FM1236 MN PHILIPS_FR1236_NTSC + 0x18 Philips FM1246 I + 0x19 Philips FM1256 DK + 0x1a Temic 4036FY5 MN (FI1236 MK2 clone) PHILIPS_NTSC + 0x1b Samsung TCPN9082D MN + 0x1c Samsung TCPM9092P Pal BG/I/DK + 0x1d Temic 4006FH5 BG PHILIPS_PALI + 0x1e Samsung TCPN9085D MN/Radio + 0x1f Samsung TCPB9085P Pal BG/I/DK / Radio + 0x20 Samsung TCPL9091P Pal BG & Secam L/L' + 0x21 Temic 4039FY5 NTSC Radio + 0x22 Philips FQ1216ME Pal BGIDK & Secam L/L' + 0x23 Temic 4066FY5 Pal I (FI1246 MK2 clone) PHILIPS_PALI + 0x24 Philips TD1536 MN/ATSCDigital + 0x25 Philips TD1536D MN/ATSCDigital DUAL INPUT + 0x26 Philips FMR1236 M/N FM(no demod) + 0x27 Philips FI1256MP B/G, D/K + 0x28 Samsung TCPQ9091P BG/I/DK, L/L' + 0x29 Temic 4006FN5 BG/I/DK + 0x2a Temic 4009FR5 BG FM PHILIPS_FR1216_PAL + 0x2b Temic 4046FM5 B/G, I, D/K, L/L' + 0x2c Temic 4009FN5 B/G, I, D/K, FM (no demod) + 0x2d Philips TD1536D_FH_44 MN/ATSCDigital DUAL INPUT + */ + + + /* Determine the model number from the eeprom */ + if (bktr->card.eepromAddr != 0) { + /* eeprom data block structure */ + unsigned char *block_1, *block_2, *block_3, *block_4; + int block_1_data_size, block_2_data_size, block_3_data_size; + int block_1_total_size, block_2_total_size, block_3_total_size; + int block_4_header_size; + + unsigned int model,revision; + unsigned char tuner_code; + unsigned char no_audio_mux; + + readEEProm(bktr, 0, 128, (u_char *) &eeprom ); + + /* LOCATE THE EEPROM DATA BLOCKS */ + block_1 = &eeprom[0]; + block_1_data_size = (block_1[2] << 8 | block_1[1]); + block_1_total_size = block_1_data_size + 3; /* Header bytes */ + + block_2 = &eeprom[block_1_total_size]; + block_2_data_size = (block_2[2] << 8 | block_2[1]); + block_2_total_size = block_2_data_size + 3; /* Header bytes */ + + block_3 = &eeprom[block_1_total_size + block_2_total_size]; + block_3_data_size = (block_3[0] &0x07); + block_3_total_size = block_3_data_size + 1; /* Header size */ + + block_4 = &eeprom[block_1_total_size +block_2_total_size +block_3_total_size]; + block_4_header_size = 1; + + model = (block_1[12] << 8 | block_1[11]); + revision = (block_1[15] << 16 | block_1[14] << 8 | block_1[13]); + + tuner_code = block_1[9]; + + no_audio_mux = ((block_3[3] >> 7) &0x01); + + if (no_audio_mux) bktr->audio_mux_present = 0; + + if (verbose) + printf("%s: Hauppauge Model %d %c%c%c%c\n", + bktr_name(bktr), + model, + ((revision >> 18) & 0x3f) + 32, + ((revision >> 12) & 0x3f) + 32, + ((revision >> 6) & 0x3f) + 32, + ((revision >> 0) & 0x3f) + 32 ); + + /* Determine the tuner type from the eeprom */ + + switch (tuner_code) { + + case 0x5: + case 0x0a: + case 0x1a: + select_tuner( bktr, PHILIPS_NTSC ); + goto checkDBX; + + case 0x4: + case 0x9: + select_tuner( bktr, PHILIPS_SECAM ); + goto checkDBX; + + case 0x11: + case 0x16: + select_tuner( bktr, PHILIPS_FR1236_SECAM ); + goto checkDBX; + + case 0x12: + case 0x17: + select_tuner( bktr, PHILIPS_FR1236_NTSC ); + goto checkDBX; + + case 0x6: + case 0x8: + case 0xb: + case 0x1d: + case 0x23: + select_tuner( bktr, PHILIPS_PALI ); + goto checkDBX; + + case 0xd: + select_tuner( bktr, TEMIC_NTSC ); + goto checkDBX; + + case 0xe: + select_tuner( bktr, TEMIC_PAL ); + goto checkDBX; + + case 0xf: + select_tuner( bktr, TEMIC_PALI ); + goto checkDBX; + + case 0x15: + select_tuner( bktr, PHILIPS_FR1216_PAL ); + goto checkDBX; + + case 0x2a: + bktr->msp_use_mono_source = 1; + select_tuner( bktr, PHILIPS_FR1216_PAL ); + goto checkDBX; + + default : + printf("%s: Warning - Unknown Hauppauge Tuner 0x%x\n", + bktr_name(bktr), tuner_code); + } + } + break; + + + case CARD_AVER_MEDIA: + /* AVerMedia kindly supplied some details of their EEPROM contents + * which allow us to auto select the Tuner Type. + * Only the newer AVerMedia cards actually have an EEPROM. + */ + if (bktr->card.eepromAddr != 0) { + + u_char tuner_make; /* Eg Philips, Temic */ + u_char tuner_tv_fm; /* TV or TV with FM Radio */ + u_char tuner_format; /* Eg NTSC, PAL, SECAM */ + int tuner; + + int tuner_0_table[] = { + PHILIPS_NTSC, PHILIPS_PAL, + PHILIPS_PAL, PHILIPS_PAL, + PHILIPS_PAL, PHILIPS_PAL, + PHILIPS_SECAM, PHILIPS_SECAM, + PHILIPS_SECAM, PHILIPS_PAL}; + + int tuner_0_fm_table[] = { + PHILIPS_FR1236_NTSC, PHILIPS_FR1216_PAL, + PHILIPS_FR1216_PAL, PHILIPS_FR1216_PAL, + PHILIPS_FR1216_PAL, PHILIPS_FR1216_PAL, + PHILIPS_FR1236_SECAM, PHILIPS_FR1236_SECAM, + PHILIPS_FR1236_SECAM, PHILIPS_FR1216_PAL}; + + int tuner_1_table[] = { + TEMIC_NTSC, TEMIC_PAL, TEMIC_PAL, + TEMIC_PAL, TEMIC_PAL, TEMIC_PAL, + TEMIC_SECAM, TEMIC_SECAM, TEMIC_SECAM, + TEMIC_PAL}; + + + /* Extract information from the EEPROM data */ + readEEProm(bktr, 0, 128, (u_char *) &eeprom ); + + tuner_make = (eeprom[0x41] & 0x7); + tuner_tv_fm = (eeprom[0x41] & 0x18) >> 3; + tuner_format = (eeprom[0x42] & 0xf0) >> 4; + + /* Treat tuner make 0 (Philips) and make 2 (LG) the same */ + if ( ((tuner_make == 0) || (tuner_make == 2)) + && (tuner_format <= 9) && (tuner_tv_fm == 0) ) { + tuner = tuner_0_table[tuner_format]; + select_tuner( bktr, tuner ); + goto checkDBX; + } + + if ( ((tuner_make == 0) || (tuner_make == 2)) + && (tuner_format <= 9) && (tuner_tv_fm == 1) ) { + tuner = tuner_0_fm_table[tuner_format]; + select_tuner( bktr, tuner ); + goto checkDBX; + } + + if ( (tuner_make == 1) && (tuner_format <= 9) ) { + tuner = tuner_1_table[tuner_format]; + select_tuner( bktr, tuner ); + goto checkDBX; + } + + printf("%s: Warning - Unknown AVerMedia Tuner Make %d Format %d\n", + bktr_name(bktr), tuner_make, tuner_format); + } + break; + + case CARD_LEADTEK: +#if BKTR_SYSTEM_DEFAULT == BROOKTREE_PAL + select_tuner( bktr, PHILIPS_FR1216_PAL ); +#else + select_tuner( bktr, PHILIPS_FR1236_NTSC ); +#endif + goto checkDBX; + break; + + } /* end switch(card) */ + + + /* At this point, a goto checkDBX has not occured */ + /* We have not been able to select a Tuner */ + /* Some cards make use of the tuner address to */ + /* identify the make/model of tuner */ + + /* At address 0xc0/0xc1 we often find a TEMIC NTSC */ + if ( i2cRead( bktr, 0xc1 ) != ABSENT ) { + select_tuner( bktr, TEMIC_NTSC ); + goto checkDBX; + } + + /* At address 0xc6/0xc7 we often find a PHILIPS NTSC Tuner */ + if ( i2cRead( bktr, 0xc7 ) != ABSENT ) { + select_tuner( bktr, PHILIPS_NTSC ); + goto checkDBX; + } + + /* Address 0xc2/0xc3 is default (or common address) for several */ + /* tuners and we cannot tell which is which. */ + /* And for all other tuner i2c addresses, select the default */ + select_tuner( bktr, DEFAULT_TUNER ); + + +checkDBX: +#if defined( BKTR_OVERRIDE_DBX ) + bktr->card.dbx = BKTR_OVERRIDE_DBX; + goto checkMSP; +#endif + /* Check for i2c devices */ + if (!any_i2c_devices) { + goto checkMSP; + } + + /* probe for BTSC (dbx) chip */ + if ( i2cRead( bktr, TDA9850_RADDR ) != ABSENT ) + bktr->card.dbx = 1; + +checkMSP: + /* If this is a Hauppauge Bt878 card, we need to enable the + * MSP 34xx audio chip. + * If this is a Hauppauge Bt848 card, reset the MSP device. + * The MSP reset line is wired to GPIO pin 5. On Bt878 cards a pulldown + * resistor holds the device in reset until we set GPIO pin 5. + */ + + /* Optionally skip the MSP reset. This is handy if you initialise the + * MSP audio in another operating system (eg Windows) first and then + * do a soft reboot. + */ + +#ifndef BKTR_NO_MSP_RESET + if (card == CARD_HAUPPAUGE) { + OUTL(bktr, BKTR_GPIO_OUT_EN, INL(bktr, BKTR_GPIO_OUT_EN) | (1<<5)); + OUTL(bktr, BKTR_GPIO_DATA, INL(bktr, BKTR_GPIO_DATA) | (1<<5)); /* write '1' */ + DELAY(2500); /* wait 2.5ms */ + OUTL(bktr, BKTR_GPIO_DATA, INL(bktr, BKTR_GPIO_DATA) & ~(1<<5)); /* write '0' */ + DELAY(2500); /* wait 2.5ms */ + OUTL(bktr, BKTR_GPIO_DATA, INL(bktr, BKTR_GPIO_DATA) | (1<<5)); /* write '1' */ + DELAY(2500); /* wait 2.5ms */ + } +#endif + +#if defined( BKTR_OVERRIDE_MSP ) + bktr->card.msp3400c = BKTR_OVERRIDE_MSP; + goto checkMSPEnd; +#endif + + /* Check for i2c devices */ + if (!any_i2c_devices) { + goto checkMSPEnd; + } + + if ( i2cRead( bktr, MSP3400C_RADDR ) != ABSENT ) { + bktr->card.msp3400c = 1; + } + +checkMSPEnd: + + if (bktr->card.msp3400c) { + bktr->msp_addr = MSP3400C_WADDR; + msp_read_id( bktr ); + printf("%s: Detected a MSP%s at 0x%x\n", bktr_name(bktr), + bktr->msp_version_string, + bktr->msp_addr); + + } + +/* Check for Dolby Surround Sound DPL3518A sound chip */ + if ( i2cRead( bktr, DPL3518A_RADDR ) != ABSENT ) { + bktr->card.dpl3518a = 1; + } + + if (bktr->card.dpl3518a) { + bktr->dpl_addr = DPL3518A_WADDR; + dpl_read_id( bktr ); + printf("%s: Detected a DPL%s at 0x%x\n", bktr_name(bktr), + bktr->dpl_version_string, + bktr->dpl_addr); + } + +/* Start of Check Remote */ + /* Check for the Hauppauge IR Remote Control */ + /* If there is an external unit, the internal will be ignored */ + + bktr->remote_control = 0; /* initial value */ + + if (any_i2c_devices) { + if (i2cRead( bktr, HAUP_REMOTE_EXT_RADDR ) != ABSENT ) + { + bktr->remote_control = 1; + bktr->remote_control_addr = HAUP_REMOTE_EXT_RADDR; + } + else if (i2cRead( bktr, HAUP_REMOTE_INT_RADDR ) != ABSENT ) + { + bktr->remote_control = 1; + bktr->remote_control_addr = HAUP_REMOTE_INT_RADDR; + } + + } + /* If a remote control is found, poll it 5 times to turn off the LED */ + if (bktr->remote_control) { + int i; + for (i=0; i<5; i++) + i2cRead( bktr, bktr->remote_control_addr ); + } +/* End of Check Remote */ + +#if defined( BKTR_USE_PLL ) + bktr->xtal_pll_mode = BT848_USE_PLL; + goto checkPLLEnd; +#endif + /* Default is to use XTALS and not PLL mode */ + bktr->xtal_pll_mode = BT848_USE_XTALS; + + /* Enable PLL mode for OSPREY users */ + if (card == CARD_OSPREY) + bktr->xtal_pll_mode = BT848_USE_PLL; + + /* Enable PLL mode for Video Highway Xtreme users */ + if (card == CARD_VIDEO_HIGHWAY_XTREME) + bktr->xtal_pll_mode = BT848_USE_PLL; + + + /* Most (perhaps all) Bt878 cards need to be switched to PLL mode */ + /* as they only fit the NTSC crystal to their cards */ + /* Default to enabling PLL mode for all Bt878/879 cards */ + + if ((bktr->id==BROOKTREE_878 || bktr->id==BROOKTREE_879) ) + bktr->xtal_pll_mode = BT848_USE_PLL; + + +#if defined( BKTR_USE_PLL ) +checkPLLEnd: +#endif + + + bktr->card.tuner_pllAddr = tuner_i2c_address; + + if ( verbose ) { + printf( "%s: %s", bktr_name(bktr), bktr->card.name ); + if ( bktr->card.tuner ) + printf( ", %s tuner", bktr->card.tuner->name ); + if ( bktr->card.dbx ) + printf( ", dbx stereo" ); + if ( bktr->card.msp3400c ) + printf( ", msp3400c stereo" ); + if ( bktr->card.dpl3518a ) + printf( ", dpl3518a dolby" ); + if ( bktr->remote_control ) + printf( ", remote control" ); + printf( ".\n" ); + } +} + +#undef ABSENT diff --git a/sys/dev/pci/bktr/bktr_card.h b/sys/dev/pci/bktr/bktr_card.h new file mode 100644 index 00000000000..5d948440c7d --- /dev/null +++ b/sys/dev/pci/bktr/bktr_card.h @@ -0,0 +1,88 @@ +/* $OpenBSD: bktr_card.h,v 1.1 2001/03/28 03:27:09 fgsch Exp $ */ +/* $FreeBSD: src/sys/dev/bktr/bktr_card.h,v 1.4 2000/06/26 09:41:31 roger Exp $ */ + +/* + * This is part of the Driver for Video Capture Cards (Frame grabbers) + * and TV Tuner cards using the Brooktree Bt848, Bt848A, Bt849A, Bt878, Bt879 + * chipset. + * Copyright Roger Hardiman and Amancio Hasty. + * + * bktr_card : This deals with identifying TV cards. + * trying to find the card make and model of card. + * trying to find the type of tuner fitted. + * reading the configuration EEPROM. + * locating i2c devices. + * + */ + +/* + * 1. Redistributions of source code must retain the + * Copyright (c) 1997 Amancio Hasty, 1999 Roger Hardiman + * 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 and + * Roger Hardiman + * 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. + */ + +/* + * If probeCard() fails to detect the correct card on boot you can + * override it by setting adding the following option to your kernel config + * options BKTR_OVERRIDE_CARD <card type> + * eg options BKTR_OVERRIDE CARD=1 + * + * or using the sysclt hw.bt848.card + * eg sysctl -w hw.bt848.card=1 + * + * where <card type> is one of the following card defines. + */ + +#define CARD_UNKNOWN 0 +#define CARD_MIRO 1 +#define CARD_HAUPPAUGE 2 +#define CARD_STB 3 +#define CARD_INTEL 4 /* Also for VideoLogic Captivator PCI */ +#define CARD_IMS_TURBO 5 +#define CARD_AVER_MEDIA 6 +#define CARD_OSPREY 7 +#define CARD_NEC_PK 8 +#define CARD_IO_GV 9 +#define CARD_FLYVIDEO 10 +#define CARD_ZOLTRIX 11 +#define CARD_KISS 12 +#define CARD_VIDEO_HIGHWAY_XTREME 13 +#define CARD_ASKEY_DYNALINK_MAGIC_TVIEW 14 +#define CARD_LEADTEK 15 +#define CARD_TERRATVPLUS 16 +#define Bt848_MAX_CARD 17 + + +int signCard( bktr_ptr_t bktr, int offset, int count, u_char* sig ); +void probeCard( bktr_ptr_t bktr, int verbose, int unit); + +int writeEEProm( bktr_ptr_t bktr, int offset, int count, u_char *data ); +int readEEProm( bktr_ptr_t bktr, int offset, int count, u_char *data ); + diff --git a/sys/dev/pci/bktr/bktr_core.c b/sys/dev/pci/bktr/bktr_core.c new file mode 100644 index 00000000000..16f56150d44 --- /dev/null +++ b/sys/dev/pci/bktr/bktr_core.c @@ -0,0 +1,4264 @@ +/* $OpenBSD: bktr_core.c,v 1.1 2001/03/28 03:27:09 fgsch Exp $ */ +/* $FreeBSD: src/sys/dev/bktr/bktr_core.c,v 1.114 2000/10/31 13:09:56 roger Exp $ */ + +/* + * This is part of the Driver for Video Capture Cards (Frame grabbers) + * and TV Tuner cards using the Brooktree Bt848, Bt848A, Bt849A, Bt878, Bt879 + * chipset. + * Copyright Roger Hardiman and Amancio Hasty. + * + * bktr_core : This deals with the Bt848/849/878/879 PCI Frame Grabber, + * Handles all the open, close, ioctl and read userland calls. + * Sets the Bt848 registers and generates RISC pograms. + * Controls the i2c bus and GPIO interface. + * Contains the interface to the kernel. + * (eg probe/attach and open/close/ioctl) + * + */ + + /* + 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. + + The original copyright notice by Mark and Jim is included mostly + to honor their fantastic work in the Matrox Meteor driver! + + */ + +/* + * 1. Redistributions of source code must retain the + * Copyright (c) 1997 Amancio Hasty, 1999 Roger Hardiman + * 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 and + * Roger Hardiman + * 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. + */ + +#ifndef __OpenBSD__ +#include "opt_bktr.h" /* Include any kernel config options */ +#endif + +#ifdef __FreeBSD__ +#include "bktr.h" +#endif /* __FreeBSD__ */ + +#if ( \ + (defined(__FreeBSD__) && (NBKTR > 0)) \ + || (defined(__bsdi__)) \ + || (defined(__OpenBSD__)) \ + || (defined(__NetBSD__)) \ + ) + + +/*******************/ +/* *** FreeBSD *** */ +/*******************/ +#ifdef __FreeBSD__ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/signalvar.h> +#include <sys/vnode.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <vm/pmap.h> +#include <vm/vm_extern.h> + +#if (__FreeBSD_version >=400000) || (NSMBUS > 0) +#include <sys/bus.h> /* used by smbus and newbus */ +#endif + +#if (__FreeBSD_version < 500000) +#include <machine/clock.h> /* for DELAY */ +#endif + +#include <pci/pcivar.h> + +#if (__FreeBSD_version >=300000) +#include <machine/bus_memio.h> /* for bus space */ +#include <machine/bus.h> +#include <sys/bus.h> +#endif + +#include <machine/ioctl_meteor.h> +#include <machine/ioctl_bt848.h> /* extensions to ioctl_meteor.h */ +#include <dev/bktr/bktr_reg.h> +#include <dev/bktr/bktr_tuner.h> +#include <dev/bktr/bktr_card.h> +#include <dev/bktr/bktr_audio.h> +#include <dev/bktr/bktr_os.h> +#include <dev/bktr/bktr_core.h> +#if defined(BKTR_FREEBSD_MODULE) +#include <dev/bktr/bktr_mem.h> +#endif + +#if defined(BKTR_USE_FREEBSD_SMBUS) +#include <dev/bktr/bktr_i2c.h> +#include <dev/smbus/smbconf.h> +#include <dev/iicbus/iiconf.h> +#include "smbus_if.h" +#include "iicbus_if.h" +#endif + +const char * +bktr_name(bktr_ptr_t bktr) +{ + return bktr->bktr_xname; +} + + +#if (__FreeBSD__ == 2) +typedef unsigned int uintptr_t; +#endif +#endif /* __FreeBSD__ */ + + +/****************/ +/* *** BSDI *** */ +/****************/ +#ifdef __bsdi__ +#endif /* __bsdi__ */ + + +/**************************/ +/* *** OpenBSD/NetBSD *** */ +/**************************/ +#if defined(__NetBSD__) || defined(__OpenBSD__) + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/signalvar.h> +#include <sys/vnode.h> + +#ifdef __NetBSD__ +#include <uvm/uvm_extern.h> +#else +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#endif + +#ifdef __OpenBSD__ +typedef unsigned long uintptr_t; +typedef int intrmask_t; +#else +#include <sys/inttypes.h> /* uintptr_t */ +#endif +#include <dev/ic/bt8xx.h> +#include <dev/pci/bktr/bktr_reg.h> +#include <dev/pci/bktr/bktr_tuner.h> +#include <dev/pci/bktr/bktr_card.h> +#include <dev/pci/bktr/bktr_audio.h> +#include <dev/pci/bktr/bktr_core.h> +#include <dev/pci/bktr/bktr_os.h> + +static int bt848_format = -1; + +const char * +bktr_name(bktr_ptr_t bktr) +{ + return (bktr->bktr_dev.dv_xname); +} + +#endif /* __NetBSD__ || __OpenBSD__ */ + + + +typedef u_char bool_t; + +#define BKTRPRI (PZERO+8)|PCATCH +#define VBIPRI (PZERO-4)|PCATCH + + +/* + * memory allocated for DMA programs + */ +#define DMA_PROG_ALLOC (8 * PAGE_SIZE) + +/* When to split a dma transfer , the bt848 has timing as well as + dma transfer size limitations so that we have to split dma + transfers into two dma requests + */ +#define DMA_BT848_SPLIT 319*2 + +/* + * Allocate enough memory for: + * 768x576 RGB 16 or YUV (16 storage bits/pixel) = 884736 = 216 pages + * + * You may override this using the options "BROOKTREE_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) + +/* Definitions for VBI capture. + * There are 16 VBI lines in a PAL video field (32 in a frame), + * and we take 2044 samples from each line (placed in a 2048 byte buffer + * for alignment). + * VBI lines are held in a circular buffer before being read by a + * user program from /dev/vbi. + */ + +#define MAX_VBI_LINES 16 /* Maximum for all vidoe formats */ +#define VBI_LINE_SIZE 2048 /* Store upto 2048 bytes per line */ +#define VBI_BUFFER_ITEMS 20 /* Number of frames we buffer */ +#define VBI_DATA_SIZE (VBI_LINE_SIZE * MAX_VBI_LINES * 2) +#define VBI_BUFFER_SIZE (VBI_DATA_SIZE * VBI_BUFFER_ITEMS) + + +/* Defines for fields */ +#define ODD_F 0x01 +#define EVEN_F 0x02 + + +/* + * Parameters describing size of transmitted image. + */ + +static struct format_params format_params[] = { +/* # define BT848_IFORM_F_AUTO (0x0) - don't matter. */ + { 525, 26, 480, 910, 135, 754, 640, 780, 30, 0x68, 0x5d, BT848_IFORM_X_AUTO, + 12, 1600 }, +/* # define BT848_IFORM_F_NTSCM (0x1) */ + { 525, 26, 480, 910, 135, 754, 640, 780, 30, 0x68, 0x5d, BT848_IFORM_X_XT0, + 12, 1600 }, +/* # define BT848_IFORM_F_NTSCJ (0x2) */ + { 525, 22, 480, 910, 135, 754, 640, 780, 30, 0x68, 0x5d, BT848_IFORM_X_XT0, + 12, 1600 }, +/* # define BT848_IFORM_F_PALBDGHI (0x3) */ + { 625, 32, 576, 1135, 186, 924, 768, 944, 25, 0x7f, 0x72, BT848_IFORM_X_XT1, + 16, 2044 }, +/* # define BT848_IFORM_F_PALM (0x4) */ + { 525, 22, 480, 910, 135, 754, 640, 780, 30, 0x68, 0x5d, BT848_IFORM_X_XT0, + 12, 1600 }, +/* # define BT848_IFORM_F_PALN (0x5) */ + { 625, 32, 576, 1135, 186, 924, 768, 944, 25, 0x7f, 0x72, BT848_IFORM_X_XT1, + 16, 2044 }, +/* # define BT848_IFORM_F_SECAM (0x6) */ + { 625, 32, 576, 1135, 186, 924, 768, 944, 25, 0x7f, 0xa0, BT848_IFORM_X_XT1, + 16, 2044 }, +/* # define BT848_IFORM_F_RSVD (0x7) - ???? */ + { 625, 32, 576, 1135, 186, 924, 768, 944, 25, 0x7f, 0x72, BT848_IFORM_X_XT0, + 16, 2044 }, +}; + +/* + * Table of supported Pixel Formats + */ + +static struct meteor_pixfmt_internal { + struct meteor_pixfmt public; + u_int color_fmt; +} pixfmt_table[] = { + +{ { 0, METEOR_PIXTYPE_RGB, 2, { 0x7c00, 0x03e0, 0x001f }, 0,0 }, 0x33 }, +{ { 0, METEOR_PIXTYPE_RGB, 2, { 0x7c00, 0x03e0, 0x001f }, 1,0 }, 0x33 }, + +{ { 0, METEOR_PIXTYPE_RGB, 2, { 0xf800, 0x07e0, 0x001f }, 0,0 }, 0x22 }, +{ { 0, METEOR_PIXTYPE_RGB, 2, { 0xf800, 0x07e0, 0x001f }, 1,0 }, 0x22 }, + +{ { 0, METEOR_PIXTYPE_RGB, 3, { 0xff0000,0x00ff00,0x0000ff }, 1,0 }, 0x11 }, + +{ { 0, METEOR_PIXTYPE_RGB, 4, { 0xff0000,0x00ff00,0x0000ff }, 0,0 }, 0x00 }, +{ { 0, METEOR_PIXTYPE_RGB, 4, { 0xff0000,0x00ff00,0x0000ff }, 0,1 }, 0x00 }, +{ { 0, METEOR_PIXTYPE_RGB, 4, { 0xff0000,0x00ff00,0x0000ff }, 1,0 }, 0x00 }, +{ { 0, METEOR_PIXTYPE_RGB, 4, { 0xff0000,0x00ff00,0x0000ff }, 1,1 }, 0x00 }, +{ { 0, METEOR_PIXTYPE_YUV, 2, { 0xff0000,0x00ff00,0x0000ff }, 1,1 }, 0x88 }, +{ { 0, METEOR_PIXTYPE_YUV_PACKED, 2, { 0xff0000,0x00ff00,0x0000ff }, 0,1 }, 0x44 }, +{ { 0, METEOR_PIXTYPE_YUV_12, 2, { 0xff0000,0x00ff00,0x0000ff }, 1,1 }, 0x88 }, + +}; +#define PIXFMT_TABLE_SIZE ( sizeof(pixfmt_table) / sizeof(pixfmt_table[0]) ) + +/* + * Table of Meteor-supported Pixel Formats (for SETGEO compatibility) + */ + +/* FIXME: Also add YUV_422 and YUV_PACKED as well */ +static struct { + u_long meteor_format; + struct meteor_pixfmt public; +} meteor_pixfmt_table[] = { + { METEOR_GEO_YUV_12, + { 0, METEOR_PIXTYPE_YUV_12, 2, { 0xff0000,0x00ff00,0x0000ff }, 1,1 } + }, + + /* FIXME: Should byte swap flag be on for this one; negative in drvr? */ + { METEOR_GEO_YUV_422, + { 0, METEOR_PIXTYPE_YUV, 2, { 0xff0000,0x00ff00,0x0000ff }, 1,1 } + }, + { METEOR_GEO_YUV_PACKED, + { 0, METEOR_PIXTYPE_YUV_PACKED, 2, { 0xff0000,0x00ff00,0x0000ff }, 0,1 } + }, + { METEOR_GEO_RGB16, + { 0, METEOR_PIXTYPE_RGB, 2, { 0x7c00, 0x03e0, 0x001f }, 0, 0 } + }, + { METEOR_GEO_RGB24, + { 0, METEOR_PIXTYPE_RGB, 4, { 0xff0000, 0x00ff00, 0x0000ff }, 0, 0 } + }, + +}; +#define METEOR_PIXFMT_TABLE_SIZE ( sizeof(meteor_pixfmt_table) / \ + sizeof(meteor_pixfmt_table[0]) ) + + +#define BSWAP (BT848_COLOR_CTL_BSWAP_ODD | BT848_COLOR_CTL_BSWAP_EVEN) +#define WSWAP (BT848_COLOR_CTL_WSWAP_ODD | BT848_COLOR_CTL_WSWAP_EVEN) + + + +/* sync detect threshold */ +#if 0 +#define SYNC_LEVEL (BT848_ADC_RESERVED | \ + BT848_ADC_CRUSH) /* threshold ~125 mV */ +#else +#define SYNC_LEVEL (BT848_ADC_RESERVED | \ + BT848_ADC_SYNC_T) /* threshold ~75 mV */ +#endif + + + + +/* debug utility for holding previous INT_STAT contents */ +#define STATUS_SUM +static u_long status_sum = 0; + +/* + * defines to make certain bit-fiddles understandable + */ +#define FIFO_ENABLED BT848_DMA_CTL_FIFO_EN +#define RISC_ENABLED BT848_DMA_CTL_RISC_EN +#define FIFO_RISC_ENABLED (BT848_DMA_CTL_FIFO_EN | BT848_DMA_CTL_RISC_EN) +#define FIFO_RISC_DISABLED 0 + +#define ALL_INTS_DISABLED 0 +#define ALL_INTS_CLEARED 0xffffffff +#define CAPTURE_OFF 0 + +#define BIT_SEVEN_HIGH (1<<7) +#define BIT_EIGHT_HIGH (1<<8) + +#define I2C_BITS (BT848_INT_RACK | BT848_INT_I2CDONE) +#define TDEC_BITS (BT848_INT_FDSR | BT848_INT_FBUS) + + + +static int oformat_meteor_to_bt( u_long format ); + +static u_int pixfmt_swap_flags( int pixfmt ); + +/* + * bt848 RISC programming routines. + */ +#ifdef BT848_DUMP +static int dump_bt848( bktr_ptr_t bktr ); +#endif + +static void yuvpack_prog( bktr_ptr_t bktr, char i_flag, int cols, + int rows, int interlace ); +static void yuv422_prog( bktr_ptr_t bktr, char i_flag, int cols, + int rows, int interlace ); +static void yuv12_prog( bktr_ptr_t bktr, char i_flag, int cols, + int rows, int interlace ); +static void rgb_prog( bktr_ptr_t bktr, char i_flag, int cols, + int rows, int interlace ); +static void rgb_vbi_prog( bktr_ptr_t bktr, char i_flag, int cols, + int rows, int interlace ); +static void build_dma_prog( bktr_ptr_t bktr, char i_flag ); + +static bool_t getline(bktr_reg_t *, int); +static bool_t notclipped(bktr_reg_t * , int , int); +static bool_t split(bktr_reg_t *, volatile u_long **, int, u_long, int, + volatile u_char ** , int ); + +static void start_capture( bktr_ptr_t bktr, unsigned type ); +static void set_fps( bktr_ptr_t bktr, u_short fps ); + + + +/* + * Remote Control Functions + */ +static void remote_read(bktr_ptr_t bktr, struct bktr_remote *remote); + + +/* + * ioctls common to both video & tuner. + */ +static int common_ioctl( bktr_ptr_t bktr, ioctl_cmd_t cmd, caddr_t arg ); + + +#if !defined(BKTR_USE_FREEBSD_SMBUS) +/* + * i2c primitives for low level control of i2c bus. Added for MSP34xx control + */ +static void i2c_start( bktr_ptr_t bktr); +static void i2c_stop( bktr_ptr_t bktr); +static int i2c_write_byte( bktr_ptr_t bktr, unsigned char data); +static int i2c_read_byte( bktr_ptr_t bktr, unsigned char *data, int last ); +#endif + + + +/* + * the common attach code, used by all OS versions. + */ +void +common_bktr_attach( bktr_ptr_t bktr, int unit, u_long pci_id, u_int rev ) +{ + vm_offset_t buf = 0; + +/***************************************/ +/* *** OS Specific memory routines *** */ +/***************************************/ +#if defined(__NetBSD__) || defined(__OpenBSD__) + /* allocate space for dma program */ + bktr->dma_prog = get_bktr_mem(bktr, &bktr->dm_prog, + DMA_PROG_ALLOC); + bktr->odd_dma_prog = get_bktr_mem(bktr, &bktr->dm_oprog, + DMA_PROG_ALLOC); + + /* allocate space for the VBI buffer */ + bktr->vbidata = get_bktr_mem(bktr, &bktr->dm_vbidata, + VBI_DATA_SIZE); + bktr->vbibuffer = get_bktr_mem(bktr, &bktr->dm_vbibuffer, + VBI_BUFFER_SIZE); + + /* allocate space for pixel buffer */ + if ( BROOKTREE_ALLOC ) + buf = get_bktr_mem(bktr, &bktr->dm_mem, BROOKTREE_ALLOC); + else + buf = 0; +#endif + +#if defined(__FreeBSD__) || defined(__bsdi__) + int need_to_allocate_memory = 1; + +/* If this is a module, check if there is any currently saved contiguous memory */ +#if defined(BKTR_FREEBSD_MODULE) + if (bktr_has_stored_addresses(unit) == 1) { + /* recover the addresses */ + bktr->dma_prog = bktr_retrieve_address(unit, BKTR_MEM_DMA_PROG); + bktr->odd_dma_prog = bktr_retrieve_address(unit, BKTR_MEM_ODD_DMA_PROG); + bktr->vbidata = bktr_retrieve_address(unit, BKTR_MEM_VBIDATA); + bktr->vbibuffer = bktr_retrieve_address(unit, BKTR_MEM_VBIBUFFER); + buf = bktr_retrieve_address(unit, BKTR_MEM_BUF); + need_to_allocate_memory = 0; + } +#endif + + if (need_to_allocate_memory == 1) { + /* allocate space for dma program */ + bktr->dma_prog = get_bktr_mem(unit, DMA_PROG_ALLOC); + bktr->odd_dma_prog = get_bktr_mem(unit, DMA_PROG_ALLOC); + + /* allocte space for the VBI buffer */ + bktr->vbidata = get_bktr_mem(unit, VBI_DATA_SIZE); + bktr->vbibuffer = get_bktr_mem(unit, VBI_BUFFER_SIZE); + + /* allocate space for pixel buffer */ + if ( BROOKTREE_ALLOC ) + buf = get_bktr_mem(unit, BROOKTREE_ALLOC); + else + buf = 0; + } +#endif /* FreeBSD or BSDi */ + + +/* If this is a module, save the current contiguous memory */ +#if defined(BKTR_FREEBSD_MODULE) +bktr_store_address(unit, BKTR_MEM_DMA_PROG, bktr->dma_prog); +bktr_store_address(unit, BKTR_MEM_ODD_DMA_PROG, bktr->odd_dma_prog); +bktr_store_address(unit, BKTR_MEM_VBIDATA, bktr->vbidata); +bktr_store_address(unit, BKTR_MEM_VBIBUFFER, bktr->vbibuffer); +bktr_store_address(unit, BKTR_MEM_BUF, buf); +#endif + + + if ( bootverbose ) { + printf("%s: buffer size %d, addr 0x%x\n", + bktr_name(bktr), BROOKTREE_ALLOC, vtophys(buf)); + } + + if ( buf != 0 ) { + bktr->bigbuf = buf; + bktr->alloc_pages = BROOKTREE_ALLOC_PAGES; + bzero((caddr_t) bktr->bigbuf, BROOKTREE_ALLOC); + } else { + bktr->alloc_pages = 0; + } + + + bktr->flags = METEOR_INITALIZED | METEOR_AUTOMODE | + METEOR_DEV0 | METEOR_RGB16; + bktr->dma_prog_loaded = FALSE; + bktr->cols = 640; + bktr->rows = 480; + bktr->frames = 1; /* one frame */ + bktr->format = METEOR_GEO_RGB16; + bktr->pixfmt = oformat_meteor_to_bt( bktr->format ); + bktr->pixfmt_compat = TRUE; + + + bktr->vbiinsert = 0; + bktr->vbistart = 0; + bktr->vbisize = 0; + bktr->vbiflags = 0; + + + /* using the pci device id and revision id */ + /* and determine the card type */ + if (PCI_VENDOR(pci_id) == PCI_VENDOR_BROOKTREE) + { + switch (PCI_PRODUCT(pci_id)) { + case PCI_PRODUCT_BROOKTREE_BT848: + if (rev == 0x12) + bktr->id = BROOKTREE_848A; + else + bktr->id = BROOKTREE_848; + break; + case PCI_PRODUCT_BROOKTREE_BT849: + bktr->id = BROOKTREE_849A; + break; + case PCI_PRODUCT_BROOKTREE_BT878: + bktr->id = BROOKTREE_878; + break; + case PCI_PRODUCT_BROOKTREE_BT879: + bktr->id = BROOKTREE_879; + break; + } + }; + + bktr->clr_on_start = FALSE; + + /* defaults for the tuner section of the card */ + bktr->tflags = TUNER_INITALIZED; + bktr->tuner.frequency = 0; + bktr->tuner.channel = 0; + bktr->tuner.chnlset = DEFAULT_CHNLSET; + bktr->tuner.afc = 0; + bktr->tuner.radio_mode = 0; + bktr->audio_mux_select = 0; + bktr->audio_mute_state = FALSE; + bktr->bt848_card = -1; + bktr->bt848_tuner = -1; + bktr->reverse_mute = -1; + bktr->slow_msp_audio = 0; + bktr->msp_use_mono_source = 0; + bktr->msp_source_selected = -1; + bktr->audio_mux_present = 1; + + probeCard( bktr, TRUE, unit ); + + /* Initialise any MSP34xx or TDA98xx audio chips */ + init_audio_devices( bktr ); + +} + + +/* Copy the vbi lines from 'vbidata' into the circular buffer, 'vbibuffer'. + * The circular buffer holds 'n' fixed size data blocks. + * vbisize is the number of bytes in the circular buffer + * vbiread is the point we reading data out of the circular buffer + * vbiinsert is the point we insert data into the circular buffer + */ +static void vbidecode(bktr_ptr_t bktr) { + unsigned char *dest; + unsigned int *seq_dest; + + /* Check if there is room in the buffer to insert the data. */ + if (bktr->vbisize + VBI_DATA_SIZE > VBI_BUFFER_SIZE) return; + + /* Copy the VBI data into the next free slot in the buffer. */ + /* 'dest' is the point in vbibuffer where we want to insert new data */ + dest = (unsigned char *)bktr->vbibuffer + bktr->vbiinsert; + memcpy(dest, (unsigned char*)bktr->vbidata, VBI_DATA_SIZE); + + /* Write the VBI sequence number to the end of the vbi data */ + /* This is used by the AleVT teletext program */ + seq_dest = (unsigned int *)((unsigned char *)bktr->vbibuffer + + bktr->vbiinsert + + (VBI_DATA_SIZE - sizeof(bktr->vbi_sequence_number))); + *seq_dest = bktr->vbi_sequence_number; + + /* And increase the VBI sequence number */ + /* This can wrap around */ + bktr->vbi_sequence_number++; + + + /* Increment the vbiinsert pointer */ + /* This can wrap around */ + bktr->vbiinsert += VBI_DATA_SIZE; + bktr->vbiinsert = (bktr->vbiinsert % VBI_BUFFER_SIZE); + + /* And increase the amount of vbi data in the buffer */ + bktr->vbisize = bktr->vbisize + VBI_DATA_SIZE; + +} + + +/* + * the common interrupt handler. + * Returns a 0 or 1 depending on whether the interrupt has handled. + * In the OS specific section, bktr_intr() is defined which calls this + * common interrupt handler. + */ +int +common_bktr_intr( void *arg ) +{ + bktr_ptr_t bktr; + u_long bktr_status; + u_char dstatus; + u_long field; + u_long w_field; + u_long req_field; + + bktr = (bktr_ptr_t) arg; + + /* + * check to see if any interrupts are unmasked on this device. If + * none are, then we likely got here by way of being on a PCI shared + * interrupt dispatch list. + */ + if (INL(bktr, BKTR_INT_MASK) == ALL_INTS_DISABLED) + return 0; /* bail out now, before we do something we + shouldn't */ + + if (!(bktr->flags & METEOR_OPEN)) { + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED); + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + /* return; ?? */ + } + + /* record and clear the INTerrupt status bits */ + bktr_status = INL(bktr, BKTR_INT_STAT); + OUTL(bktr, BKTR_INT_STAT, bktr_status & ~I2C_BITS); /* don't touch i2c */ + + /* record and clear the device status register */ + dstatus = INB(bktr, BKTR_DSTATUS); + OUTB(bktr, BKTR_DSTATUS, 0x00); + +#if defined( STATUS_SUM ) + /* add any new device status or INTerrupt status bits */ + status_sum |= (bktr_status & ~(BT848_INT_RSV0|BT848_INT_RSV1)); + status_sum |= ((dstatus & (BT848_DSTATUS_COF|BT848_DSTATUS_LOF)) << 6); +#endif /* STATUS_SUM */ + /* printf( "%s: STATUS %x %x %x \n", bktr_name(bktr), + dstatus, bktr_status, INL(bktr, BKTR_RISC_COUNT) ); + */ + + + /* if risc was disabled re-start process again */ + /* if there was one of the following errors re-start again */ + if ( !(bktr_status & BT848_INT_RISC_EN) || + ((bktr_status &(/* BT848_INT_FBUS | */ + /* BT848_INT_FTRGT | */ + /* BT848_INT_FDSR | */ + BT848_INT_PPERR | + BT848_INT_RIPERR | BT848_INT_PABORT | + BT848_INT_OCERR | BT848_INT_SCERR) ) != 0) + || ((INB(bktr, BKTR_TDEC) == 0) && (bktr_status & TDEC_BITS)) ) { + + u_short tdec_save = INB(bktr, BKTR_TDEC); + + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED); + OUTB(bktr, BKTR_CAP_CTL, CAPTURE_OFF); + + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + + /* Reset temporal decimation counter */ + OUTB(bktr, BKTR_TDEC, 0); + OUTB(bktr, BKTR_TDEC, tdec_save); + + /* Reset to no-fields captured state */ + 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; + } + } + + OUTL(bktr, BKTR_RISC_STRT_ADD, vtophys(bktr->dma_prog)); + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_ENABLED); + OUTW(bktr, BKTR_GPIO_DMA_CTL, bktr->capcontrol); + + OUTL(bktr, BKTR_INT_MASK, BT848_INT_MYSTERYBIT | + BT848_INT_RISCI | + BT848_INT_VSYNC | + BT848_INT_FMTCHG); + + OUTB(bktr, BKTR_CAP_CTL, bktr->bktr_cap_ctl); + return 1; + } + + /* If this is not a RISC program interrupt, return */ + if (!(bktr_status & BT848_INT_RISCI)) + return 0; + +/** + printf( "%s: intr status %x %x %x\n", bktr_name(bktr), + bktr_status, dstatus, INL(bktr, BKTR_RISC_COUNT) ); + */ + + + /* + * 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)) + OUTB(bktr, BKTR_CAP_CTL, CAPTURE_OFF); + + + /* Determine which field generated this interrupt */ + field = ( bktr_status & BT848_INT_FIELD ) ? EVEN_F : ODD_F; + + + /* + * Process the VBI data if it is being captured. We do this once + * both Odd and Even VBI data is captured. Therefore we do this + * in the Even field interrupt handler. + */ + if ( (bktr->vbiflags & VBI_CAPTURE) + &&(bktr->vbiflags & VBI_OPEN) + &&(field==EVEN_F)) { + /* Put VBI data into circular buffer */ + vbidecode(bktr); + + /* If someone is blocked on reading from /dev/vbi, wake them */ + if (bktr->vbi_read_blocked) { + bktr->vbi_read_blocked = FALSE; + wakeup(VBI_SLEEP); + } + + /* If someone has a select() on /dev/vbi, inform them */ +#ifndef __OpenBSD__ + if (bktr->vbi_select.si_pid) { +#else + if (bktr->vbi_select.si_selpid) { +#endif + selwakeup(&bktr->vbi_select); + } + + + } + + + /* + * Register the completed field + * (For dual-field mode, require fields from the same frame) + */ + switch ( bktr->flags & METEOR_WANT_MASK ) { + case METEOR_WANT_ODD : w_field = ODD_F ; break; + case METEOR_WANT_EVEN : w_field = EVEN_F ; break; + default : w_field = (ODD_F|EVEN_F); break; + } + switch ( bktr->flags & METEOR_ONLY_FIELDS_MASK ) { + case METEOR_ONLY_ODD_FIELDS : req_field = ODD_F ; break; + case METEOR_ONLY_EVEN_FIELDS : req_field = EVEN_F ; break; + default : req_field = (ODD_F|EVEN_F); + break; + } + + if (( field == EVEN_F ) && ( w_field == EVEN_F )) + bktr->flags &= ~METEOR_WANT_EVEN; + else if (( field == ODD_F ) && ( req_field == ODD_F ) && + ( w_field == ODD_F )) + bktr->flags &= ~METEOR_WANT_ODD; + else if (( field == ODD_F ) && ( req_field == (ODD_F|EVEN_F) ) && + ( w_field == (ODD_F|EVEN_F) )) + bktr->flags &= ~METEOR_WANT_ODD; + else if (( field == ODD_F ) && ( req_field == (ODD_F|EVEN_F) ) && + ( w_field == ODD_F )) { + bktr->flags &= ~METEOR_WANT_ODD; + bktr->flags |= METEOR_WANT_EVEN; + } + else { + /* We're out of sync. Start over. */ + 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 1; + } + + /* + * 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) { + + /* stop dma */ + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + + /* disable risc, leave fifo running */ + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_ENABLED); + wakeup(BKTR_SLEEP); + } + + /* + * 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. + */ +/* +* XXX NOTE (Luigi): +* currently we only support 3 capture modes: odd only, even only, +* odd+even interlaced (odd field first). A fourth mode (non interlaced, +* either even OR odd) could provide 60 (50 for PAL) pictures per +* second, but it would require this routine to toggle the desired frame +* each time, and one more different DMA program for the Bt848. +* As a consequence, this fourth mode is currently unsupported. +*/ + + 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 1; +} + + + + +/* + * + */ +extern int bt848_format; /* used to set the default format, PAL or NTSC */ +int +video_open( bktr_ptr_t bktr ) +{ + int frame_rate, video_format=0; + + if (bktr->flags & METEOR_OPEN) /* device is busy */ + return( EBUSY ); + + bktr->flags |= METEOR_OPEN; + +#ifdef BT848_DUMP + dump_bt848( bt848 ); +#endif + + bktr->clr_on_start = FALSE; + + OUTB(bktr, BKTR_DSTATUS, 0x00); /* clear device status reg. */ + + OUTB(bktr, BKTR_ADC, SYNC_LEVEL); + +#if BKTR_SYSTEM_DEFAULT == BROOKTREE_PAL + video_format = 0; +#else + video_format = 1; +#endif + + if (bt848_format == 0 ) + video_format = 0; + + if (bt848_format == 1 ) + video_format = 1; + + if (video_format == 1 ) { + OUTB(bktr, BKTR_IFORM, BT848_IFORM_F_NTSCM); + bktr->format_params = BT848_IFORM_F_NTSCM; + + } else { + OUTB(bktr, BKTR_IFORM, BT848_IFORM_F_PALBDGHI); + bktr->format_params = BT848_IFORM_F_PALBDGHI; + + } + + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) | format_params[bktr->format_params].iform_xtsel); + + /* work around for new Hauppauge 878 cards */ + if ((bktr->card.card_id == CARD_HAUPPAUGE) && + (bktr->id==BROOKTREE_878 || bktr->id==BROOKTREE_879) ) + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) | BT848_IFORM_M_MUX3); + else + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) | BT848_IFORM_M_MUX1); + + OUTB(bktr, BKTR_ADELAY, format_params[bktr->format_params].adelay); + OUTB(bktr, BKTR_BDELAY, format_params[bktr->format_params].bdelay); + frame_rate = format_params[bktr->format_params].frame_rate; + + /* enable PLL mode using 28Mhz crystal for PAL/SECAM users */ + if (bktr->xtal_pll_mode == BT848_USE_PLL) { + OUTB(bktr, BKTR_TGCTRL, 0); + OUTB(bktr, BKTR_PLL_F_LO, 0xf9); + OUTB(bktr, BKTR_PLL_F_HI, 0xdc); + OUTB(bktr, BKTR_PLL_F_XCI, 0x8e); + } + + bktr->flags = (bktr->flags & ~METEOR_DEV_MASK) | METEOR_DEV0; + + bktr->max_clip_node = 0; + + OUTB(bktr, BKTR_COLOR_CTL, BT848_COLOR_CTL_GAMMA | BT848_COLOR_CTL_RGB_DED); + + OUTB(bktr, BKTR_E_HSCALE_LO, 170); + OUTB(bktr, BKTR_O_HSCALE_LO, 170); + + OUTB(bktr, BKTR_E_DELAY_LO, 0x72); + OUTB(bktr, BKTR_O_DELAY_LO, 0x72); + OUTB(bktr, BKTR_E_SCLOOP, 0); + OUTB(bktr, BKTR_O_SCLOOP, 0); + + OUTB(bktr, BKTR_VBI_PACK_SIZE, 0); + OUTB(bktr, BKTR_VBI_PACK_DEL, 0); + + 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, frame_rate); + bktr->video.addr = 0; + bktr->video.width = 0; + bktr->video.banksize = 0; + bktr->video.ramsize = 0; + bktr->pixfmt_compat = TRUE; + bktr->format = METEOR_GEO_RGB16; + bktr->pixfmt = oformat_meteor_to_bt( bktr->format ); + + bktr->capture_area_enabled = FALSE; + + OUTL(bktr, BKTR_INT_MASK, BT848_INT_MYSTERYBIT); /* if you take this out triton + based motherboards will + operate unreliably */ + return( 0 ); +} + +int +vbi_open( bktr_ptr_t bktr ) +{ + if (bktr->vbiflags & VBI_OPEN) /* device is busy */ + return( EBUSY ); + + bktr->vbiflags |= VBI_OPEN; + + /* reset the VBI circular buffer pointers and clear the buffers */ + bktr->vbiinsert = 0; + bktr->vbistart = 0; + bktr->vbisize = 0; + bktr->vbi_sequence_number = 0; + bktr->vbi_read_blocked = FALSE; + + bzero((caddr_t) bktr->vbibuffer, VBI_BUFFER_SIZE); + bzero((caddr_t) bktr->vbidata, VBI_DATA_SIZE); + + return( 0 ); +} + +/* + * + */ +int +tuner_open( bktr_ptr_t bktr ) +{ + if ( !(bktr->tflags & TUNER_INITALIZED) ) /* device not found */ + return( ENXIO ); + + if ( bktr->tflags & TUNER_OPEN ) /* already open */ + return( 0 ); + + bktr->tflags |= TUNER_OPEN; + bktr->tuner.frequency = 0; + bktr->tuner.channel = 0; + bktr->tuner.chnlset = DEFAULT_CHNLSET; + bktr->tuner.afc = 0; + bktr->tuner.radio_mode = 0; + + /* enable drivers on the GPIO port that control the MUXes */ + OUTL(bktr, BKTR_GPIO_OUT_EN, INL(bktr, BKTR_GPIO_OUT_EN) | bktr->card.gpio_mux_bits); + + /* unmute the audio stream */ + set_audio( bktr, AUDIO_UNMUTE ); + + /* Initialise any audio chips, eg MSP34xx or TDA98xx */ + init_audio_devices( bktr ); + + return( 0 ); +} + + + + +/* + * + */ +int +video_close( bktr_ptr_t bktr ) +{ + bktr->flags &= ~(METEOR_OPEN | + METEOR_SINGLE | + METEOR_CAP_MASK | + METEOR_WANT_MASK); + + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED); + OUTB(bktr, BKTR_CAP_CTL, CAPTURE_OFF); + + bktr->dma_prog_loaded = FALSE; + OUTB(bktr, BKTR_TDEC, 0); + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + +/** FIXME: is 0xf magic, wouldn't 0x00 work ??? */ + OUTL(bktr, BKTR_SRESET, 0xf); + OUTL(bktr, BKTR_INT_STAT, ALL_INTS_CLEARED); + + return( 0 ); +} + + +/* + * tuner close handle, + * place holder for tuner specific operations on a close. + */ +int +tuner_close( bktr_ptr_t bktr ) +{ + bktr->tflags &= ~TUNER_OPEN; + + /* mute the audio by switching the mux */ + set_audio( bktr, AUDIO_MUTE ); + + /* disable drivers on the GPIO port that control the MUXes */ + OUTL(bktr, BKTR_GPIO_OUT_EN, INL(bktr, BKTR_GPIO_OUT_EN) & ~bktr->card.gpio_mux_bits); + + return( 0 ); +} + +int +vbi_close( bktr_ptr_t bktr ) +{ + + bktr->vbiflags &= ~VBI_OPEN; + + return( 0 ); +} + +/* + * + */ +int +video_read(bktr_ptr_t bktr, int unit, dev_t dev, struct uio *uio) +{ + int status; + int count; + + + if (bktr->bigbuf == 0) /* no frame buffer allocated (ioctl failed) */ + return( ENOMEM ); + + if (bktr->flags & METEOR_CAP_MASK) + return( EIO ); /* already capturing */ + + OUTB(bktr, BKTR_CAP_CTL, bktr->bktr_cap_ctl); + + + count = bktr->rows * bktr->cols * + pixfmt_table[ bktr->pixfmt ].public.Bpp; + + if ((int) uio->uio_iov->iov_len < count) + return( EINVAL ); + + bktr->flags &= ~(METEOR_CAP_MASK | METEOR_WANT_MASK); + + /* capture one frame */ + start_capture(bktr, METEOR_SINGLE); + /* wait for capture to complete */ + OUTL(bktr, BKTR_INT_STAT, ALL_INTS_CLEARED); + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_ENABLED); + OUTW(bktr, BKTR_GPIO_DMA_CTL, bktr->capcontrol); + OUTL(bktr, BKTR_INT_MASK, BT848_INT_MYSTERYBIT | + BT848_INT_RISCI | + BT848_INT_VSYNC | + BT848_INT_FMTCHG); + + + status = tsleep(BKTR_SLEEP, BKTRPRI, "captur", 0); + if (!status) /* successful capture */ + status = uiomove((caddr_t)bktr->bigbuf, count, uio); + else + printf ("%s: read: tsleep error %d\n", + bktr_name(bktr), status); + + bktr->flags &= ~(METEOR_SINGLE | METEOR_WANT_MASK); + + return( status ); +} + +/* + * Read VBI data from the vbi circular buffer + * The buffer holds vbi data blocks which are the same size + * vbiinsert is the position we will insert the next item into the buffer + * vbistart is the actual position in the buffer we want to read from + * vbisize is the exact number of bytes in the buffer left to read + */ +int +vbi_read(bktr_ptr_t bktr, struct uio *uio, int ioflag) +{ + int readsize, readsize2; + int status; + + + while(bktr->vbisize == 0) { + if (ioflag & IO_NDELAY) { + return EWOULDBLOCK; + } + + bktr->vbi_read_blocked = TRUE; + if ((status = tsleep(VBI_SLEEP, VBIPRI, "vbi", 0))) { + return status; + } + } + + /* Now we have some data to give to the user */ + + /* We cannot read more bytes than there are in + * the circular buffer + */ + readsize = (int)uio->uio_iov->iov_len; + + if (readsize > bktr->vbisize) readsize = bktr->vbisize; + + /* Check if we can read this number of bytes without having + * to wrap around the circular buffer */ + if((bktr->vbistart + readsize) >= VBI_BUFFER_SIZE) { + /* We need to wrap around */ + + readsize2 = VBI_BUFFER_SIZE - bktr->vbistart; + status = uiomove((caddr_t)bktr->vbibuffer + bktr->vbistart, readsize2, uio); + status += uiomove((caddr_t)bktr->vbibuffer, (readsize - readsize2), uio); + } else { + /* We do not need to wrap around */ + status = uiomove((caddr_t)bktr->vbibuffer + bktr->vbistart, readsize, uio); + } + + /* Update the number of bytes left to read */ + bktr->vbisize -= readsize; + + /* Update vbistart */ + bktr->vbistart += readsize; + bktr->vbistart = bktr->vbistart % VBI_BUFFER_SIZE; /* wrap around if needed */ + + return( status ); + +} + + + +/* + * video ioctls + */ +int +video_ioctl( bktr_ptr_t bktr, int unit, ioctl_cmd_t cmd, caddr_t arg, struct proc* pr ) +{ + volatile u_char c_temp; + unsigned int temp; + unsigned int temp_iform; + unsigned int error; + struct meteor_geomet *geo; + struct meteor_counts *counts; + struct meteor_video *video; + struct bktr_capture_area *cap_area; + vm_offset_t buf; + int i; + char char_temp; + + switch ( cmd ) { + + case BT848SCLIP: /* set clip region */ + bktr->max_clip_node = 0; + memcpy(&bktr->clip_list, arg, sizeof(bktr->clip_list)); + + for (i = 0; i < BT848_MAX_CLIP_NODE; i++) { + if (bktr->clip_list[i].y_min == 0 && + bktr->clip_list[i].y_max == 0) + break; + } + bktr->max_clip_node = i; + + /* make sure that the list contains a valid clip secquence */ + /* the clip rectangles should be sorted by x then by y as the + second order sort key */ + + /* clip rectangle list is terminated by y_min and y_max set to 0 */ + + /* to disable clipping set y_min and y_max to 0 in the first + clip rectangle . The first clip rectangle is clip_list[0]. + */ + + + + if (bktr->max_clip_node == 0 && + (bktr->clip_list[0].y_min != 0 && + bktr->clip_list[0].y_max != 0)) { + return EINVAL; + } + + for (i = 0; i < BT848_MAX_CLIP_NODE - 1 ; i++) { + if (bktr->clip_list[i].y_min == 0 && + bktr->clip_list[i].y_max == 0) { + break; + } + if ( bktr->clip_list[i+1].y_min != 0 && + bktr->clip_list[i+1].y_max != 0 && + bktr->clip_list[i].x_min > bktr->clip_list[i+1].x_min ) { + + bktr->max_clip_node = 0; + return (EINVAL); + + } + + if (bktr->clip_list[i].x_min >= bktr->clip_list[i].x_max || + bktr->clip_list[i].y_min >= bktr->clip_list[i].y_max || + bktr->clip_list[i].x_min < 0 || + bktr->clip_list[i].x_max < 0 || + bktr->clip_list[i].y_min < 0 || + bktr->clip_list[i].y_max < 0 ) { + bktr->max_clip_node = 0; + return (EINVAL); + } + } + + bktr->dma_prog_loaded = FALSE; + + break; + + case METEORSTATUS: /* get Bt848 status */ + c_temp = INB(bktr, BKTR_DSTATUS); + temp = 0; + if (!(c_temp & 0x40)) temp |= METEOR_STATUS_HCLK; + if (!(c_temp & 0x10)) temp |= METEOR_STATUS_FIDT; + *(u_short *)arg = temp; + break; + + case BT848SFMT: /* set input format */ + temp = *(unsigned long*)arg & BT848_IFORM_FORMAT; + temp_iform = INB(bktr, BKTR_IFORM); + temp_iform &= ~BT848_IFORM_FORMAT; + temp_iform &= ~BT848_IFORM_XTSEL; + OUTB(bktr, BKTR_IFORM, (temp_iform | temp | format_params[temp].iform_xtsel)); + switch( temp ) { + case BT848_IFORM_F_AUTO: + bktr->flags = (bktr->flags & ~METEOR_FORM_MASK) | + METEOR_AUTOMODE; + break; + + case BT848_IFORM_F_NTSCM: + case BT848_IFORM_F_NTSCJ: + bktr->flags = (bktr->flags & ~METEOR_FORM_MASK) | + METEOR_NTSC; + OUTB(bktr, BKTR_ADELAY, format_params[temp].adelay); + OUTB(bktr, BKTR_BDELAY, format_params[temp].bdelay); + bktr->format_params = temp; + break; + + case BT848_IFORM_F_PALBDGHI: + case BT848_IFORM_F_PALN: + case BT848_IFORM_F_SECAM: + case BT848_IFORM_F_RSVD: + case BT848_IFORM_F_PALM: + bktr->flags = (bktr->flags & ~METEOR_FORM_MASK) | + METEOR_PAL; + OUTB(bktr, BKTR_ADELAY, format_params[temp].adelay); + OUTB(bktr, BKTR_BDELAY, format_params[temp].bdelay); + bktr->format_params = temp; + break; + + } + bktr->dma_prog_loaded = FALSE; + break; + + case METEORSFMT: /* set input format */ + temp_iform = INB(bktr, BKTR_IFORM); + temp_iform &= ~BT848_IFORM_FORMAT; + temp_iform &= ~BT848_IFORM_XTSEL; + switch(*(unsigned long *)arg & METEOR_FORM_MASK ) { + case 0: /* default */ + case METEOR_FMT_NTSC: + bktr->flags = (bktr->flags & ~METEOR_FORM_MASK) | + METEOR_NTSC; + OUTB(bktr, BKTR_IFORM, temp_iform | BT848_IFORM_F_NTSCM | + format_params[BT848_IFORM_F_NTSCM].iform_xtsel); + OUTB(bktr, BKTR_ADELAY, format_params[BT848_IFORM_F_NTSCM].adelay); + OUTB(bktr, BKTR_BDELAY, format_params[BT848_IFORM_F_NTSCM].bdelay); + bktr->format_params = BT848_IFORM_F_NTSCM; + break; + + case METEOR_FMT_PAL: + bktr->flags = (bktr->flags & ~METEOR_FORM_MASK) | + METEOR_PAL; + OUTB(bktr, BKTR_IFORM, temp_iform | BT848_IFORM_F_PALBDGHI | + format_params[BT848_IFORM_F_PALBDGHI].iform_xtsel); + OUTB(bktr, BKTR_ADELAY, format_params[BT848_IFORM_F_PALBDGHI].adelay); + OUTB(bktr, BKTR_BDELAY, format_params[BT848_IFORM_F_PALBDGHI].bdelay); + bktr->format_params = BT848_IFORM_F_PALBDGHI; + break; + + case METEOR_FMT_AUTOMODE: + bktr->flags = (bktr->flags & ~METEOR_FORM_MASK) | + METEOR_AUTOMODE; + OUTB(bktr, BKTR_IFORM, temp_iform | BT848_IFORM_F_AUTO | + format_params[BT848_IFORM_F_AUTO].iform_xtsel); + break; + + default: + return( EINVAL ); + } + bktr->dma_prog_loaded = FALSE; + break; + + case METEORGFMT: /* get input format */ + *(u_long *)arg = bktr->flags & METEOR_FORM_MASK; + break; + + + case BT848GFMT: /* get input format */ + *(u_long *)arg = INB(bktr, BKTR_IFORM) & BT848_IFORM_FORMAT; + break; + + case METEORSCOUNT: /* (re)set error counts */ + counts = (struct meteor_counts *) arg; + bktr->fifo_errors = counts->fifo_errors; + bktr->dma_errors = counts->dma_errors; + bktr->frames_captured = counts->frames_captured; + bktr->even_fields_captured = counts->even_fields_captured; + bktr->odd_fields_captured = counts->odd_fields_captured; + break; + + case METEORGCOUNT: /* get error counts */ + counts = (struct meteor_counts *) arg; + counts->fifo_errors = bktr->fifo_errors; + counts->dma_errors = bktr->dma_errors; + counts->frames_captured = bktr->frames_captured; + counts->even_fields_captured = bktr->even_fields_captured; + counts->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 */ + OUTB(bktr, BKTR_HUE, (*(u_char *) arg) & 0xff); + break; + + case METEORGHUE: /* get hue */ + *(u_char *)arg = INB(bktr, BKTR_HUE); + break; + + case METEORSBRIG: /* set brightness */ + char_temp = ( *(u_char *)arg & 0xff) - 128; + OUTB(bktr, BKTR_BRIGHT, char_temp); + + break; + + case METEORGBRIG: /* get brightness */ + *(u_char *)arg = INB(bktr, BKTR_BRIGHT); + break; + + case METEORSCSAT: /* set chroma saturation */ + temp = (int)*(u_char *)arg; + + OUTB(bktr, BKTR_SAT_U_LO, (temp << 1) & 0xff); + OUTB(bktr, BKTR_SAT_V_LO, (temp << 1) & 0xff); + OUTB(bktr, BKTR_E_CONTROL, INB(bktr, BKTR_E_CONTROL) + & ~(BT848_E_CONTROL_SAT_U_MSB + | BT848_E_CONTROL_SAT_V_MSB)); + OUTB(bktr, BKTR_O_CONTROL, INB(bktr, BKTR_O_CONTROL) + & ~(BT848_O_CONTROL_SAT_U_MSB | + BT848_O_CONTROL_SAT_V_MSB)); + + if ( temp & BIT_SEVEN_HIGH ) { + OUTB(bktr, BKTR_E_CONTROL, INB(bktr, BKTR_E_CONTROL) + | (BT848_E_CONTROL_SAT_U_MSB + | BT848_E_CONTROL_SAT_V_MSB)); + OUTB(bktr, BKTR_O_CONTROL, INB(bktr, BKTR_O_CONTROL) + | (BT848_O_CONTROL_SAT_U_MSB + | BT848_O_CONTROL_SAT_V_MSB)); + } + break; + + case METEORGCSAT: /* get chroma saturation */ + temp = (INB(bktr, BKTR_SAT_V_LO) >> 1) & 0xff; + if ( INB(bktr, BKTR_E_CONTROL) & BT848_E_CONTROL_SAT_V_MSB ) + temp |= BIT_SEVEN_HIGH; + *(u_char *)arg = (u_char)temp; + break; + + case METEORSCONT: /* set contrast */ + temp = (int)*(u_char *)arg & 0xff; + temp <<= 1; + OUTB(bktr, BKTR_CONTRAST_LO, temp & 0xff); + OUTB(bktr, BKTR_E_CONTROL, INB(bktr, BKTR_E_CONTROL) & ~BT848_E_CONTROL_CON_MSB); + OUTB(bktr, BKTR_O_CONTROL, INB(bktr, BKTR_O_CONTROL) & ~BT848_O_CONTROL_CON_MSB); + OUTB(bktr, BKTR_E_CONTROL, INB(bktr, BKTR_E_CONTROL) | + (((temp & 0x100) >> 6 ) & BT848_E_CONTROL_CON_MSB)); + OUTB(bktr, BKTR_O_CONTROL, INB(bktr, BKTR_O_CONTROL) | + (((temp & 0x100) >> 6 ) & BT848_O_CONTROL_CON_MSB)); + break; + + case METEORGCONT: /* get contrast */ + temp = (int)INB(bktr, BKTR_CONTRAST_LO) & 0xff; + temp |= ((int)INB(bktr, BKTR_O_CONTROL) & 0x04) << 6; + *(u_char *)arg = (u_char)((temp >> 1) & 0xff); + break; + + case BT848SCBUF: /* set Clear-Buffer-on-start flag */ + bktr->clr_on_start = (*(int *)arg != 0); + break; + + case BT848GCBUF: /* get Clear-Buffer-on-start flag */ + *(int *)arg = (int) bktr->clr_on_start; + break; + + case METEORSSIGNAL: + if(*(int *)arg == 0 || *(int *)arg >= NSIG) { + return( EINVAL ); + break; + } + 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 ); + /* already capturing */ + if (temp & METEOR_CAP_MASK) + return( EIO ); + + + + start_capture(bktr, METEOR_SINGLE); + + /* wait for capture to complete */ + OUTL(bktr, BKTR_INT_STAT, ALL_INTS_CLEARED); + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_ENABLED); + OUTW(bktr, BKTR_GPIO_DMA_CTL, bktr->capcontrol); + + OUTL(bktr, BKTR_INT_MASK, BT848_INT_MYSTERYBIT | + BT848_INT_RISCI | + BT848_INT_VSYNC | + BT848_INT_FMTCHG); + + OUTB(bktr, BKTR_CAP_CTL, bktr->bktr_cap_ctl); + error = tsleep(BKTR_SLEEP, BKTRPRI, "captur", hz); + if (error && (error != ERESTART)) { + /* Here if we didn't get complete frame */ +#ifdef DIAGNOSTIC + printf( "%s: ioctl: tsleep error %d %x\n", + bktr_name(bktr), error, + INL(bktr, BKTR_RISC_COUNT)); +#endif + + /* stop dma */ + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + + /* disable risc, leave fifo running */ + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_ENABLED); + } + + bktr->flags &= ~(METEOR_SINGLE|METEOR_WANT_MASK); + /* FIXME: should we set bt848->int_stat ??? */ + break; + + case METEOR_CAP_CONTINOUS: + if (bktr->bigbuf==0) /* no frame buffer allocated */ + return( ENOMEM ); + /* already capturing */ + if (temp & METEOR_CAP_MASK) + return( EIO ); + + + start_capture(bktr, METEOR_CONTIN); + + /* Clear the interrypt status register */ + OUTL(bktr, BKTR_INT_STAT, INL(bktr, BKTR_INT_STAT)); + + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_ENABLED); + OUTW(bktr, BKTR_GPIO_DMA_CTL, bktr->capcontrol); + OUTB(bktr, BKTR_CAP_CTL, bktr->bktr_cap_ctl); + + OUTL(bktr, BKTR_INT_MASK, BT848_INT_MYSTERYBIT | + BT848_INT_RISCI | + BT848_INT_VSYNC | + BT848_INT_FMTCHG); +#ifdef BT848_DUMP + dump_bt848( bt848 ); +#endif + break; + + case METEOR_CAP_STOP_CONT: + if (bktr->flags & METEOR_CONTIN) { + /* turn off capture */ + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED); + OUTB(bktr, BKTR_CAP_CTL, CAPTURE_OFF); + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + bktr->flags &= + ~(METEOR_CONTIN | METEOR_WANT_MASK); + + } + } + break; + + case METEORSETGEO: + /* can't change parameters while capturing */ + if (bktr->flags & METEOR_CAP_MASK) + return( EBUSY ); + + + 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( "%s: ioctl: Geometry odd or even only.\n", + bktr_name(bktr)); + 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; + + if (geo->columns <= 0) { + printf( + "%s: ioctl: %d: columns must be greater than zero.\n", + bktr_name(bktr), geo->columns); + error = EINVAL; + } + else if ((geo->columns & 0x3fe) != geo->columns) { + printf( + "%s: ioctl: %d: columns too large or not even.\n", + bktr_name(bktr), geo->columns); + error = EINVAL; + } + + if (geo->rows <= 0) { + printf( + "%s: ioctl: %d: rows must be greater than zero.\n", + bktr_name(bktr), geo->rows); + error = EINVAL; + } + else if (((geo->rows & 0x7fe) != geo->rows) || + ((geo->oformat & METEOR_GEO_FIELD_MASK) && + ((geo->rows & 0x3fe) != geo->rows)) ) { + printf( + "%s: ioctl: %d: rows too large or not even.\n", + bktr_name(bktr), geo->rows); + error = EINVAL; + } + + if (geo->frames > 32) { + printf("%s: ioctl: too many frames.\n", + bktr_name(bktr)); + + error = EINVAL; + } + + if (error) + return( error ); + + bktr->dma_prog_loaded = FALSE; + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED); + + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + + 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) { + +/*****************************/ +/* *** OS Dependant code *** */ +/*****************************/ +#if defined(__NetBSD__) || defined(__OpenBSD__) + bus_dmamap_t dmamap; + + buf = get_bktr_mem(bktr, &dmamap, + temp * PAGE_SIZE); + if (buf != 0) { + free_bktr_mem(bktr, bktr->dm_mem, + bktr->bigbuf); + bktr->dm_mem = dmamap; + +#else + buf = get_bktr_mem(unit, temp*PAGE_SIZE); + if (buf != 0) { + kmem_free(kernel_map, bktr->bigbuf, + (bktr->alloc_pages * PAGE_SIZE)); +#endif + + bktr->bigbuf = buf; + bktr->alloc_pages = temp; + if (bootverbose) + printf( + "%s: ioctl: Allocating %d bytes\n", + bktr_name(bktr), temp*PAGE_SIZE); + } + else + error = ENOMEM; + } + } + + if (error) + return error; + + bktr->rows = geo->rows; + bktr->cols = geo->columns; + bktr->frames = geo->frames; + + /* Pixel format (if in meteor pixfmt compatibility mode) */ + if ( bktr->pixfmt_compat ) { + 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; + break; + case METEOR_GEO_RGB24: + bktr->format = METEOR_GEO_RGB24; + break; + case METEOR_GEO_YUV_422: + bktr->format = METEOR_GEO_YUV_422; + if (geo->oformat & METEOR_GEO_YUV_12) + bktr->format = METEOR_GEO_YUV_12; + break; + case METEOR_GEO_YUV_PACKED: + bktr->format = METEOR_GEO_YUV_PACKED; + break; + } + bktr->pixfmt = oformat_meteor_to_bt( bktr->format ); + } + + 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); + OUTL(bktr, BKTR_INT_STAT, INL(bktr, BKTR_INT_STAT)); + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_ENABLED); + OUTW(bktr, BKTR_GPIO_DMA_CTL, bktr->capcontrol); + OUTL(bktr, BKTR_INT_MASK, BT848_INT_MYSTERYBIT | + BT848_INT_VSYNC | + BT848_INT_FMTCHG); + } + } + break; + /* end of METEORSETGEO */ + + /* FIXME. The Capture Area currently has the following restrictions: + GENERAL + y_offset may need to be even in interlaced modes + RGB24 - Interlaced mode + x_size must be greater than or equal to 1.666*METEORSETGEO width (cols) + y_size must be greater than or equal to METEORSETGEO height (rows) + RGB24 - Even Only (or Odd Only) mode + x_size must be greater than or equal to 1.666*METEORSETGEO width (cols) + y_size must be greater than or equal to 2*METEORSETGEO height (rows) + YUV12 - Interlaced mode + x_size must be greater than or equal to METEORSETGEO width (cols) + y_size must be greater than or equal to METEORSETGEO height (rows) + YUV12 - Even Only (or Odd Only) mode + x_size must be greater than or equal to METEORSETGEO width (cols) + y_size must be greater than or equal to 2*METEORSETGEO height (rows) + */ + + case BT848_SCAPAREA: /* set capture area of each video frame */ + /* can't change parameters while capturing */ + if (bktr->flags & METEOR_CAP_MASK) + return( EBUSY ); + + cap_area = (struct bktr_capture_area *) arg; + bktr->capture_area_x_offset = cap_area->x_offset; + bktr->capture_area_y_offset = cap_area->y_offset; + bktr->capture_area_x_size = cap_area->x_size; + bktr->capture_area_y_size = cap_area->y_size; + bktr->capture_area_enabled = TRUE; + + bktr->dma_prog_loaded = FALSE; + break; + + case BT848_GCAPAREA: /* get capture area of each video frame */ + cap_area = (struct bktr_capture_area *) arg; + if (bktr->capture_area_enabled == FALSE) { + cap_area->x_offset = 0; + cap_area->y_offset = 0; + cap_area->x_size = format_params[ + bktr->format_params].scaled_hactive; + cap_area->y_size = format_params[ + bktr->format_params].vactive; + } else { + cap_area->x_offset = bktr->capture_area_x_offset; + cap_area->y_offset = bktr->capture_area_y_offset; + cap_area->x_size = bktr->capture_area_x_size; + cap_area->y_size = bktr->capture_area_y_size; + } + break; + + default: + return common_ioctl( bktr, cmd, arg ); + } + + return( 0 ); +} + +/* + * tuner ioctls + */ +int +tuner_ioctl( bktr_ptr_t bktr, int unit, ioctl_cmd_t cmd, caddr_t arg, struct proc* pr ) +{ + int tmp_int; + unsigned int temp, temp1; + int offset; + int count; + u_char *buf; + u_long par; + u_char write; + int i2c_addr; + int i2c_port; + u_long data; + + switch ( cmd ) { + + case REMOTE_GETKEY: + /* Read the last key pressed by the Remote Control */ + if (bktr->remote_control == 0) return (EINVAL); + remote_read(bktr, (struct bktr_remote *)arg); + break; + +#if defined( TUNER_AFC ) + case TVTUNER_SETAFC: + bktr->tuner.afc = (*(int *)arg != 0); + break; + + case TVTUNER_GETAFC: + *(int *)arg = bktr->tuner.afc; + /* XXX Perhaps use another bit to indicate AFC success? */ + break; +#endif /* TUNER_AFC */ + + case TVTUNER_SETCHNL: + temp_mute( bktr, TRUE ); + temp = tv_channel( bktr, (int)*(unsigned long *)arg ); + if ( temp < 0 ) { + temp_mute( bktr, FALSE ); + return( EINVAL ); + } + *(unsigned long *)arg = temp; + + /* after every channel change, we must restart the MSP34xx */ + /* audio chip to reselect NICAM STEREO or MONO audio */ + if ( bktr->card.msp3400c ) + msp_autodetect( bktr ); + + /* after every channel change, we must restart the DPL35xx */ + if ( bktr->card.dpl3518a ) + dpl_autodetect( bktr ); + + temp_mute( bktr, FALSE ); + 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 = get_tuner_status( bktr ); + *(unsigned long *)arg = temp & 0xff; + break; + + case TVTUNER_SETFREQ: + temp_mute( bktr, TRUE ); + temp = tv_freq( bktr, (int)*(unsigned long *)arg, TV_FREQUENCY); + temp_mute( bktr, FALSE ); + if ( temp < 0 ) { + temp_mute( bktr, FALSE ); + return( EINVAL ); + } + *(unsigned long *)arg = temp; + + /* after every channel change, we must restart the MSP34xx */ + /* audio chip to reselect NICAM STEREO or MONO audio */ + if ( bktr->card.msp3400c ) + msp_autodetect( bktr ); + + /* after every channel change, we must restart the DPL35xx */ + if ( bktr->card.dpl3518a ) + dpl_autodetect( bktr ); + + temp_mute( bktr, FALSE ); + break; + + case TVTUNER_GETFREQ: + *(unsigned long *)arg = bktr->tuner.frequency; + break; + + case TVTUNER_GETCHNLSET: + return tuner_getchnlset((struct bktr_chnlset *)arg); + + case BT848_SAUDIO: /* set audio channel */ + if ( set_audio( bktr, *(int*)arg ) < 0 ) + return( EIO ); + break; + + /* hue is a 2's compliment number, -90' to +89.3' in 0.7' steps */ + case BT848_SHUE: /* set hue */ + OUTB(bktr, BKTR_HUE, (u_char)(*(int*)arg & 0xff)); + break; + + case BT848_GHUE: /* get hue */ + *(int*)arg = (signed char)(INB(bktr, BKTR_HUE) & 0xff); + break; + + /* brightness is a 2's compliment #, -50 to +%49.6% in 0.39% steps */ + case BT848_SBRIG: /* set brightness */ + OUTB(bktr, BKTR_BRIGHT, (u_char)(*(int *)arg & 0xff)); + break; + + case BT848_GBRIG: /* get brightness */ + *(int *)arg = (signed char)(INB(bktr, BKTR_BRIGHT) & 0xff); + break; + + /* */ + case BT848_SCSAT: /* set chroma saturation */ + tmp_int = *(int*)arg; + + temp = INB(bktr, BKTR_E_CONTROL); + temp1 = INB(bktr, BKTR_O_CONTROL); + if ( tmp_int & BIT_EIGHT_HIGH ) { + temp |= (BT848_E_CONTROL_SAT_U_MSB | + BT848_E_CONTROL_SAT_V_MSB); + temp1 |= (BT848_O_CONTROL_SAT_U_MSB | + BT848_O_CONTROL_SAT_V_MSB); + } + else { + temp &= ~(BT848_E_CONTROL_SAT_U_MSB | + BT848_E_CONTROL_SAT_V_MSB); + temp1 &= ~(BT848_O_CONTROL_SAT_U_MSB | + BT848_O_CONTROL_SAT_V_MSB); + } + + OUTB(bktr, BKTR_SAT_U_LO, (u_char)(tmp_int & 0xff)); + OUTB(bktr, BKTR_SAT_V_LO, (u_char)(tmp_int & 0xff)); + OUTB(bktr, BKTR_E_CONTROL, temp); + OUTB(bktr, BKTR_O_CONTROL, temp1); + break; + + case BT848_GCSAT: /* get chroma saturation */ + tmp_int = (int)(INB(bktr, BKTR_SAT_V_LO) & 0xff); + if ( INB(bktr, BKTR_E_CONTROL) & BT848_E_CONTROL_SAT_V_MSB ) + tmp_int |= BIT_EIGHT_HIGH; + *(int*)arg = tmp_int; + break; + + /* */ + case BT848_SVSAT: /* set chroma V saturation */ + tmp_int = *(int*)arg; + + temp = INB(bktr, BKTR_E_CONTROL); + temp1 = INB(bktr, BKTR_O_CONTROL); + if ( tmp_int & BIT_EIGHT_HIGH) { + temp |= BT848_E_CONTROL_SAT_V_MSB; + temp1 |= BT848_O_CONTROL_SAT_V_MSB; + } + else { + temp &= ~BT848_E_CONTROL_SAT_V_MSB; + temp1 &= ~BT848_O_CONTROL_SAT_V_MSB; + } + + OUTB(bktr, BKTR_SAT_V_LO, (u_char)(tmp_int & 0xff)); + OUTB(bktr, BKTR_E_CONTROL, temp); + OUTB(bktr, BKTR_O_CONTROL, temp1); + break; + + case BT848_GVSAT: /* get chroma V saturation */ + tmp_int = (int)INB(bktr, BKTR_SAT_V_LO) & 0xff; + if ( INB(bktr, BKTR_E_CONTROL) & BT848_E_CONTROL_SAT_V_MSB ) + tmp_int |= BIT_EIGHT_HIGH; + *(int*)arg = tmp_int; + break; + + /* */ + case BT848_SUSAT: /* set chroma U saturation */ + tmp_int = *(int*)arg; + + temp = INB(bktr, BKTR_E_CONTROL); + temp1 = INB(bktr, BKTR_O_CONTROL); + if ( tmp_int & BIT_EIGHT_HIGH ) { + temp |= BT848_E_CONTROL_SAT_U_MSB; + temp1 |= BT848_O_CONTROL_SAT_U_MSB; + } + else { + temp &= ~BT848_E_CONTROL_SAT_U_MSB; + temp1 &= ~BT848_O_CONTROL_SAT_U_MSB; + } + + OUTB(bktr, BKTR_SAT_U_LO, (u_char)(tmp_int & 0xff)); + OUTB(bktr, BKTR_E_CONTROL, temp); + OUTB(bktr, BKTR_O_CONTROL, temp1); + break; + + case BT848_GUSAT: /* get chroma U saturation */ + tmp_int = (int)INB(bktr, BKTR_SAT_U_LO) & 0xff; + if ( INB(bktr, BKTR_E_CONTROL) & BT848_E_CONTROL_SAT_U_MSB ) + tmp_int |= BIT_EIGHT_HIGH; + *(int*)arg = tmp_int; + break; + +/* lr 970528 luma notch etc - 3 high bits of e_control/o_control */ + + case BT848_SLNOTCH: /* set luma notch */ + tmp_int = (*(int *)arg & 0x7) << 5 ; + OUTB(bktr, BKTR_E_CONTROL, INB(bktr, BKTR_E_CONTROL) & ~0xe0); + OUTB(bktr, BKTR_O_CONTROL, INB(bktr, BKTR_O_CONTROL) & ~0xe0); + OUTB(bktr, BKTR_E_CONTROL, INB(bktr, BKTR_E_CONTROL) | tmp_int); + OUTB(bktr, BKTR_O_CONTROL, INB(bktr, BKTR_O_CONTROL) | tmp_int); + break; + + case BT848_GLNOTCH: /* get luma notch */ + *(int *)arg = (int) ( (INB(bktr, BKTR_E_CONTROL) & 0xe0) >> 5) ; + break; + + + /* */ + case BT848_SCONT: /* set contrast */ + tmp_int = *(int*)arg; + + temp = INB(bktr, BKTR_E_CONTROL); + temp1 = INB(bktr, BKTR_O_CONTROL); + if ( tmp_int & BIT_EIGHT_HIGH ) { + temp |= BT848_E_CONTROL_CON_MSB; + temp1 |= BT848_O_CONTROL_CON_MSB; + } + else { + temp &= ~BT848_E_CONTROL_CON_MSB; + temp1 &= ~BT848_O_CONTROL_CON_MSB; + } + + OUTB(bktr, BKTR_CONTRAST_LO, (u_char)(tmp_int & 0xff)); + OUTB(bktr, BKTR_E_CONTROL, temp); + OUTB(bktr, BKTR_O_CONTROL, temp1); + break; + + case BT848_GCONT: /* get contrast */ + tmp_int = (int)INB(bktr, BKTR_CONTRAST_LO) & 0xff; + if ( INB(bktr, BKTR_E_CONTROL) & BT848_E_CONTROL_CON_MSB ) + tmp_int |= BIT_EIGHT_HIGH; + *(int*)arg = tmp_int; + break; + + /* FIXME: SCBARS and CCBARS require a valid int * */ + /* argument to succeed, but its not used; consider */ + /* using the arg to store the on/off state so */ + /* there's only one ioctl() needed to turn cbars on/off */ + case BT848_SCBARS: /* set colorbar output */ + OUTB(bktr, BKTR_COLOR_CTL, INB(bktr, BKTR_COLOR_CTL) | BT848_COLOR_CTL_COLOR_BARS); + break; + + case BT848_CCBARS: /* clear colorbar output */ + OUTB(bktr, BKTR_COLOR_CTL, INB(bktr, BKTR_COLOR_CTL) & ~(BT848_COLOR_CTL_COLOR_BARS)); + 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; + + case BT848_WEEPROM: /* write eeprom */ + offset = (((struct eeProm *)arg)->offset); + count = (((struct eeProm *)arg)->count); + buf = &(((struct eeProm *)arg)->bytes[ 0 ]); + if ( writeEEProm( bktr, offset, count, buf ) < 0 ) + return( EIO ); + break; + + case BT848_REEPROM: /* read eeprom */ + offset = (((struct eeProm *)arg)->offset); + count = (((struct eeProm *)arg)->count); + buf = &(((struct eeProm *)arg)->bytes[ 0 ]); + if ( readEEProm( bktr, offset, count, buf ) < 0 ) + return( EIO ); + break; + + case BT848_SIGNATURE: + offset = (((struct eeProm *)arg)->offset); + count = (((struct eeProm *)arg)->count); + buf = &(((struct eeProm *)arg)->bytes[ 0 ]); + if ( signCard( bktr, offset, count, buf ) < 0 ) + return( EIO ); + break; + + /* Ioctl's for direct gpio access */ +#ifdef BKTR_GPIO_ACCESS + case BT848_GPIO_GET_EN: + *(int*)arg = INL(bktr, BKTR_GPIO_OUT_EN); + break; + + case BT848_GPIO_SET_EN: + OUTL(bktr, BKTR_GPIO_OUT_EN, *(int*)arg); + break; + + case BT848_GPIO_GET_DATA: + *(int*)arg = INL(bktr, BKTR_GPIO_DATA); + break; + + case BT848_GPIO_SET_DATA: + OUTL(bktr, BKTR_GPIO_DATA, *(int*)arg); + break; +#endif /* BKTR_GPIO_ACCESS */ + + /* Ioctl's for running the tuner device in radio mode */ + + case RADIO_GETMODE: + *(unsigned char *)arg = bktr->tuner.radio_mode; + break; + + case RADIO_SETMODE: + bktr->tuner.radio_mode = *(unsigned char *)arg; + break; + + case RADIO_GETFREQ: + *(unsigned long *)arg = bktr->tuner.frequency; + break; + + case RADIO_SETFREQ: + /* The argument to this ioctl is NOT freq*16. It is + ** freq*100. + */ + + temp=(int)*(unsigned long *)arg; + +#ifdef BKTR_RADIO_DEBUG + printf("%s: arg=%d temp=%d\n", bktr_name(bktr), + (int)*(unsigned long *)arg, temp); +#endif + +#ifndef BKTR_RADIO_NOFREQCHECK + /* According to the spec. sheet the band: 87.5MHz-108MHz */ + /* is supported. */ + if(temp<8750 || temp>10800) { + printf("%s: Radio frequency out of range\n", bktr_name(bktr)); + return(EINVAL); + } +#endif + temp_mute( bktr, TRUE ); + temp = tv_freq( bktr, temp, FM_RADIO_FREQUENCY ); + temp_mute( bktr, FALSE ); +#ifdef BKTR_RADIO_DEBUG + if(temp) + printf("%s: tv_freq returned: %d\n", bktr_name(bktr), temp); +#endif + if ( temp < 0 ) + return( EINVAL ); + *(unsigned long *)arg = temp; + break; + + /* Luigi's I2CWR ioctl */ + case BT848_I2CWR: + par = *(u_long *)arg; + write = (par >> 24) & 0xff ; + i2c_addr = (par >> 16) & 0xff ; + i2c_port = (par >> 8) & 0xff ; + data = (par) & 0xff ; + + if (write) { + i2cWrite( bktr, i2c_addr, i2c_port, data); + } else { + data = i2cRead( bktr, i2c_addr); + } + *(u_long *)arg = (par & 0xffffff00) | ( data & 0xff ); + break; + + +#ifdef BT848_MSP_READ + /* I2C ioctls to allow userland access to the MSP chip */ + case BT848_MSP_READ: + { + struct bktr_msp_control *msp; + msp = (struct bktr_msp_control *) arg; + msp->data = msp_dpl_read(bktr, bktr->msp_addr, + msp->function, msp->address); + break; + } + + case BT848_MSP_WRITE: + { + struct bktr_msp_control *msp; + msp = (struct bktr_msp_control *) arg; + msp_dpl_write(bktr, bktr->msp_addr, msp->function, + msp->address, msp->data ); + break; + } + + case BT848_MSP_RESET: + msp_dpl_reset(bktr, bktr->msp_addr); + break; +#endif + + default: + return common_ioctl( bktr, cmd, arg ); + } + + return( 0 ); +} + + +/* + * common ioctls + */ +int +common_ioctl( bktr_ptr_t bktr, ioctl_cmd_t cmd, caddr_t arg ) +{ + int pixfmt; + unsigned int temp; + struct meteor_pixfmt *pf_pub; + + switch (cmd) { + + case METEORSINPUT: /* set input device */ + /*Bt848 has 3 MUX Inputs. Bt848A/849A/878/879 has 4 MUX Inputs*/ + /* On the original bt848 boards, */ + /* Tuner is MUX0, RCA is MUX1, S-Video is MUX2 */ + /* On the Hauppauge bt878 boards, */ + /* Tuner is MUX0, RCA is MUX3 */ + /* Unfortunatly Meteor driver codes DEV_RCA as DEV_0, so we */ + /* stick with this system in our Meteor Emulation */ + + switch(*(unsigned long *)arg & METEOR_DEV_MASK) { + + /* this is the RCA video input */ + case 0: /* default */ + case METEOR_INPUT_DEV0: + /* METEOR_INPUT_DEV_RCA: */ + bktr->flags = (bktr->flags & ~METEOR_DEV_MASK) + | METEOR_DEV0; + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) + & ~BT848_IFORM_MUXSEL); + + /* work around for new Hauppauge 878 cards */ + if ((bktr->card.card_id == CARD_HAUPPAUGE) && + (bktr->id==BROOKTREE_878 || + bktr->id==BROOKTREE_879) ) + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) | BT848_IFORM_M_MUX3); + else + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) | BT848_IFORM_M_MUX1); + + OUTB(bktr, BKTR_E_CONTROL, INB(bktr, BKTR_E_CONTROL) & ~BT848_E_CONTROL_COMP); + OUTB(bktr, BKTR_O_CONTROL, INB(bktr, BKTR_O_CONTROL) & ~BT848_O_CONTROL_COMP); + set_audio( bktr, AUDIO_EXTERN ); + break; + + /* this is the tuner input */ + case METEOR_INPUT_DEV1: + bktr->flags = (bktr->flags & ~METEOR_DEV_MASK) + | METEOR_DEV1; + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) & ~BT848_IFORM_MUXSEL); + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) | BT848_IFORM_M_MUX0); + OUTB(bktr, BKTR_E_CONTROL, INB(bktr, BKTR_E_CONTROL) & ~BT848_E_CONTROL_COMP); + OUTB(bktr, BKTR_O_CONTROL, INB(bktr, BKTR_O_CONTROL) & ~BT848_O_CONTROL_COMP); + set_audio( bktr, AUDIO_TUNER ); + break; + + /* this is the S-VHS input, but with a composite camera */ + case METEOR_INPUT_DEV2: + bktr->flags = (bktr->flags & ~METEOR_DEV_MASK) + | METEOR_DEV2; + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) & ~BT848_IFORM_MUXSEL); + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) | BT848_IFORM_M_MUX2); + OUTB(bktr, BKTR_E_CONTROL, INB(bktr, BKTR_E_CONTROL) & ~BT848_E_CONTROL_COMP); + OUTB(bktr, BKTR_O_CONTROL, INB(bktr, BKTR_E_CONTROL) & ~BT848_O_CONTROL_COMP); + set_audio( bktr, AUDIO_EXTERN ); + break; + + /* this is the S-VHS input */ + case METEOR_INPUT_DEV_SVIDEO: + bktr->flags = (bktr->flags & ~METEOR_DEV_MASK) + | METEOR_DEV_SVIDEO; + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) & ~BT848_IFORM_MUXSEL); + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) | BT848_IFORM_M_MUX2); + OUTB(bktr, BKTR_E_CONTROL, INB(bktr, BKTR_E_CONTROL) | BT848_E_CONTROL_COMP); + OUTB(bktr, BKTR_O_CONTROL, INB(bktr, BKTR_O_CONTROL) | BT848_O_CONTROL_COMP); + set_audio( bktr, AUDIO_EXTERN ); + break; + + case METEOR_INPUT_DEV3: + if ((bktr->id == BROOKTREE_848A) || + (bktr->id == BROOKTREE_849A) || + (bktr->id == BROOKTREE_878) || + (bktr->id == BROOKTREE_879) ) { + bktr->flags = (bktr->flags & ~METEOR_DEV_MASK) + | METEOR_DEV3; + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) & ~BT848_IFORM_MUXSEL); + + /* work around for new Hauppauge 878 cards */ + if ((bktr->card.card_id == CARD_HAUPPAUGE) && + (bktr->id==BROOKTREE_878 || + bktr->id==BROOKTREE_879) ) + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) | BT848_IFORM_M_MUX1); + else + OUTB(bktr, BKTR_IFORM, INB(bktr, BKTR_IFORM) | BT848_IFORM_M_MUX3); + + OUTB(bktr, BKTR_E_CONTROL, INB(bktr, BKTR_E_CONTROL) & ~BT848_E_CONTROL_COMP); + OUTB(bktr, BKTR_O_CONTROL, INB(bktr, BKTR_O_CONTROL) & ~BT848_O_CONTROL_COMP); + set_audio( bktr, AUDIO_EXTERN ); + + break; + } + + default: + return( EINVAL ); + } + break; + + case METEORGINPUT: /* get input device */ + *(u_long *)arg = bktr->flags & METEOR_DEV_MASK; + break; + + case METEORSACTPIXFMT: + if (( *(int *)arg < 0 ) || + ( *(int *)arg >= PIXFMT_TABLE_SIZE )) + return( EINVAL ); + + bktr->pixfmt = *(int *)arg; + OUTB(bktr, BKTR_COLOR_CTL, (INB(bktr, BKTR_COLOR_CTL) & 0xf0) + | pixfmt_swap_flags( bktr->pixfmt )); + bktr->pixfmt_compat = FALSE; + break; + + case METEORGACTPIXFMT: + *(int *)arg = bktr->pixfmt; + break; + + case METEORGSUPPIXFMT : + pf_pub = (struct meteor_pixfmt *)arg; + pixfmt = pf_pub->index; + + if (( pixfmt < 0 ) || ( pixfmt >= PIXFMT_TABLE_SIZE )) + return( EINVAL ); + + memcpy( pf_pub, &pixfmt_table[ pixfmt ].public, + sizeof( *pf_pub ) ); + + /* Patch in our format index */ + pf_pub->index = pixfmt; + break; + +#if defined( STATUS_SUM ) + case BT848_GSTATUS: /* reap status */ + { + DECLARE_INTR_MASK(s); + DISABLE_INTR(s); + temp = status_sum; + status_sum = 0; + ENABLE_INTR(s); + *(u_int*)arg = temp; + break; + } +#endif /* STATUS_SUM */ + + default: + return( ENOTTY ); + } + + return( 0 ); +} + + + + +/****************************************************************************** + * bt848 RISC programming routines: + */ + + +/* + * + */ +#ifdef BT848_DEBUG +static int +dump_bt848( bktr_ptr_t bktr ) +{ + 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("%s: Reg:value : \t%x:%x \t%x:%x \t %x:%x \t %x:%x\n", + bktr_name(bktr), + r[i], INL(bktr, r[i]), + r[i+1], INL(bktr, r[i+1]), + r[i+2], INL(bktr, r[i+2]), + r[i+3], INL(bktr, r[i+3]])); + } + + printf("%s: INT STAT %x \n", bktr_name(bktr), + INL(bktr, BKTR_INT_STAT)); + printf("%s: Reg INT_MASK %x \n", bktr_name(bktr), + INL(bktr, BKTR_INT_MASK)); + printf("%s: Reg GPIO_DMA_CTL %x \n", bktr_name(bktr), + INW(bktr, BKTR_GPIO_DMA_CTL)); + + return( 0 ); +} + +#endif + +/* + * build write instruction + */ +#define BKTR_FM1 0x6 /* packed data to follow */ +#define BKTR_FM3 0xe /* planar data to follow */ +#define BKTR_VRE 0x4 /* Marks the end of the even field */ +#define BKTR_VRO 0xC /* Marks the end of the odd field */ +#define BKTR_PXV 0x0 /* valid word (never used) */ +#define BKTR_EOL 0x1 /* last dword, 4 bytes */ +#define BKTR_SOL 0x2 /* first dword */ + +#define OP_WRITE (0x1 << 28) +#define OP_SKIP (0x2 << 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) /* first instr for scanline */ +#define OP_EOL (1 << 26) + +#define BKTR_RESYNC (1 << 15) +#define BKTR_GEN_IRQ (1 << 24) + +/* + * The RISC status bits can be set/cleared in the RISC programs + * and tested in the Interrupt Handler + */ +#define BKTR_SET_RISC_STATUS_BIT0 (1 << 16) +#define BKTR_SET_RISC_STATUS_BIT1 (1 << 17) +#define BKTR_SET_RISC_STATUS_BIT2 (1 << 18) +#define BKTR_SET_RISC_STATUS_BIT3 (1 << 19) + +#define BKTR_CLEAR_RISC_STATUS_BIT0 (1 << 20) +#define BKTR_CLEAR_RISC_STATUS_BIT1 (1 << 21) +#define BKTR_CLEAR_RISC_STATUS_BIT2 (1 << 22) +#define BKTR_CLEAR_RISC_STATUS_BIT3 (1 << 23) + +#define BKTR_TEST_RISC_STATUS_BIT0 (1 << 28) +#define BKTR_TEST_RISC_STATUS_BIT1 (1 << 29) +#define BKTR_TEST_RISC_STATUS_BIT2 (1 << 30) +#define BKTR_TEST_RISC_STATUS_BIT3 (1 << 31) + +bool_t notclipped (bktr_reg_t * bktr, int x, int width) { + int i; + bktr_clip_t * clip_node; + bktr->clip_start = -1; + bktr->last_y = 0; + bktr->y = 0; + bktr->y2 = width; + bktr->line_length = width; + bktr->yclip = -1; + bktr->yclip2 = -1; + bktr->current_col = 0; + + if (bktr->max_clip_node == 0 ) return TRUE; + clip_node = (bktr_clip_t *) &bktr->clip_list[0]; + + + for (i = 0; i < bktr->max_clip_node; i++ ) { + clip_node = (bktr_clip_t *) &bktr->clip_list[i]; + if (x >= clip_node->x_min && x <= clip_node->x_max ) { + bktr->clip_start = i; + return FALSE; + } + } + + return TRUE; +} + +bool_t getline(bktr_reg_t *bktr, int x ) { + int i, j; + bktr_clip_t * clip_node ; + + if (bktr->line_length == 0 || + bktr->current_col >= bktr->line_length) return FALSE; + + bktr->y = min(bktr->last_y, bktr->line_length); + bktr->y2 = bktr->line_length; + + bktr->yclip = bktr->yclip2 = -1; + for (i = bktr->clip_start; i < bktr->max_clip_node; i++ ) { + clip_node = (bktr_clip_t *) &bktr->clip_list[i]; + if (x >= clip_node->x_min && x <= clip_node->x_max) { + if (bktr->last_y <= clip_node->y_min) { + bktr->y = min(bktr->last_y, bktr->line_length); + bktr->y2 = min(clip_node->y_min, bktr->line_length); + bktr->yclip = min(clip_node->y_min, bktr->line_length); + bktr->yclip2 = min(clip_node->y_max, bktr->line_length); + bktr->last_y = bktr->yclip2; + bktr->clip_start = i; + + for (j = i+1; j < bktr->max_clip_node; j++ ) { + clip_node = (bktr_clip_t *) &bktr->clip_list[j]; + if (x >= clip_node->x_min && x <= clip_node->x_max) { + if (bktr->last_y >= clip_node->y_min) { + bktr->yclip2 = min(clip_node->y_max, bktr->line_length); + bktr->last_y = bktr->yclip2; + bktr->clip_start = j; + } + } else break ; + } + return TRUE; + } + } + } + + if (bktr->current_col <= bktr->line_length) { + bktr->current_col = bktr->line_length; + return TRUE; + } + return FALSE; +} + +static bool_t split(bktr_reg_t * bktr, volatile u_long **dma_prog, int width , + u_long operation, int pixel_width, + volatile u_char ** target_buffer, int cols ) { + + u_long flag, flag2; + struct meteor_pixfmt *pf = &pixfmt_table[ bktr->pixfmt ].public; + u_int skip, start_skip; + + /* For RGB24, we need to align the component in FIFO Byte Lane 0 */ + /* to the 1st byte in the mem dword containing our start addr. */ + /* BTW, we know this pixfmt's 1st byte is Blue; thus the start addr */ + /* must be Blue. */ + start_skip = 0; + if (( pf->type == METEOR_PIXTYPE_RGB ) && ( pf->Bpp == 3 )) + switch ( ((uintptr_t) (volatile void *) *target_buffer) % 4 ) { + case 2 : start_skip = 4 ; break; + case 1 : start_skip = 8 ; break; + } + + if ((width * pixel_width) < DMA_BT848_SPLIT ) { + if ( width == cols) { + flag = OP_SOL | OP_EOL; + } else if (bktr->current_col == 0 ) { + flag = OP_SOL; + } else if (bktr->current_col == cols) { + flag = OP_EOL; + } else flag = 0; + + skip = 0; + if (( flag & OP_SOL ) && ( start_skip > 0 )) { + *(*dma_prog)++ = OP_SKIP | OP_SOL | start_skip; + flag &= ~OP_SOL; + skip = start_skip; + } + + *(*dma_prog)++ = operation | flag | (width * pixel_width - skip); + if (operation != OP_SKIP ) + *(*dma_prog)++ = (uintptr_t) (volatile void *) *target_buffer; + + *target_buffer += width * pixel_width; + bktr->current_col += width; + + } else { + + if (bktr->current_col == 0 && width == cols) { + flag = OP_SOL ; + flag2 = OP_EOL; + } else if (bktr->current_col == 0 ) { + flag = OP_SOL; + flag2 = 0; + } else if (bktr->current_col >= cols) { + flag = 0; + flag2 = OP_EOL; + } else { + flag = 0; + flag2 = 0; + } + + skip = 0; + if (( flag & OP_SOL ) && ( start_skip > 0 )) { + *(*dma_prog)++ = OP_SKIP | OP_SOL | start_skip; + flag &= ~OP_SOL; + skip = start_skip; + } + + *(*dma_prog)++ = operation | flag | + (width * pixel_width / 2 - skip); + if (operation != OP_SKIP ) + *(*dma_prog)++ = (uintptr_t) (volatile void *) *target_buffer ; + *target_buffer += (width * pixel_width / 2) ; + + if ( operation == OP_WRITE ) + operation = OP_WRITEC; + *(*dma_prog)++ = operation | flag2 | + (width * pixel_width / 2); + *target_buffer += (width * pixel_width / 2) ; + bktr->current_col += width; + + } + return TRUE; +} + + +/* + * Generate the RISC instructions to capture both VBI and video images + */ +static void +rgb_vbi_prog( bktr_ptr_t bktr, char i_flag, int cols, int rows, int interlace ) +{ + int i; + volatile u_long target_buffer, buffer, target,width; + volatile u_long pitch; + volatile u_long *dma_prog; /* DMA prog is an array of + 32 bit RISC instructions */ + volatile u_long *loop_point; + struct meteor_pixfmt_internal *pf_int = &pixfmt_table[ bktr->pixfmt ]; + u_int Bpp = pf_int->public.Bpp; + unsigned int vbisamples; /* VBI samples per line */ + unsigned int vbilines; /* VBI lines per field */ + unsigned int num_dwords; /* DWORDS per line */ + + vbisamples = format_params[bktr->format_params].vbi_num_samples; + vbilines = format_params[bktr->format_params].vbi_num_lines; + num_dwords = vbisamples/4; + + OUTB(bktr, BKTR_COLOR_FMT, pf_int->color_fmt); + OUTB(bktr, BKTR_ADC, SYNC_LEVEL); + OUTB(bktr, BKTR_VBI_PACK_SIZE, ((num_dwords)) & 0xff); + OUTB(bktr, BKTR_VBI_PACK_DEL, ((num_dwords)>> 8) & 0x01); /* no hdelay */ + /* no ext frame */ + + OUTB(bktr, BKTR_OFORM, 0x00); + + OUTB(bktr, BKTR_E_VSCALE_HI, INB(bktr, BKTR_E_VSCALE_HI) | 0x40); /* set chroma comb */ + OUTB(bktr, BKTR_O_VSCALE_HI, INB(bktr, BKTR_O_VSCALE_HI) | 0x40); + OUTB(bktr, BKTR_E_VSCALE_HI, INB(bktr, BKTR_E_VSCALE_HI) & ~0x80); /* clear Ycomb */ + OUTB(bktr, BKTR_O_VSCALE_HI, INB(bktr, BKTR_O_VSCALE_HI) & ~0x80); + + /* disable gamma correction removal */ + OUTB(bktr, BKTR_COLOR_CTL, INB(bktr, BKTR_COLOR_CTL) | BT848_COLOR_CTL_GAMMA); + + if (cols > 385 ) { + OUTB(bktr, BKTR_E_VTC, 0); + OUTB(bktr, BKTR_O_VTC, 0); + } else { + OUTB(bktr, BKTR_E_VTC, 1); + OUTB(bktr, BKTR_O_VTC, 1); + } + bktr->capcontrol = 3 << 2 | 3; + + dma_prog = (u_long *) bktr->dma_prog; + + /* Construct Write */ + + 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*Bpp; + } + + buffer = target_buffer; + + /* Wait for the VRE sync marking the end of the Even and + * the start of the Odd field. Resync here. + */ + *dma_prog++ = OP_SYNC | BKTR_RESYNC |BKTR_VRE; + *dma_prog++ = 0; + + loop_point = dma_prog; + + /* store the VBI data */ + /* look for sync with packed data */ + *dma_prog++ = OP_SYNC | BKTR_FM1; + *dma_prog++ = 0; + for(i = 0; i < vbilines; i++) { + *dma_prog++ = OP_WRITE | OP_SOL | OP_EOL | vbisamples; + *dma_prog++ = (u_long) vtophys((caddr_t)bktr->vbidata + + (i * VBI_LINE_SIZE)); + } + + if ( (i_flag == 2/*Odd*/) || (i_flag==3) /*interlaced*/ ) { + /* store the Odd field video image */ + /* look for sync with packed data */ + *dma_prog++ = OP_SYNC | BKTR_FM1; + *dma_prog++ = 0; /* NULL WORD */ + width = cols; + for (i = 0; i < (rows/interlace); i++) { + target = target_buffer; + if ( notclipped(bktr, i, width)) { + split(bktr, (volatile u_long **) &dma_prog, + bktr->y2 - bktr->y, OP_WRITE, + Bpp, (volatile u_char **) &target, cols); + + } else { + while(getline(bktr, i)) { + if (bktr->y != bktr->y2 ) { + split(bktr, (volatile u_long **) &dma_prog, + bktr->y2 - bktr->y, OP_WRITE, + Bpp, (volatile u_char **) &target, cols); + } + if (bktr->yclip != bktr->yclip2 ) { + split(bktr,(volatile u_long **) &dma_prog, + bktr->yclip2 - bktr->yclip, + OP_SKIP, + Bpp, (volatile u_char **) &target, cols); + } + } + + } + + target_buffer += interlace * pitch; + + } + + } /* end if */ + + /* Grab the Even field */ + /* Look for the VRO, end of Odd field, marker */ + *dma_prog++ = OP_SYNC | BKTR_GEN_IRQ | BKTR_RESYNC | BKTR_VRO; + *dma_prog++ = 0; /* NULL WORD */ + + /* store the VBI data */ + /* look for sync with packed data */ + *dma_prog++ = OP_SYNC | BKTR_FM1; + *dma_prog++ = 0; + for(i = 0; i < vbilines; i++) { + *dma_prog++ = OP_WRITE | OP_SOL | OP_EOL | vbisamples; + *dma_prog++ = (u_long) vtophys((caddr_t)bktr->vbidata + + ((i+MAX_VBI_LINES) * VBI_LINE_SIZE)); + } + + /* store the video image */ + if (i_flag == 1) /*Even Only*/ + target_buffer = buffer; + if (i_flag == 3) /*interlaced*/ + target_buffer = buffer+pitch; + + + if ((i_flag == 1) /*Even Only*/ || (i_flag==3) /*interlaced*/) { + /* look for sync with packed data */ + *dma_prog++ = OP_SYNC | BKTR_FM1; + *dma_prog++ = 0; /* NULL WORD */ + width = cols; + for (i = 0; i < (rows/interlace); i++) { + target = target_buffer; + if ( notclipped(bktr, i, width)) { + split(bktr, (volatile u_long **) &dma_prog, + bktr->y2 - bktr->y, OP_WRITE, + Bpp, (volatile u_char **) &target, cols); + } else { + while(getline(bktr, i)) { + if (bktr->y != bktr->y2 ) { + split(bktr, (volatile u_long **) &dma_prog, + bktr->y2 - bktr->y, OP_WRITE, + Bpp, (volatile u_char **) &target, + cols); + } + if (bktr->yclip != bktr->yclip2 ) { + split(bktr, (volatile u_long **) &dma_prog, + bktr->yclip2 - bktr->yclip, OP_SKIP, + Bpp, (volatile u_char **) &target, cols); + } + + } + + } + + target_buffer += interlace * pitch; + + } + } + + /* Look for end of 'Even Field' */ + *dma_prog++ = OP_SYNC | BKTR_GEN_IRQ | BKTR_RESYNC | BKTR_VRE; + *dma_prog++ = 0; /* NULL WORD */ + + *dma_prog++ = OP_JUMP ; + *dma_prog++ = (u_long ) vtophys(loop_point) ; + *dma_prog++ = 0; /* NULL WORD */ + +} + + + + +static void +rgb_prog( bktr_ptr_t bktr, char i_flag, int cols, int rows, int interlace ) +{ + int i; + volatile u_long target_buffer, buffer, target,width; + volatile u_long pitch; + volatile u_long *dma_prog; + struct meteor_pixfmt_internal *pf_int = &pixfmt_table[ bktr->pixfmt ]; + u_int Bpp = pf_int->public.Bpp; + + OUTB(bktr, BKTR_COLOR_FMT, pf_int->color_fmt); + OUTB(bktr, BKTR_VBI_PACK_SIZE, 0); + OUTB(bktr, BKTR_VBI_PACK_DEL, 0); + OUTB(bktr, BKTR_ADC, SYNC_LEVEL); + + OUTB(bktr, BKTR_OFORM, 0x00); + + OUTB(bktr, BKTR_E_VSCALE_HI, INB(bktr, BKTR_E_VSCALE_HI) | 0x40); /* set chroma comb */ + OUTB(bktr, BKTR_O_VSCALE_HI, INB(bktr, BKTR_O_VSCALE_HI) | 0x40); + OUTB(bktr, BKTR_E_VSCALE_HI, INB(bktr, BKTR_E_VSCALE_HI) & ~0x80); /* clear Ycomb */ + OUTB(bktr, BKTR_O_VSCALE_HI, INB(bktr, BKTR_O_VSCALE_HI) & ~0x80); + + /* disable gamma correction removal */ + OUTB(bktr, BKTR_COLOR_CTL, INB(bktr, BKTR_COLOR_CTL) | BT848_COLOR_CTL_GAMMA); + + if (cols > 385 ) { + OUTB(bktr, BKTR_E_VTC, 0); + OUTB(bktr, BKTR_O_VTC, 0); + } else { + OUTB(bktr, BKTR_E_VTC, 1); + OUTB(bktr, BKTR_O_VTC, 1); + } + bktr->capcontrol = 3 << 2 | 3; + + dma_prog = (u_long *) bktr->dma_prog; + + /* Construct Write */ + + 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*Bpp; + } + + buffer = target_buffer; + + /* contruct sync : for video packet format */ + *dma_prog++ = OP_SYNC | BKTR_RESYNC | BKTR_FM1; + + /* sync, mode indicator packed data */ + *dma_prog++ = 0; /* NULL WORD */ + width = cols; + for (i = 0; i < (rows/interlace); i++) { + target = target_buffer; + if ( notclipped(bktr, i, width)) { + split(bktr, (volatile u_long **) &dma_prog, + bktr->y2 - bktr->y, OP_WRITE, + Bpp, (volatile u_char **) &target, cols); + + } else { + while(getline(bktr, i)) { + if (bktr->y != bktr->y2 ) { + split(bktr, (volatile u_long **) &dma_prog, + bktr->y2 - bktr->y, OP_WRITE, + Bpp, (volatile u_char **) &target, cols); + } + if (bktr->yclip != bktr->yclip2 ) { + split(bktr,(volatile u_long **) &dma_prog, + bktr->yclip2 - bktr->yclip, + OP_SKIP, + Bpp, (volatile u_char **) &target, cols); + } + } + + } + + target_buffer += interlace * pitch; + + } + + switch (i_flag) { + case 1: + /* sync vre */ + *dma_prog++ = OP_SYNC | BKTR_GEN_IRQ | BKTR_VRO; + *dma_prog++ = 0; /* NULL WORD */ + + *dma_prog++ = OP_JUMP; + *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); + return; + + case 2: + /* sync vro */ + *dma_prog++ = OP_SYNC | BKTR_GEN_IRQ | BKTR_VRE; + *dma_prog++ = 0; /* NULL WORD */ + + *dma_prog++ = OP_JUMP; + *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); + return; + + case 3: + /* sync vro */ + *dma_prog++ = OP_SYNC | BKTR_GEN_IRQ | BKTR_RESYNC | BKTR_VRO; + *dma_prog++ = 0; /* NULL WORD */ + *dma_prog++ = OP_JUMP; ; + *dma_prog = (u_long ) vtophys(bktr->odd_dma_prog); + break; + } + + if (interlace == 2) { + + target_buffer = buffer + pitch; + + dma_prog = (u_long *) bktr->odd_dma_prog; + + /* sync vre IRQ bit */ + *dma_prog++ = OP_SYNC | BKTR_RESYNC | BKTR_FM1; + *dma_prog++ = 0; /* NULL WORD */ + width = cols; + for (i = 0; i < (rows/interlace); i++) { + target = target_buffer; + if ( notclipped(bktr, i, width)) { + split(bktr, (volatile u_long **) &dma_prog, + bktr->y2 - bktr->y, OP_WRITE, + Bpp, (volatile u_char **) &target, cols); + } else { + while(getline(bktr, i)) { + if (bktr->y != bktr->y2 ) { + split(bktr, (volatile u_long **) &dma_prog, + bktr->y2 - bktr->y, OP_WRITE, + Bpp, (volatile u_char **) &target, + cols); + } + if (bktr->yclip != bktr->yclip2 ) { + split(bktr, (volatile u_long **) &dma_prog, + bktr->yclip2 - bktr->yclip, OP_SKIP, + Bpp, (volatile u_char **) &target, cols); + } + + } + + } + + target_buffer += interlace * pitch; + + } + } + + /* sync vre IRQ bit */ + *dma_prog++ = OP_SYNC | BKTR_GEN_IRQ | BKTR_RESYNC | BKTR_VRE; + *dma_prog++ = 0; /* NULL WORD */ + *dma_prog++ = OP_JUMP ; + *dma_prog++ = (u_long ) vtophys(bktr->dma_prog) ; + *dma_prog++ = 0; /* NULL WORD */ +} + + +/* + * + */ +static void +yuvpack_prog( bktr_ptr_t bktr, char i_flag, + int cols, int rows, int interlace ) +{ + int i; + volatile unsigned int inst; + volatile unsigned int inst3; + volatile u_long target_buffer, buffer; + volatile u_long *dma_prog; + struct meteor_pixfmt_internal *pf_int = &pixfmt_table[ bktr->pixfmt ]; + int b; + + OUTB(bktr, BKTR_COLOR_FMT, pf_int->color_fmt); + + OUTB(bktr, BKTR_E_SCLOOP, INB(bktr, BKTR_E_SCLOOP) | BT848_E_SCLOOP_CAGC); /* enable chroma comb */ + OUTB(bktr, BKTR_O_SCLOOP, INB(bktr, BKTR_O_SCLOOP) | BT848_O_SCLOOP_CAGC); + + OUTB(bktr, BKTR_COLOR_CTL, INB(bktr, BKTR_COLOR_CTL) | BT848_COLOR_CTL_RGB_DED | BT848_COLOR_CTL_GAMMA); + OUTB(bktr, BKTR_ADC, SYNC_LEVEL); + + bktr->capcontrol = 1 << 6 | 1 << 4 | 1 << 2 | 3; + bktr->capcontrol = 3 << 2 | 3; + + dma_prog = (u_long *) bktr->dma_prog; + + /* Construct Write */ + + /* write , sol, eol */ + inst = OP_WRITE | OP_SOL | (cols); + /* write , sol, eol */ + inst3 = OP_WRITE | OP_EOL | (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 << 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 vro */ + *dma_prog++ = OP_SYNC | 1 << 24 | BKTR_VRO; + *dma_prog++ = 0; /* NULL WORD */ + *dma_prog++ = OP_JUMP; + *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); + return; + + case 3: + /* sync vro */ + *dma_prog++ = OP_SYNC | 1 << 24 | 1 << 15 | BKTR_VRO; + *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 | 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 vro IRQ bit */ + *dma_prog++ = OP_SYNC | 1 << 24 | 1 << 15 | BKTR_VRE; + *dma_prog++ = 0; /* NULL WORD */ + *dma_prog++ = OP_JUMP ; + *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_ptr_t bktr, char i_flag, + int cols, int rows, int interlace ){ + + int i; + volatile unsigned int inst; + volatile u_long target_buffer, t1, buffer; + volatile u_long *dma_prog; + struct meteor_pixfmt_internal *pf_int = &pixfmt_table[ bktr->pixfmt ]; + + OUTB(bktr, BKTR_COLOR_FMT, pf_int->color_fmt); + + dma_prog = (u_long *) bktr->dma_prog; + + bktr->capcontrol = 1 << 6 | 1 << 4 | 3; + + OUTB(bktr, BKTR_ADC, SYNC_LEVEL); + OUTB(bktr, BKTR_OFORM, 0x00); + + OUTB(bktr, BKTR_E_CONTROL, INB(bktr, BKTR_E_CONTROL) | BT848_E_CONTROL_LDEC); /* disable luma decimation */ + OUTB(bktr, BKTR_O_CONTROL, INB(bktr, BKTR_O_CONTROL) | BT848_O_CONTROL_LDEC); + + OUTB(bktr, BKTR_E_SCLOOP, INB(bktr, BKTR_E_SCLOOP) | BT848_E_SCLOOP_CAGC); /* chroma agc enable */ + OUTB(bktr, BKTR_O_SCLOOP, INB(bktr, BKTR_O_SCLOOP) | BT848_O_SCLOOP_CAGC); + + OUTB(bktr, BKTR_E_VSCALE_HI, INB(bktr, BKTR_E_VSCALE_HI) & ~0x80); /* clear Ycomb */ + OUTB(bktr, BKTR_O_VSCALE_HI, INB(bktr, BKTR_O_VSCALE_HI) & ~0x80); + OUTB(bktr, BKTR_E_VSCALE_HI, INB(bktr, BKTR_E_VSCALE_HI) | ~0x40); /* set chroma comb */ + OUTB(bktr, BKTR_O_VSCALE_HI, INB(bktr, BKTR_O_VSCALE_HI) | ~0x40); + + /* disable gamma correction removal */ + OUTB(bktr, BKTR_COLOR_CTL, INB(bktr, BKTR_COLOR_CTL) | BT848_COLOR_CTL_GAMMA); + + /* Construct Write */ + inst = OP_WRITE123 | OP_SOL | OP_EOL | (cols); + if (bktr->video.addr) + target_buffer = (u_long) bktr->video.addr; + else + target_buffer = (u_long) vtophys(bktr->bigbuf); + + buffer = target_buffer; + + t1 = buffer; + + /* contruct sync : for video packet format */ + *dma_prog++ = OP_SYNC | 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 | 1 << 24 | BKTR_VRE; /*sync vre*/ + *dma_prog++ = 0; /* NULL WORD */ + + *dma_prog++ = OP_JUMP ; + *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); + return; + + case 2: + *dma_prog++ = OP_SYNC | 1 << 24 | BKTR_VRO; /*sync vre*/ + *dma_prog++ = 0; /* NULL WORD */ + + *dma_prog++ = OP_JUMP; + *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); + return; + + case 3: + *dma_prog++ = OP_SYNC | 1 << 24 | 1 << 15 | BKTR_VRO; + *dma_prog++ = 0; /* NULL WORD */ + + *dma_prog++ = OP_JUMP ; + *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 = buffer + cols/2; + *dma_prog++ = OP_SYNC | 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 | 1 << 24 | 1 << 15 | BKTR_VRE; + *dma_prog++ = 0; /* NULL WORD */ + *dma_prog++ = OP_JUMP ; + *dma_prog++ = (u_long ) vtophys(bktr->dma_prog) ; + *dma_prog++ = 0; /* NULL WORD */ +} + + +/* + * + */ +static void +yuv12_prog( bktr_ptr_t bktr, char i_flag, + int cols, int rows, int interlace ){ + + int i; + volatile unsigned int inst; + volatile unsigned int inst1; + volatile u_long target_buffer, t1, buffer; + volatile u_long *dma_prog; + struct meteor_pixfmt_internal *pf_int = &pixfmt_table[ bktr->pixfmt ]; + + OUTB(bktr, BKTR_COLOR_FMT, pf_int->color_fmt); + + dma_prog = (u_long *) bktr->dma_prog; + + bktr->capcontrol = 1 << 6 | 1 << 4 | 3; + + OUTB(bktr, BKTR_ADC, SYNC_LEVEL); + OUTB(bktr, BKTR_OFORM, 0x0); + + /* Construct Write */ + inst = OP_WRITE123 | OP_SOL | OP_EOL | (cols); + inst1 = OP_WRITES123 | OP_SOL | OP_EOL | (cols); + if (bktr->video.addr) + target_buffer = (u_long) bktr->video.addr; + else + target_buffer = (u_long) vtophys(bktr->bigbuf); + + buffer = target_buffer; + t1 = buffer; + + *dma_prog++ = OP_SYNC | 1 << 15 | BKTR_FM3; /*sync, mode indicator packed data*/ + *dma_prog++ = 0; /* NULL WORD */ + + for (i = 0; i < (rows/interlace )/2 ; 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/4) + i*cols/2 * interlace; + target_buffer += interlace*cols; + *dma_prog++ = inst1; + *dma_prog++ = cols/2 | (cols/2 << 16); + *dma_prog++ = target_buffer; + target_buffer += interlace*cols; + + } + + switch (i_flag) { + case 1: + *dma_prog++ = OP_SYNC | 1 << 24 | BKTR_VRE; /*sync vre*/ + *dma_prog++ = 0; /* NULL WORD */ + + *dma_prog++ = OP_JUMP; + *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); + return; + + case 2: + *dma_prog++ = OP_SYNC | 1 << 24 | BKTR_VRO; /*sync vro*/ + *dma_prog++ = 0; /* NULL WORD */ + + *dma_prog++ = OP_JUMP; + *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); + return; + + case 3: + *dma_prog++ = OP_SYNC | 1 << 24 | 1 << 15 | BKTR_VRO; + *dma_prog++ = 0; /* NULL WORD */ + *dma_prog++ = OP_JUMP ; + *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 = buffer + cols/2; + *dma_prog++ = OP_SYNC | 1 << 15 | BKTR_FM3; + *dma_prog++ = 0; /* NULL WORD */ + + for (i = 0; i < ((rows/interlace )/2 ) ; 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/4) + i*cols/2 * interlace; + target_buffer += interlace*cols; + *dma_prog++ = inst1; + *dma_prog++ = cols/2 | (cols/2 << 16); + *dma_prog++ = target_buffer; + target_buffer += interlace*cols; + + } + + + } + + *dma_prog++ = OP_SYNC | 1 << 24 | 1 << 15 | BKTR_VRE; + *dma_prog++ = 0; /* NULL WORD */ + *dma_prog++ = OP_JUMP; + *dma_prog++ = (u_long ) vtophys(bktr->dma_prog); + *dma_prog++ = 0; /* NULL WORD */ +} + + + +/* + * + */ +static void +build_dma_prog( bktr_ptr_t bktr, char i_flag ) +{ + int rows, cols, interlace; + int tmp_int; + unsigned int temp; + struct format_params *fp; + struct meteor_pixfmt_internal *pf_int = &pixfmt_table[ bktr->pixfmt ]; + + + fp = &format_params[bktr->format_params]; + + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + + /* disable FIFO & RISC, leave other bits alone */ + OUTW(bktr, BKTR_GPIO_DMA_CTL, INW(bktr, BKTR_GPIO_DMA_CTL) & ~FIFO_RISC_ENABLED); + + /* set video parameters */ + if (bktr->capture_area_enabled) + temp = ((quad_t ) fp->htotal* (quad_t) bktr->capture_area_x_size * 4096 + / fp->scaled_htotal / bktr->cols) - 4096; + else + temp = ((quad_t ) fp->htotal* (quad_t) fp->scaled_hactive * 4096 + / fp->scaled_htotal / bktr->cols) - 4096; + + /* printf("%s: HSCALE value is %d\n", bktr_name(bktr), temp); */ + OUTB(bktr, BKTR_E_HSCALE_LO, temp & 0xff); + OUTB(bktr, BKTR_O_HSCALE_LO, temp & 0xff); + OUTB(bktr, BKTR_E_HSCALE_HI, (temp >> 8) & 0xff); + OUTB(bktr, BKTR_O_HSCALE_HI, (temp >> 8) & 0xff); + + /* horizontal active */ + temp = bktr->cols; + /* printf("%s: HACTIVE value is %d\n", bktr_name(bktr), temp); */ + OUTB(bktr, BKTR_E_HACTIVE_LO, temp & 0xff); + OUTB(bktr, BKTR_O_HACTIVE_LO, temp & 0xff); + OUTB(bktr, BKTR_E_CROP, INB(bktr, BKTR_E_CROP) & ~0x3); + OUTB(bktr, BKTR_O_CROP, INB(bktr, BKTR_O_CROP) & ~0x3); + OUTB(bktr, BKTR_E_CROP, INB(bktr, BKTR_E_CROP) | ((temp >> 8) & 0x3)); + OUTB(bktr, BKTR_O_CROP, INB(bktr, BKTR_O_CROP) | ((temp >> 8) & 0x3)); + + /* horizontal delay */ + if (bktr->capture_area_enabled) + temp = ( (fp->hdelay* fp->scaled_hactive + bktr->capture_area_x_offset* fp->scaled_htotal) + * bktr->cols) / (bktr->capture_area_x_size * fp->hactive); + else + temp = (fp->hdelay * bktr->cols) / fp->hactive; + + temp = temp & 0x3fe; + + /* printf("%s: HDELAY value is %d\n", bktr_name(bktr), temp); */ + OUTB(bktr, BKTR_E_DELAY_LO, temp & 0xff); + OUTB(bktr, BKTR_O_DELAY_LO, temp & 0xff); + OUTB(bktr, BKTR_E_CROP, INB(bktr, BKTR_E_CROP) & ~0xc); + OUTB(bktr, BKTR_O_CROP, INB(bktr, BKTR_O_CROP) & ~0xc); + OUTB(bktr, BKTR_E_CROP, INB(bktr, BKTR_E_CROP) | ((temp >> 6) & 0xc)); + OUTB(bktr, BKTR_O_CROP, INB(bktr, BKTR_O_CROP) | ((temp >> 6) & 0xc)); + + /* vertical scale */ + + if (bktr->capture_area_enabled) { + if (bktr->flags & METEOR_ONLY_ODD_FIELDS || + bktr->flags & METEOR_ONLY_EVEN_FIELDS) + tmp_int = 65536 - + (((bktr->capture_area_y_size * 256 + (bktr->rows/2)) / bktr->rows) - 512); + else { + tmp_int = 65536 - + (((bktr->capture_area_y_size * 512 + (bktr->rows / 2)) / bktr->rows) - 512); + } + } else { + if (bktr->flags & METEOR_ONLY_ODD_FIELDS || + bktr->flags & METEOR_ONLY_EVEN_FIELDS) + tmp_int = 65536 - + (((fp->vactive * 256 + (bktr->rows/2)) / bktr->rows) - 512); + else { + tmp_int = 65536 - + (((fp->vactive * 512 + (bktr->rows / 2)) / bktr->rows) - 512); + } + } + + tmp_int &= 0x1fff; + /* printf("%s: VSCALE value is %d\n", bktr_name(bktr), tmp_int); */ + OUTB(bktr, BKTR_E_VSCALE_LO, tmp_int & 0xff); + OUTB(bktr, BKTR_O_VSCALE_LO, tmp_int & 0xff); + OUTB(bktr, BKTR_E_VSCALE_HI, INB(bktr, BKTR_E_VSCALE_HI) & ~0x1f); + OUTB(bktr, BKTR_O_VSCALE_HI, INB(bktr, BKTR_O_VSCALE_HI) & ~0x1f); + OUTB(bktr, BKTR_E_VSCALE_HI, INB(bktr, BKTR_E_VSCALE_HI) | ((tmp_int >> 8) & 0x1f)); + OUTB(bktr, BKTR_O_VSCALE_HI, INB(bktr, BKTR_O_VSCALE_HI) | ((tmp_int >> 8) & 0x1f)); + + + /* vertical active */ + if (bktr->capture_area_enabled) + temp = bktr->capture_area_y_size; + else + temp = fp->vactive; + /* printf("%s: VACTIVE is %d\n", bktr_name(bktr), temp); */ + OUTB(bktr, BKTR_E_CROP, INB(bktr, BKTR_E_CROP) & ~0x30); + OUTB(bktr, BKTR_E_CROP, INB(bktr, BKTR_E_CROP) | ((temp >> 4) & 0x30)); + OUTB(bktr, BKTR_E_VACTIVE_LO, temp & 0xff); + OUTB(bktr, BKTR_O_CROP, INB(bktr, BKTR_O_CROP) & ~0x30); + OUTB(bktr, BKTR_O_CROP, INB(bktr, BKTR_O_CROP) | ((temp >> 4) & 0x30)); + OUTB(bktr, BKTR_O_VACTIVE_LO, temp & 0xff); + + /* vertical delay */ + if (bktr->capture_area_enabled) + temp = fp->vdelay + (bktr->capture_area_y_offset); + else + temp = fp->vdelay; + /* printf("%s: VDELAY is %d\n", bktr_name(bktr), temp); */ + OUTB(bktr, BKTR_E_CROP, INB(bktr, BKTR_E_CROP) & ~0xC0); + OUTB(bktr, BKTR_E_CROP, INB(bktr, BKTR_E_CROP) | ((temp >> 2) & 0xC0)); + OUTB(bktr, BKTR_E_VDELAY_LO, temp & 0xff); + OUTB(bktr, BKTR_O_CROP, INB(bktr, BKTR_O_CROP) & ~0xC0); + OUTB(bktr, BKTR_O_CROP, INB(bktr, BKTR_O_CROP) | ((temp >> 2) & 0xC0)); + OUTB(bktr, BKTR_O_VDELAY_LO, temp & 0xff); + + /* end of video params */ + + if ((bktr->xtal_pll_mode == BT848_USE_PLL) + && (fp->iform_xtsel==BT848_IFORM_X_XT1)) { + OUTB(bktr, BKTR_TGCTRL, BT848_TGCTRL_TGCKI_PLL); /* Select PLL mode */ + } else { + OUTB(bktr, BKTR_TGCTRL, BT848_TGCTRL_TGCKI_XTAL); /* Select Normal xtal 0/xtal 1 mode */ + } + + /* capture control */ + switch (i_flag) { + case 1: + bktr->bktr_cap_ctl = + (BT848_CAP_CTL_DITH_FRAME | BT848_CAP_CTL_EVEN); + OUTB(bktr, BKTR_E_VSCALE_HI, INB(bktr, BKTR_E_VSCALE_HI) & ~0x20); + OUTB(bktr, BKTR_O_VSCALE_HI, INB(bktr, BKTR_O_VSCALE_HI) & ~0x20); + interlace = 1; + break; + case 2: + bktr->bktr_cap_ctl = + (BT848_CAP_CTL_DITH_FRAME | BT848_CAP_CTL_ODD); + OUTB(bktr, BKTR_E_VSCALE_HI, INB(bktr, BKTR_E_VSCALE_HI) & ~0x20); + OUTB(bktr, BKTR_O_VSCALE_HI, INB(bktr, BKTR_O_VSCALE_HI) & ~0x20); + interlace = 1; + break; + default: + bktr->bktr_cap_ctl = + (BT848_CAP_CTL_DITH_FRAME | + BT848_CAP_CTL_EVEN | BT848_CAP_CTL_ODD); + OUTB(bktr, BKTR_E_VSCALE_HI, INB(bktr, BKTR_E_VSCALE_HI) | 0x20); + OUTB(bktr, BKTR_O_VSCALE_HI, INB(bktr, BKTR_O_VSCALE_HI) | 0x20); + interlace = 2; + break; + } + + OUTL(bktr, BKTR_RISC_STRT_ADD, vtophys(bktr->dma_prog)); + + rows = bktr->rows; + cols = bktr->cols; + + bktr->vbiflags &= ~VBI_CAPTURE; /* default - no vbi capture */ + + /* RGB Grabs. If /dev/vbi is already open, or we are a PAL/SECAM */ + /* user, then use the rgb_vbi RISC program. */ + /* Otherwise, use the normal rgb RISC program */ + if (pf_int->public.type == METEOR_PIXTYPE_RGB) { + if ( (bktr->vbiflags & VBI_OPEN) + ||(bktr->format_params == BT848_IFORM_F_PALBDGHI) + ||(bktr->format_params == BT848_IFORM_F_SECAM) + ){ + bktr->bktr_cap_ctl |= + BT848_CAP_CTL_VBI_EVEN | BT848_CAP_CTL_VBI_ODD; + bktr->vbiflags |= VBI_CAPTURE; + rgb_vbi_prog(bktr, i_flag, cols, rows, interlace); + return; + } else { + rgb_prog(bktr, i_flag, cols, rows, interlace); + return; + } + } + + if ( pf_int->public.type == METEOR_PIXTYPE_YUV ) { + yuv422_prog(bktr, i_flag, cols, rows, interlace); + OUTB(bktr, BKTR_COLOR_CTL, (INB(bktr, BKTR_COLOR_CTL) & 0xf0) + | pixfmt_swap_flags( bktr->pixfmt )); + return; + } + + if ( pf_int->public.type == METEOR_PIXTYPE_YUV_PACKED ) { + yuvpack_prog(bktr, i_flag, cols, rows, interlace); + OUTB(bktr, BKTR_COLOR_CTL, (INB(bktr, BKTR_COLOR_CTL) & 0xf0) + | pixfmt_swap_flags( bktr->pixfmt )); + return; + } + + if ( pf_int->public.type == METEOR_PIXTYPE_YUV_12 ) { + yuv12_prog(bktr, i_flag, cols, rows, interlace); + OUTB(bktr, BKTR_COLOR_CTL, (INB(bktr, BKTR_COLOR_CTL) & 0xf0) + | pixfmt_swap_flags( bktr->pixfmt )); + return; + } + return; +} + + +/****************************************************************************** + * video & video capture specific routines: + */ + + +/* + * + */ +static void +start_capture( bktr_ptr_t bktr, unsigned type ) +{ + u_char i_flag; + struct format_params *fp; + + fp = &format_params[bktr->format_params]; + + /* If requested, clear out capture buf first */ + if (bktr->clr_on_start && (bktr->video.addr == 0)) { + bzero((caddr_t)bktr->bigbuf, + (size_t)bktr->rows * bktr->cols * bktr->frames * + pixfmt_table[ bktr->pixfmt ].public.Bpp); + } + + OUTB(bktr, BKTR_DSTATUS, 0); + OUTL(bktr, BKTR_INT_STAT, INL(bktr, BKTR_INT_STAT)); + + bktr->flags |= type; + bktr->flags &= ~METEOR_WANT_MASK; + 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; + } + + /* TDEC is only valid for continuous captures */ + if ( type == METEOR_SINGLE ) { + u_short fps_save = bktr->fps; + + set_fps(bktr, fp->frame_rate); + bktr->fps = fps_save; + } + else + set_fps(bktr, bktr->fps); + + if (bktr->dma_prog_loaded == FALSE) { + build_dma_prog(bktr, i_flag); + bktr->dma_prog_loaded = TRUE; + } + + + OUTL(bktr, BKTR_RISC_STRT_ADD, vtophys(bktr->dma_prog)); + +} + + +/* + * + */ +static void +set_fps( bktr_ptr_t bktr, u_short fps ) +{ + struct format_params *fp; + int i_flag; + + fp = &format_params[bktr->format_params]; + + 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 = 1; + break; + default: + bktr->flags |= METEOR_WANT_MASK; + i_flag = 2; + break; + } + + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED); + OUTL(bktr, BKTR_INT_STAT, ALL_INTS_CLEARED); + + bktr->fps = fps; + OUTB(bktr, BKTR_TDEC, 0); + + if (fps < fp->frame_rate) + OUTB(bktr, BKTR_TDEC, i_flag*(fp->frame_rate - fps) & 0x3f); + else + OUTB(bktr, BKTR_TDEC, 0); + return; + +} + + + + + +/* + * Given a pixfmt index, compute the bt848 swap_flags necessary to + * achieve the specified swapping. + * Note that without bt swapping, 2Bpp and 3Bpp modes are written + * byte-swapped, and 4Bpp modes are byte and word swapped (see Table 6 + * and read R->L). + * Note also that for 3Bpp, we may additionally need to do some creative + * SKIPing to align the FIFO bytelines with the target buffer (see split()). + * This is abstracted here: e.g. no swaps = RGBA; byte & short swap = ABGR + * as one would expect. + */ + +static u_int pixfmt_swap_flags( int pixfmt ) +{ + struct meteor_pixfmt *pf = &pixfmt_table[ pixfmt ].public; + u_int swapf = 0; + + switch ( pf->Bpp ) { + case 2 : swapf = ( pf->swap_bytes ? 0 : BSWAP ); + break; + + case 3 : /* no swaps supported for 3bpp - makes no sense w/ bt848 */ + break; + + case 4 : if ( pf->swap_bytes ) + swapf = pf->swap_shorts ? 0 : WSWAP; + else + swapf = pf->swap_shorts ? BSWAP : (BSWAP | WSWAP); + break; + } + return swapf; +} + + + +/* + * Converts meteor-defined pixel formats (e.g. METEOR_GEO_RGB16) into + * our pixfmt_table indices. + */ + +static int oformat_meteor_to_bt( u_long format ) +{ + int i; + struct meteor_pixfmt *pf1, *pf2; + + /* Find format in compatibility table */ + for ( i = 0; i < METEOR_PIXFMT_TABLE_SIZE; i++ ) + if ( meteor_pixfmt_table[i].meteor_format == format ) + break; + + if ( i >= METEOR_PIXFMT_TABLE_SIZE ) + return -1; + pf1 = &meteor_pixfmt_table[i].public; + + /* Match it with an entry in master pixel format table */ + for ( i = 0; i < PIXFMT_TABLE_SIZE; i++ ) { + pf2 = &pixfmt_table[i].public; + + if (( pf1->type == pf2->type ) && + ( pf1->Bpp == pf2->Bpp ) && + !bcmp( pf1->masks, pf2->masks, sizeof( pf1->masks )) && + ( pf1->swap_bytes == pf2->swap_bytes ) && + ( pf1->swap_shorts == pf2->swap_shorts )) + break; + } + if ( i >= PIXFMT_TABLE_SIZE ) + return -1; + + return i; +} + +/****************************************************************************** + * i2c primitives: + */ + +/* */ +#define I2CBITTIME (0x5<<4) /* 5 * 0.48uS */ +#define I2CBITTIME_878 (1 << 7) +#define I2C_READ 0x01 +#define I2C_COMMAND (I2CBITTIME | \ + BT848_DATA_CTL_I2CSCL | \ + BT848_DATA_CTL_I2CSDA) + +#define I2C_COMMAND_878 (I2CBITTIME_878 | \ + BT848_DATA_CTL_I2CSCL | \ + BT848_DATA_CTL_I2CSDA) + +/* Select between old i2c code and new iicbus / smbus code */ +#if defined(BKTR_USE_FREEBSD_SMBUS) + +/* + * The hardware interface is actually SMB commands + */ +int +i2cWrite( bktr_ptr_t bktr, int addr, int byte1, int byte2 ) +{ + char cmd; + + if (bktr->id == BROOKTREE_848 || + bktr->id == BROOKTREE_848A || + bktr->id == BROOKTREE_849A) + cmd = I2C_COMMAND; + else + cmd = I2C_COMMAND_878; + + if (byte2 != -1) { + if (smbus_writew(bktr->i2c_sc.smbus, addr, cmd, + (short)(((byte2 & 0xff) << 8) | (byte1 & 0xff)))) + return (-1); + } else { + if (smbus_writeb(bktr->i2c_sc.smbus, addr, cmd, + (char)(byte1 & 0xff))) + return (-1); + } + + /* return OK */ + return( 0 ); +} + +int +i2cRead( bktr_ptr_t bktr, int addr ) +{ + char result; + char cmd; + + if (bktr->id == BROOKTREE_848 || + bktr->id == BROOKTREE_848A || + bktr->id == BROOKTREE_849A) + cmd = I2C_COMMAND; + else + cmd = I2C_COMMAND_878; + + if (smbus_readb(bktr->i2c_sc.smbus, addr, cmd, &result)) + return (-1); + + return ((int)((unsigned char)result)); +} + +#define IICBUS(bktr) ((bktr)->i2c_sc.iicbus) + +/* The MSP34xx and DPL35xx Audio chip require i2c bus writes of up */ +/* to 5 bytes which the bt848 automated i2c bus controller cannot handle */ +/* Therefore we need low level control of the i2c bus hardware */ + +/* Write to the MSP or DPL registers */ +void +msp_dpl_write(bktr_ptr_t bktr, int i2c_addr, unsigned char dev, unsigned int addr, unsigned int data) +{ + unsigned char addr_l, addr_h, data_h, data_l ; + + addr_h = (addr >>8) & 0xff; + addr_l = addr & 0xff; + data_h = (data >>8) & 0xff; + data_l = data & 0xff; + + iicbus_start(IICBUS(bktr), i2c_addr, 0 /* no timeout? */); + + iicbus_write_byte(IICBUS(bktr), dev, 0); + iicbus_write_byte(IICBUS(bktr), addr_h, 0); + iicbus_write_byte(IICBUS(bktr), addr_l, 0); + iicbus_write_byte(IICBUS(bktr), data_h, 0); + iicbus_write_byte(IICBUS(bktr), data_l, 0); + + iicbus_stop(IICBUS(bktr)); + + return; +} + +/* Read from the MSP or DPL registers */ +unsigned int +msp_dpl_read(bktr_ptr_t bktr, int i2c_addr, unsigned char dev, unsigned int addr) +{ + unsigned int data; + unsigned char addr_l, addr_h, dev_r; + int read; + u_char data_read[2]; + + addr_h = (addr >>8) & 0xff; + addr_l = addr & 0xff; + dev_r = dev+1; + + /* XXX errors ignored */ + iicbus_start(IICBUS(bktr), i2c_addr, 0 /* no timeout? */); + + iicbus_write_byte(IICBUS(bktr), dev_r, 0); + iicbus_write_byte(IICBUS(bktr), addr_h, 0); + iicbus_write_byte(IICBUS(bktr), addr_l, 0); + + iicbus_repeated_start(IICBUS(bktr), i2c_addr +1, 0 /* no timeout? */); + iicbus_read(IICBUS(bktr), data_read, 2, &read, IIC_LAST_READ, 0); + iicbus_stop(IICBUS(bktr)); + + data = (data_read[0]<<8) | data_read[1]; + + return (data); +} + +/* Reset the MSP or DPL chip */ +/* The user can block the reset (which is handy if you initialise the + * MSP and/or DPL audio in another operating system first (eg in Windows) + */ +void +msp_dpl_reset( bktr_ptr_t bktr, int i2c_addr ) +{ + +#ifndef BKTR_NO_MSP_RESET + /* put into reset mode */ + iicbus_start(IICBUS(bktr), i2c_addr, 0 /* no timeout? */); + iicbus_write_byte(IICBUS(bktr), 0x00, 0); + iicbus_write_byte(IICBUS(bktr), 0x80, 0); + iicbus_write_byte(IICBUS(bktr), 0x00, 0); + iicbus_stop(IICBUS(bktr)); + + /* put back to operational mode */ + iicbus_start(IICBUS(bktr), i2c_addr, 0 /* no timeout? */); + iicbus_write_byte(IICBUS(bktr), 0x00, 0); + iicbus_write_byte(IICBUS(bktr), 0x00, 0); + iicbus_write_byte(IICBUS(bktr), 0x00, 0); + iicbus_stop(IICBUS(bktr)); +#endif + return; +} + +static void remote_read(bktr_ptr_t bktr, struct bktr_remote *remote) { + int read; + + /* XXX errors ignored */ + iicbus_start(IICBUS(bktr), bktr->remote_control_addr, 0 /* no timeout? */); + iicbus_read(IICBUS(bktr), remote->data, 3, &read, IIC_LAST_READ, 0); + iicbus_stop(IICBUS(bktr)); + + return; +} + +#else /* defined(BKTR_USE_FREEBSD_SMBUS) */ + +/* + * Program the i2c bus directly + */ +int +i2cWrite( bktr_ptr_t bktr, int addr, int byte1, int byte2 ) +{ + u_long x; + u_long data; + + /* clear status bits */ + OUTL(bktr, BKTR_INT_STAT, BT848_INT_RACK | BT848_INT_I2CDONE); + + /* build the command datum */ + if (bktr->id == BROOKTREE_848 || + bktr->id == BROOKTREE_848A || + bktr->id == BROOKTREE_849A) { + data = ((addr & 0xff) << 24) | ((byte1 & 0xff) << 16) | I2C_COMMAND; + } else { + data = ((addr & 0xff) << 24) | ((byte1 & 0xff) << 16) | I2C_COMMAND_878; + } + if ( byte2 != -1 ) { + data |= ((byte2 & 0xff) << 8); + data |= BT848_DATA_CTL_I2CW3B; + } + + /* write the address and data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, data); + + /* wait for completion */ + for ( x = 0x7fffffff; x; --x ) { /* safety valve */ + if ( INL(bktr, BKTR_INT_STAT) & BT848_INT_I2CDONE ) + break; + } + + /* check for ACK */ + if ( !x || !(INL(bktr, BKTR_INT_STAT) & BT848_INT_RACK) ) + return( -1 ); + + /* return OK */ + return( 0 ); +} + + +/* + * + */ +int +i2cRead( bktr_ptr_t bktr, int addr ) +{ + u_long x; + + /* clear status bits */ + OUTL(bktr, BKTR_INT_STAT, BT848_INT_RACK | BT848_INT_I2CDONE); + + /* write the READ address */ + /* The Bt878 and Bt879 differed on the treatment of i2c commands */ + + if (bktr->id == BROOKTREE_848 || + bktr->id == BROOKTREE_848A || + bktr->id == BROOKTREE_849A) { + OUTL(bktr, BKTR_I2C_DATA_CTL, ((addr & 0xff) << 24) | I2C_COMMAND); + } else { + OUTL(bktr, BKTR_I2C_DATA_CTL, ((addr & 0xff) << 24) | I2C_COMMAND_878); + } + + /* wait for completion */ + for ( x = 0x7fffffff; x; --x ) { /* safety valve */ + if ( INL(bktr, BKTR_INT_STAT) & BT848_INT_I2CDONE ) + break; + } + + /* check for ACK */ + if ( !x || !(INL(bktr, BKTR_INT_STAT) & BT848_INT_RACK) ) + return( -1 ); + + /* it was a read */ + return( (INL(bktr, BKTR_I2C_DATA_CTL) >> 8) & 0xff ); +} + +/* The MSP34xx Audio chip require i2c bus writes of up to 5 bytes which the */ +/* bt848 automated i2c bus controller cannot handle */ +/* Therefore we need low level control of the i2c bus hardware */ +/* Idea for the following functions are from elsewhere in this driver and */ +/* from the Linux BTTV i2c driver by Gerd Knorr <kraxel@cs.tu-berlin.de> */ + +#define BITD 40 +static void i2c_start( bktr_ptr_t bktr) { + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); DELAY( BITD ); /* release data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 3); DELAY( BITD ); /* release clock */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 2); DELAY( BITD ); /* lower data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 0); DELAY( BITD ); /* lower clock */ +} + +static void i2c_stop( bktr_ptr_t bktr) { + OUTL(bktr, BKTR_I2C_DATA_CTL, 0); DELAY( BITD ); /* lower clock & data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 2); DELAY( BITD ); /* release clock */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 3); DELAY( BITD ); /* release data */ +} + +static int i2c_write_byte( bktr_ptr_t bktr, unsigned char data) { + int x; + int status; + + /* write out the byte */ + for ( x = 7; x >= 0; --x ) { + if ( data & (1<<x) ) { + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); + DELAY( BITD ); /* assert HI data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 3); + DELAY( BITD ); /* strobe clock */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); + DELAY( BITD ); /* release clock */ + } + else { + OUTL(bktr, BKTR_I2C_DATA_CTL, 0); + DELAY( BITD ); /* assert LO data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 2); + DELAY( BITD ); /* strobe clock */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 0); + DELAY( BITD ); /* release clock */ + } + } + + /* look for an ACK */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); DELAY( BITD ); /* float data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 3); DELAY( BITD ); /* strobe clock */ + status = INL(bktr, BKTR_I2C_DATA_CTL) & 1; /* read the ACK bit */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); DELAY( BITD ); /* release clock */ + + return( status ); +} + +static int i2c_read_byte( bktr_ptr_t bktr, unsigned char *data, int last ) { + int x; + int bit; + int byte = 0; + + /* read in the byte */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); + DELAY( BITD ); /* float data */ + for ( x = 7; x >= 0; --x ) { + OUTL(bktr, BKTR_I2C_DATA_CTL, 3); + DELAY( BITD ); /* strobe clock */ + bit = INL(bktr, BKTR_I2C_DATA_CTL) & 1; /* read the data bit */ + if ( bit ) byte |= (1<<x); + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); + DELAY( BITD ); /* release clock */ + } + /* After reading the byte, send an ACK */ + /* (unless that was the last byte, for which we send a NAK */ + if (last) { /* send NAK - same a writing a 1 */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); + DELAY( BITD ); /* set data bit */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 3); + DELAY( BITD ); /* strobe clock */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); + DELAY( BITD ); /* release clock */ + } else { /* send ACK - same as writing a 0 */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 0); + DELAY( BITD ); /* set data bit */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 2); + DELAY( BITD ); /* strobe clock */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 0); + DELAY( BITD ); /* release clock */ + } + + *data=byte; + return 0; +} +#undef BITD + +/* Write to the MSP or DPL registers */ +void msp_dpl_write( bktr_ptr_t bktr, int i2c_addr, unsigned char dev, unsigned int addr, + unsigned int data){ + unsigned int msp_w_addr = i2c_addr; + unsigned char addr_l, addr_h, data_h, data_l ; + addr_h = (addr >>8) & 0xff; + addr_l = addr & 0xff; + data_h = (data >>8) & 0xff; + data_l = data & 0xff; + + i2c_start(bktr); + i2c_write_byte(bktr, msp_w_addr); + i2c_write_byte(bktr, dev); + i2c_write_byte(bktr, addr_h); + i2c_write_byte(bktr, addr_l); + i2c_write_byte(bktr, data_h); + i2c_write_byte(bktr, data_l); + i2c_stop(bktr); +} + +/* Read from the MSP or DPL registers */ +unsigned int msp_dpl_read(bktr_ptr_t bktr, int i2c_addr, unsigned char dev, unsigned int addr){ + unsigned int data; + unsigned char addr_l, addr_h, data_1, data_2, dev_r ; + addr_h = (addr >>8) & 0xff; + addr_l = addr & 0xff; + dev_r = dev+1; + + i2c_start(bktr); + i2c_write_byte(bktr,i2c_addr); + i2c_write_byte(bktr,dev_r); + i2c_write_byte(bktr,addr_h); + i2c_write_byte(bktr,addr_l); + + i2c_start(bktr); + i2c_write_byte(bktr,i2c_addr+1); + i2c_read_byte(bktr,&data_1, 0); + i2c_read_byte(bktr,&data_2, 1); + i2c_stop(bktr); + data = (data_1<<8) | data_2; + return data; +} + +/* Reset the MSP or DPL chip */ +/* The user can block the reset (which is handy if you initialise the + * MSP audio in another operating system first (eg in Windows) + */ +void msp_dpl_reset( bktr_ptr_t bktr, int i2c_addr ) { + +#ifndef BKTR_NO_MSP_RESET + /* put into reset mode */ + i2c_start(bktr); + i2c_write_byte(bktr, i2c_addr); + i2c_write_byte(bktr, 0x00); + i2c_write_byte(bktr, 0x80); + i2c_write_byte(bktr, 0x00); + i2c_stop(bktr); + + /* put back to operational mode */ + i2c_start(bktr); + i2c_write_byte(bktr, i2c_addr); + i2c_write_byte(bktr, 0x00); + i2c_write_byte(bktr, 0x00); + i2c_write_byte(bktr, 0x00); + i2c_stop(bktr); +#endif + return; + +} + +static void remote_read(bktr_ptr_t bktr, struct bktr_remote *remote) { + + /* XXX errors ignored */ + i2c_start(bktr); + i2c_write_byte(bktr,bktr->remote_control_addr); + i2c_read_byte(bktr,&(remote->data[0]), 0); + i2c_read_byte(bktr,&(remote->data[1]), 0); + i2c_read_byte(bktr,&(remote->data[2]), 0); + i2c_stop(bktr); + + return; +} + +#endif /* defined(BKTR_USE_FREEBSD_SMBUS) */ + + +#if defined( I2C_SOFTWARE_PROBE ) + +/* + * we are keeping this around for any parts that we need to probe + * but that CANNOT be probed via an i2c read. + * this is necessary because the hardware i2c mechanism + * cannot be programmed for 1 byte writes. + * currently there are no known i2c parts that we need to probe + * and that cannot be safely read. + */ +static int i2cProbe( bktr_ptr_t bktr, int addr ); +#define BITD 40 +#define EXTRA_START + +/* + * probe for an I2C device at addr. + */ +static int +i2cProbe( bktr_ptr_t bktr, int addr ) +{ + int x, status; + + /* the START */ +#if defined( EXTRA_START ) + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); DELAY( BITD ); /* release data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 3); DELAY( BITD ); /* release clock */ +#endif /* EXTRA_START */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 2); DELAY( BITD ); /* lower data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 0); DELAY( BITD ); /* lower clock */ + + /* write addr */ + for ( x = 7; x >= 0; --x ) { + if ( addr & (1<<x) ) { + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); + DELAY( BITD ); /* assert HI data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 3); + DELAY( BITD ); /* strobe clock */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); + DELAY( BITD ); /* release clock */ + } + else { + OUTL(bktr, BKTR_I2C_DATA_CTL, 0); + DELAY( BITD ); /* assert LO data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 2); + DELAY( BITD ); /* strobe clock */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 0); + DELAY( BITD ); /* release clock */ + } + } + + /* look for an ACK */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); DELAY( BITD ); /* float data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 3); DELAY( BITD ); /* strobe clock */ + status = INL(bktr, BKTR_I2C_DATA_CTL) & 1; /* read the ACK bit */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 1); DELAY( BITD ); /* release clock */ + + /* the STOP */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 0); DELAY( BITD ); /* lower clock & data */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 2); DELAY( BITD ); /* release clock */ + OUTL(bktr, BKTR_I2C_DATA_CTL, 3); DELAY( BITD ); /* release data */ + + return( status ); +} +#undef EXTRA_START +#undef BITD + +#endif /* I2C_SOFTWARE_PROBE */ + + +#define ABSENT (-1) + +#endif /* FreeBSD, BSDI, NetBSD, OpenBSD */ diff --git a/sys/dev/pci/bktr/bktr_core.h b/sys/dev/pci/bktr/bktr_core.h new file mode 100644 index 00000000000..2fd3c5bf42c --- /dev/null +++ b/sys/dev/pci/bktr/bktr_core.h @@ -0,0 +1,97 @@ +/* $OpenBSD: bktr_core.h,v 1.1 2001/03/28 03:27:09 fgsch Exp $ */ +/* $FreeBSD: src/sys/dev/bktr/bktr_core.h,v 1.4 2000/06/26 09:41:32 roger Exp $ */ + +/* + * This is part of the Driver for Video Capture Cards (Frame grabbers) + * and TV Tuner cards using the Brooktree Bt848, Bt848A, Bt849A, Bt878, Bt879 + * chipset. + * Copyright Roger Hardiman and Amancio Hasty. + * + * bktr_core : This deals with the Bt848/849/878/879 PCI Frame Grabber, + * Handles all the open, close, ioctl and read userland calls. + * Sets the Bt848 registers and generates RISC pograms. + * Controls the i2c bus and GPIO interface. + * Contains the interface to the kernel. + * (eg probe/attach and open/close/ioctl) + * + */ + +/* + * 1. Redistributions of source code must retain the + * Copyright (c) 1997 Amancio Hasty, 1999 Roger Hardiman + * 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 and + * Roger Hardiman + * 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. + */ + + +int i2cWrite( bktr_ptr_t bktr, int addr, int byte1, int byte2 ); +int i2cRead( bktr_ptr_t bktr, int addr ); + +void msp_dpl_reset( bktr_ptr_t bktr, int i2d_addr ); +unsigned int msp_dpl_read( bktr_ptr_t bktr, int i2c_addr, unsigned char dev, unsigned int addr ); +void msp_dpl_write( bktr_ptr_t bktr, int i2c_addr, unsigned char dev, + unsigned int addr, unsigned int data ); + + +/* + * Defines for userland processes blocked in this driver + * For /dev/bktr[n] use memory address of bktr structure + * For /dev/vbi[n] use memory address of bktr structure + 1 + * this is ok as the bktr structure is > 1 byte + */ +#define BKTR_SLEEP ((caddr_t)bktr ) +#define VBI_SLEEP ((caddr_t)bktr + 1) + + +/* device name for printf */ +const char *bktr_name(bktr_ptr_t bktr); + +/* Prototypes for attatch and interrupt functions */ +void common_bktr_attach( bktr_ptr_t bktr, int unit, + u_long pci_id, u_int rev ); +int common_bktr_intr( void *arg ); + + +/* Prototypes for open, close, read, mmap and ioctl calls */ +int video_open( bktr_ptr_t bktr ); +int video_close( bktr_ptr_t bktr ); +int video_read( bktr_ptr_t bktr, int unit, dev_t dev, struct uio *uio ); +int video_ioctl( bktr_ptr_t bktr, int unit, + ioctl_cmd_t cmd, caddr_t arg, struct proc* pr ); + + +int tuner_open( bktr_ptr_t bktr ); +int tuner_close( bktr_ptr_t bktr ); +int tuner_ioctl( bktr_ptr_t bktr, int unit, + ioctl_cmd_t cmd, caddr_t arg, struct proc* pr ); + +int vbi_open( bktr_ptr_t bktr ); +int vbi_close( bktr_ptr_t bktr ); +int vbi_read( bktr_ptr_t bktr, struct uio *uio, int ioflag ); + diff --git a/sys/dev/pci/bktr/bktr_os.c b/sys/dev/pci/bktr/bktr_os.c new file mode 100644 index 00000000000..0a758773159 --- /dev/null +++ b/sys/dev/pci/bktr/bktr_os.c @@ -0,0 +1,1703 @@ +/* $OpenBSD: bktr_os.c,v 1.1 2001/03/28 03:27:09 fgsch Exp $ */ +/* $FreeBSD: src/sys/dev/bktr/bktr_os.c,v 1.20 2000/10/20 08:16:53 roger Exp $ */ + +/* + * This is part of the Driver for Video Capture Cards (Frame grabbers) + * and TV Tuner cards using the Brooktree Bt848, Bt848A, Bt849A, Bt878, Bt879 + * chipset. + * Copyright Roger Hardiman and Amancio Hasty. + * + * bktr_os : This has all the Operating System dependant code, + * probe/attach and open/close/ioctl/read/mmap + * memory allocation + * PCI bus interfacing + * + * + */ + +/* + * 1. Redistributions of source code must retain the + * Copyright (c) 1997 Amancio Hasty, 1999 Roger Hardiman + * 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 and + * Roger Hardiman + * 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. + */ + + +#ifdef __FreeBSD__ +#include "bktr.h" +#endif /* __FreeBSD__ */ + +#ifndef __OpenBSD__ +#include "opt_bktr.h" /* include any kernel config options */ +#endif + +#define FIFO_RISC_DISABLED 0 +#define ALL_INTS_DISABLED 0 + + +/*******************/ +/* *** FreeBSD *** */ +/*******************/ +#ifdef __FreeBSD__ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/uio.h> +#include <sys/kernel.h> +#include <sys/signalvar.h> +#include <sys/mman.h> +#include <sys/poll.h> +#include <sys/select.h> +#include <sys/vnode.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <vm/pmap.h> +#include <vm/vm_extern.h> + +#if (__FreeBSD_version >=400000) || (NSMBUS > 0) +#include <sys/bus.h> /* used by smbus and newbus */ +#endif + +#if (__FreeBSD_version >=300000) +#include <machine/bus_memio.h> /* used by bus space */ +#include <machine/bus.h> /* used by bus space and newbus */ +#include <sys/bus.h> +#endif + +#if (__FreeBSD_version >=400000) +#include <sys/rman.h> /* used by newbus */ +#include <machine/resource.h> /* used by newbus */ +#endif + +#if (__FreeBSD_version < 500000) +#include <machine/clock.h> /* for DELAY */ +#endif + +#include <pci/pcivar.h> +#include <pci/pcireg.h> + +#include <sys/sysctl.h> +int bt848_card = -1; +int bt848_tuner = -1; +int bt848_reverse_mute = -1; +int bt848_format = -1; +int bt848_slow_msp_audio = -1; + +SYSCTL_NODE(_hw, OID_AUTO, bt848, CTLFLAG_RW, 0, "Bt848 Driver mgmt"); +SYSCTL_INT(_hw_bt848, OID_AUTO, card, CTLFLAG_RW, &bt848_card, -1, ""); +SYSCTL_INT(_hw_bt848, OID_AUTO, tuner, CTLFLAG_RW, &bt848_tuner, -1, ""); +SYSCTL_INT(_hw_bt848, OID_AUTO, reverse_mute, CTLFLAG_RW, &bt848_reverse_mute, -1, ""); +SYSCTL_INT(_hw_bt848, OID_AUTO, format, CTLFLAG_RW, &bt848_format, -1, ""); +SYSCTL_INT(_hw_bt848, OID_AUTO, slow_msp_audio, CTLFLAG_RW, &bt848_slow_msp_audio, -1, ""); + +#if (__FreeBSD__ == 2) +#define PCIR_REVID PCI_CLASS_REG +#endif + +#endif /* end freebsd section */ + + + +/****************/ +/* *** BSDI *** */ +/****************/ +#ifdef __bsdi__ +#endif /* __bsdi__ */ + + +/**************************/ +/* *** OpenBSD/NetBSD *** */ +/**************************/ +#if defined(__NetBSD__) || defined(__OpenBSD__) + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/uio.h> +#include <sys/kernel.h> +#include <sys/signalvar.h> +#include <sys/mman.h> +#include <sys/poll.h> +#include <sys/select.h> +#include <sys/vnode.h> + +#include <vm/vm.h> + +#include <sys/device.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcidevs.h> + +#define BKTR_DEBUG +#ifdef BKTR_DEBUG +int bktr_debug = 0; +#define DPR(x) (bktr_debug ? printf x : 0) +#else +#define DPR(x) +#endif +#endif /* __NetBSD__ || __OpenBSD__ */ + + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <dev/ic/bt8xx.h> /* NetBSD location for .h files */ +#include <dev/pci/bktr/bktr_reg.h> +#include <dev/pci/bktr/bktr_tuner.h> +#include <dev/pci/bktr/bktr_card.h> +#include <dev/pci/bktr/bktr_audio.h> +#include <dev/pci/bktr/bktr_core.h> +#include <dev/pci/bktr/bktr_os.h> +#else /* Traditional location for .h files */ +#include <machine/ioctl_meteor.h> +#include <machine/ioctl_bt848.h> /* extensions to ioctl_meteor.h */ +#include <dev/bktr/bktr_reg.h> +#include <dev/bktr/bktr_tuner.h> +#include <dev/bktr/bktr_card.h> +#include <dev/bktr/bktr_audio.h> +#include <dev/bktr/bktr_core.h> +#include <dev/bktr/bktr_os.h> +#if defined(BKTR_USE_FREEBSD_SMBUS) +#include <dev/bktr/bktr_i2c.h> +#endif +#endif + + + +/****************************/ +/* *** FreeBSD 4.x code *** */ +/****************************/ +#if (__FreeBSD_version >= 400000) + +static int bktr_probe( device_t dev ); +static int bktr_attach( device_t dev ); +static int bktr_detach( device_t dev ); +static int bktr_shutdown( device_t dev ); +static void bktr_intr(void *arg) { common_bktr_intr(arg); } + +static device_method_t bktr_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, bktr_probe), + DEVMETHOD(device_attach, bktr_attach), + DEVMETHOD(device_detach, bktr_detach), + DEVMETHOD(device_shutdown, bktr_shutdown), + + { 0, 0 } +}; + +static driver_t bktr_driver = { + "bktr", + bktr_methods, + sizeof(struct bktr_softc), +}; + +static devclass_t bktr_devclass; + +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; +static d_poll_t bktr_poll; + +#define CDEV_MAJOR 92 +static struct cdevsw bktr_cdevsw = { + /* open */ bktr_open, + /* close */ bktr_close, + /* read */ bktr_read, + /* write */ bktr_write, + /* ioctl */ bktr_ioctl, + /* poll */ bktr_poll, + /* mmap */ bktr_mmap, + /* strategy */ nostrategy, + /* name */ "bktr", + /* maj */ CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, + /* bmaj */ -1 +}; + +DRIVER_MODULE(bktr, pci, bktr_driver, bktr_devclass, 0, 0); +#if (__FreeBSD_version > 410000) +MODULE_DEPEND(bktr, bktr_mem, 1,1,1); +MODULE_VERSION(bktr, 1); +#endif + + +/* + * the boot time probe routine. + */ +static int +bktr_probe( device_t dev ) +{ + unsigned int type = pci_get_devid(dev); + unsigned int rev = pci_get_revid(dev); + + if (PCI_VENDOR(type) == PCI_VENDOR_BROOKTREE) + { + switch (PCI_PRODUCT(type)) { + case PCI_PRODUCT_BROOKTREE_BT848: + if (rev == 0x12) + device_set_desc(dev, "BrookTree 848A"); + else + device_set_desc(dev, "BrookTree 848"); + return 0; + case PCI_PRODUCT_BROOKTREE_BT849: + device_set_desc(dev, "BrookTree 849A"); + return 0; + case PCI_PRODUCT_BROOKTREE_BT878: + device_set_desc(dev, "BrookTree 878"); + return 0; + case PCI_PRODUCT_BROOKTREE_BT879: + device_set_desc(dev, "BrookTree 879"); + return 0; + } + }; + + return ENXIO; +} + + +/* + * the attach routine. + */ +static int +bktr_attach( device_t dev ) +{ + u_long latency; + u_long fun; + u_long val; + unsigned int rev; + unsigned int unit; + int error = 0; +#ifdef BROOKTREE_IRQ + u_long old_irq, new_irq; +#endif + + struct bktr_softc *bktr = device_get_softc(dev); + + unit = device_get_unit(dev); + + /* build the device name for bktr_name() */ + snprintf(bktr->bktr_xname, sizeof(bktr->bktr_xname), "bktr%d",unit); + + /* + * Enable bus mastering and Memory Mapped device + */ + val = pci_read_config(dev, PCIR_COMMAND, 4); + val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, val, 4); + + /* + * Map control/status registers. + */ + bktr->mem_rid = PCIR_MAPS; + bktr->res_mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &bktr->mem_rid, + 0, ~0, 1, RF_ACTIVE); + + + if (!bktr->res_mem) { + device_printf(dev, "could not map memory\n"); + error = ENXIO; + goto fail; + } + bktr->memt = rman_get_bustag(bktr->res_mem); + bktr->memh = rman_get_bushandle(bktr->res_mem); + + + /* + * Disable the brooktree device + */ + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED); + + +#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 + + /* + * Allocate our interrupt. + */ + bktr->irq_rid = 0; + bktr->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &bktr->irq_rid, + 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); + if (bktr->res_irq == NULL) { + device_printf(dev, "could not map interrupt\n"); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, bktr->res_irq, INTR_TYPE_TTY, + bktr_intr, bktr, &bktr->res_ih); + if (error) { + device_printf(dev, "could not setup irq\n"); + goto fail; + + } + + + /* Update the Device Control Register */ + /* on Bt878 and Bt879 cards */ + fun = pci_read_config( dev, 0x40, 2); + fun = fun | 1; /* Enable writes to the sub-system vendor ID */ + +#if defined( BKTR_430_FX_MODE ) + if (bootverbose) printf("Using 430 FX chipset compatibilty mode\n"); + fun = fun | 2; /* Enable Intel 430 FX compatibility mode */ +#endif + +#if defined( BKTR_SIS_VIA_MODE ) + if (bootverbose) printf("Using SiS/VIA chipset compatibilty mode\n"); + fun = fun | 4; /* Enable SiS/VIA compatibility mode (usefull for + OPTi chipset motherboards too */ +#endif + pci_write_config(dev, 0x40, fun, 2); + + + /* XXX call bt848_i2c dependent attach() routine */ +#if defined(BKTR_USE_FREEBSD_SMBUS) + if (bt848_i2c_attach(unit, bktr, &bktr->i2c_sc)) + printf("bktr%d: i2c_attach: can't attach\n", unit); +#endif + + +/* + * PCI latency timer. 32 is a good value for 4 bus mastering slots, if + * you have more than four, then 16 would probably be a better value. + */ +#ifndef BROOKTREE_DEF_LATENCY_VALUE +#define BROOKTREE_DEF_LATENCY_VALUE 10 +#endif + latency = pci_read_config(dev, PCI_LATENCY_TIMER, 4); + 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_write_config(dev, PCI_LATENCY_TIMER, latency<<8, 4); + } + if ( bootverbose ) { + printf(" %d.\n", (int) latency); + } + + /* read the pci device id and revision id */ + fun = pci_get_devid(dev); + rev = pci_get_revid(dev); + + /* call the common attach code */ + common_bktr_attach( bktr, unit, fun, rev ); + + /* make the device entries */ + bktr->bktrdev = make_dev(&bktr_cdevsw, unit, + 0, 0, 0444, "bktr%d", unit); + bktr->tunerdev= make_dev(&bktr_cdevsw, unit+16, + 0, 0, 0444, "tuner%d", unit); + bktr->vbidev = make_dev(&bktr_cdevsw, unit+32, + 0, 0, 0444, "vbi%d" , unit); + + + /* if this is unit 0 (/dev/bktr0, /dev/tuner0, /dev/vbi0) then make */ + /* alias entries to /dev/bktr /dev/tuner and /dev/vbi */ +#if (__FreeBSD_version >=500000) + if (unit == 0) { + bktr->bktrdev_alias = make_dev_alias(bktr->bktrdev, "bktr"); + bktr->tunerdev_alias= make_dev_alias(bktr->tunerdev, "tuner"); + bktr->vbidev_alias = make_dev_alias(bktr->vbidev, "vbi"); + } +#endif + + return 0; + +fail: + if (bktr->res_irq) + bus_release_resource(dev, SYS_RES_IRQ, bktr->irq_rid, bktr->res_irq); + if (bktr->res_mem) + bus_release_resource(dev, SYS_RES_IRQ, bktr->mem_rid, bktr->res_mem); + return error; + +} + +/* + * the detach routine. + */ +static int +bktr_detach( device_t dev ) +{ + unsigned int unit; + + struct bktr_softc *bktr = device_get_softc(dev); + + unit = device_get_unit(dev); + + /* Disable the brooktree device */ + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED); + + /* Note: We do not free memory for RISC programs, grab buffer, vbi buffers */ + /* The memory is retained by the bktr_mem module so we can unload and */ + /* then reload the main bktr driver module */ + + /* Unregister the /dev/bktrN, tunerN and vbiN devices */ + destroy_dev(bktr->vbidev); + destroy_dev(bktr->tunerdev); + destroy_dev(bktr->bktrdev); + + /* If this is unit 0, then destroy the alias entries too */ +#if (__FreeBSD_version >=500000) + if (unit == 0) { + destroy_dev(bktr->vbidev_alias); + destroy_dev(bktr->tunerdev_alias); + destroy_dev(bktr->bktrdev_alias); + } +#endif + + /* + * Deallocate resources. + */ + bus_teardown_intr(dev, bktr->res_irq, bktr->res_ih); + bus_release_resource(dev, SYS_RES_IRQ, bktr->irq_rid, bktr->res_irq); + bus_release_resource(dev, SYS_RES_MEMORY, bktr->mem_rid, bktr->res_mem); + + return 0; +} + +/* + * the shutdown routine. + */ +static int +bktr_shutdown( device_t dev ) +{ + struct bktr_softc *bktr = device_get_softc(dev); + + /* Disable the brooktree device */ + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED); + + return 0; +} + + +/* + * Special Memory Allocation + */ +vm_offset_t +get_bktr_mem( int unit, unsigned size ) +{ + vm_offset_t addr = 0; + + addr = vm_page_alloc_contig(size, 0, 0xffffffff, 1<<24); + if (addr == 0) + addr = vm_page_alloc_contig(size, 0, 0xffffffff, PAGE_SIZE); + if (addr == 0) { + printf("bktr%d: Unable to allocate %d bytes of memory.\n", + unit, size); + } + + return( addr ); +} + + +/*--------------------------------------------------------- +** +** BrookTree 848 character device driver routines +** +**--------------------------------------------------------- +*/ + +#define VIDEO_DEV 0x00 +#define TUNER_DEV 0x01 +#define VBI_DEV 0x02 + +#define UNIT(x) ((x) & 0x0f) +#define FUNCTION(x) (x >> 4) + +/* + * + */ +int +bktr_open( dev_t dev, int flags, int fmt, struct proc *p ) +{ + bktr_ptr_t bktr; + int unit; + int result; + + unit = UNIT( minor(dev) ); + + /* Get the device data */ + bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit); + if (bktr == NULL) { + /* the device is no longer valid/functioning */ + return (ENXIO); + } + + if (!(bktr->flags & METEOR_INITALIZED)) /* device not found */ + return( ENXIO ); + + /* Record that the device is now busy */ + device_busy(devclass_get_device(bktr_devclass, unit)); + + + if (bt848_card != -1) { + if ((bt848_card >> 8 == unit ) && + ( (bt848_card & 0xff) < Bt848_MAX_CARD )) { + if ( bktr->bt848_card != (bt848_card & 0xff) ) { + bktr->bt848_card = (bt848_card & 0xff); + probeCard(bktr, FALSE, unit); + } + } + } + + if (bt848_tuner != -1) { + if ((bt848_tuner >> 8 == unit ) && + ( (bt848_tuner & 0xff) < Bt848_MAX_TUNER )) { + if ( bktr->bt848_tuner != (bt848_tuner & 0xff) ) { + bktr->bt848_tuner = (bt848_tuner & 0xff); + probeCard(bktr, FALSE, unit); + } + } + } + + if (bt848_reverse_mute != -1) { + if ((bt848_reverse_mute >> 8) == unit ) { + bktr->reverse_mute = bt848_reverse_mute & 0xff; + } + } + + if (bt848_slow_msp_audio != -1) { + if ((bt848_slow_msp_audio >> 8) == unit ) { + bktr->slow_msp_audio = (bt848_slow_msp_audio & 0xff); + } + } + + switch ( FUNCTION( minor(dev) ) ) { + case VIDEO_DEV: + result = video_open( bktr ); + break; + case TUNER_DEV: + result = tuner_open( bktr ); + break; + case VBI_DEV: + result = vbi_open( bktr ); + break; + default: + result = ENXIO; + break; + } + + /* If there was an error opening the device, undo the busy status */ + if (result != 0) + device_unbusy(devclass_get_device(bktr_devclass, unit)); + return( result ); +} + + +/* + * + */ +int +bktr_close( dev_t dev, int flags, int fmt, struct proc *p ) +{ + bktr_ptr_t bktr; + int unit; + int result; + + unit = UNIT( minor(dev) ); + + /* Get the device data */ + bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit); + if (bktr == NULL) { + /* the device is no longer valid/functioning */ + return (ENXIO); + } + + switch ( FUNCTION( minor(dev) ) ) { + case VIDEO_DEV: + result = video_close( bktr ); + break; + case TUNER_DEV: + result = tuner_close( bktr ); + break; + case VBI_DEV: + result = vbi_close( bktr ); + break; + default: + return (ENXIO); + break; + } + + device_unbusy(devclass_get_device(bktr_devclass, unit)); + return( result ); +} + + +/* + * + */ +int +bktr_read( dev_t dev, struct uio *uio, int ioflag ) +{ + bktr_ptr_t bktr; + int unit; + + unit = UNIT(minor(dev)); + + /* Get the device data */ + bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit); + if (bktr == NULL) { + /* the device is no longer valid/functioning */ + return (ENXIO); + } + + switch ( FUNCTION( minor(dev) ) ) { + case VIDEO_DEV: + return( video_read( bktr, unit, dev, uio ) ); + case VBI_DEV: + return( vbi_read( bktr, uio, ioflag ) ); + } + return( ENXIO ); +} + + +/* + * + */ +int +bktr_write( dev_t dev, struct uio *uio, int ioflag ) +{ + return( EINVAL ); /* XXX or ENXIO ? */ +} + + +/* + * + */ +int +bktr_ioctl( dev_t dev, ioctl_cmd_t cmd, caddr_t arg, int flag, struct proc* pr ) +{ + bktr_ptr_t bktr; + int unit; + + unit = UNIT(minor(dev)); + + /* Get the device data */ + bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit); + if (bktr == NULL) { + /* the device is no longer valid/functioning */ + return (ENXIO); + } + + if (bktr->bigbuf == 0) /* no frame buffer allocated (ioctl failed) */ + return( ENOMEM ); + + switch ( FUNCTION( minor(dev) ) ) { + case VIDEO_DEV: + return( video_ioctl( bktr, unit, cmd, arg, pr ) ); + case TUNER_DEV: + return( tuner_ioctl( bktr, unit, cmd, arg, pr ) ); + } + + return( ENXIO ); +} + + +/* + * + */ +int +bktr_mmap( dev_t dev, vm_offset_t offset, int nprot ) +{ + int unit; + bktr_ptr_t bktr; + + unit = UNIT(minor(dev)); + + if (FUNCTION(minor(dev)) > 0) /* only allow mmap on /dev/bktr[n] */ + return( -1 ); + + /* Get the device data */ + bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit); + if (bktr == NULL) { + /* the device is no longer valid/functioning */ + return (ENXIO); + } + + if (nprot & PROT_EXEC) + return( -1 ); + + if (offset < 0) + return( -1 ); + + if (offset >= bktr->alloc_pages * PAGE_SIZE) + return( -1 ); + + return( atop(vtophys(bktr->bigbuf) + offset) ); +} + +int bktr_poll( dev_t dev, int events, struct proc *p) +{ + int unit; + bktr_ptr_t bktr; + int revents = 0; + DECLARE_INTR_MASK(s); + + unit = UNIT(minor(dev)); + + /* Get the device data */ + bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit); + if (bktr == NULL) { + /* the device is no longer valid/functioning */ + return (ENXIO); + } + + DISABLE_INTR(s); + + if (events & (POLLIN | POLLRDNORM)) { + + switch ( FUNCTION( minor(dev) ) ) { + case VBI_DEV: + if(bktr->vbisize == 0) + selrecord(p, &bktr->vbi_select); + else + revents |= events & (POLLIN | POLLRDNORM); + break; + } + } + + ENABLE_INTR(s); + + return (revents); +} + +#endif /* FreeBSD 4.x specific kernel interface routines */ + +/**********************************/ +/* *** FreeBSD 2.2.x and 3.x *** */ +/**********************************/ + +#if ((__FreeBSD__ == 2) || (__FreeBSD__ == 3)) + +static bktr_reg_t brooktree[ NBKTR ]; + +static const char* bktr_probe( pcici_t tag, pcidi_t type ); +static void bktr_attach( pcici_t tag, int unit ); +static void bktr_intr(void *arg) { common_bktr_intr(arg); } + +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; +static d_poll_t bktr_poll; + +#define CDEV_MAJOR 92 +static struct cdevsw bktr_cdevsw = +{ + bktr_open, bktr_close, bktr_read, bktr_write, + bktr_ioctl, nostop, nullreset, nodevtotty, + bktr_poll, bktr_mmap, NULL, "bktr", + NULL, -1 +}; + +static int bktr_devsw_installed; + +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) + +/* + * the boot time probe routine. + */ +static const char* +bktr_probe( pcici_t tag, pcidi_t type ) +{ + unsigned int rev = pci_conf_read( tag, PCIR_REVID) & 0x000000ff; + + if (PCI_VENDOR(type) == PCI_VENDOR_BROOKTREE) + { + switch (PCI_PRODUCT(type)) { + case PCI_PRODUCT_BROOKTREE_BT848: + if (rev == 0x12) return("BrookTree 848A"); + else return("BrookTree 848"); + case PCI_PRODUCT_BROOKTREE_BT849: + return("BrookTree 849A"); + case PCI_PRODUCT_BROOKTREE_BT878: + return("BrookTree 878"); + case PCI_PRODUCT_BROOKTREE_BT879: + return("BrookTree 879"); + } + }; + + return ((char *)0); +} + +/* + * the attach routine. + */ +static void +bktr_attach( pcici_t tag, int unit ) +{ + bktr_ptr_t bktr; + u_long latency; + u_long fun; + unsigned int rev; + unsigned long base; +#ifdef BROOKTREE_IRQ + u_long old_irq, new_irq; +#endif + + 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; + } + + /* build the device name for bktr_name() */ + snprintf(bktr->bktr_xname, sizeof(bktr->bktr_xname), "bktr%d",unit); + + /* Enable Memory Mapping */ + fun = pci_conf_read(tag, PCI_COMMAND_STATUS_REG); + pci_conf_write(tag, PCI_COMMAND_STATUS_REG, fun | 2); + + /* Enable Bus Mastering */ + fun = pci_conf_read(tag, PCI_COMMAND_STATUS_REG); + pci_conf_write(tag, PCI_COMMAND_STATUS_REG, fun | 4); + + bktr->tag = tag; + + + /* + * Map control/status registers + */ + pci_map_mem( tag, PCI_MAP_REG_START, (vm_offset_t *) &base, + &bktr->phys_base ); +#if (__FreeBSD_version >= 300000) + bktr->memt = I386_BUS_SPACE_MEM; /* XXX should use proper bus space */ + bktr->memh = (bus_space_handle_t)base; /* XXX functions here */ +#endif + + /* + * Disable the brooktree device + */ + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED); + +#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, &tty_imask); + + + /* Update the Device Control Register */ + /* on Bt878 and Bt879 cards */ + fun = pci_conf_read(tag, 0x40); + fun = fun | 1; /* Enable writes to the sub-system vendor ID */ + +#if defined( BKTR_430_FX_MODE ) + if (bootverbose) printf("Using 430 FX chipset compatibilty mode\n"); + fun = fun | 2; /* Enable Intel 430 FX compatibility mode */ +#endif + +#if defined( BKTR_SIS_VIA_MODE ) + if (bootverbose) printf("Using SiS/VIA chipset compatibilty mode\n"); + fun = fun | 4; /* Enable SiS/VIA compatibility mode (usefull for + OPTi chipset motherboards too */ +#endif + pci_conf_write(tag, 0x40, fun); + + + /* XXX call bt848_i2c dependent attach() routine */ +#if defined(BKTR_USE_FREEBSD_SMBUS) + if (bt848_i2c_attach(unit, bktr, &bktr->i2c_sc)) + printf("bktr%d: i2c_attach: can't attach\n", unit); +#endif + + +/* + * PCI latency timer. 32 is a good value for 4 bus mastering slots, if + * you have more than four, 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", (int) latency); + } + + + /* read the pci device id and revision id */ + fun = pci_conf_read(tag, PCI_ID_REG); + rev = pci_conf_read(tag, PCIR_REVID) & 0x000000ff; + + /* call the common attach code */ + common_bktr_attach( bktr, unit, fun, rev ); + +} + + +/* + * Special Memory Allocation + */ +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("bktr%d: Unable to allocate %d bytes of memory.\n", + unit, size); + } + + return( addr ); +} + +/*--------------------------------------------------------- +** +** BrookTree 848 character device driver routines +** +**--------------------------------------------------------- +*/ + + +#define VIDEO_DEV 0x00 +#define TUNER_DEV 0x01 +#define VBI_DEV 0x02 + +#define UNIT(x) ((x) & 0x0f) +#define FUNCTION(x) ((x >> 4) & 0x0f) + + +/* + * + */ +int +bktr_open( dev_t dev, int flags, int fmt, struct proc *p ) +{ + bktr_ptr_t bktr; + int unit; + + 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 (bt848_card != -1) { + if ((bt848_card >> 8 == unit ) && + ( (bt848_card & 0xff) < Bt848_MAX_CARD )) { + if ( bktr->bt848_card != (bt848_card & 0xff) ) { + bktr->bt848_card = (bt848_card & 0xff); + probeCard(bktr, FALSE, unit); + } + } + } + + if (bt848_tuner != -1) { + if ((bt848_tuner >> 8 == unit ) && + ( (bt848_tuner & 0xff) < Bt848_MAX_TUNER )) { + if ( bktr->bt848_tuner != (bt848_tuner & 0xff) ) { + bktr->bt848_tuner = (bt848_tuner & 0xff); + probeCard(bktr, FALSE, unit); + } + } + } + + if (bt848_reverse_mute != -1) { + if ((bt848_reverse_mute >> 8) == unit ) { + bktr->reverse_mute = bt848_reverse_mute & 0xff; + } + } + + if (bt848_slow_msp_audio != -1) { + if ((bt848_slow_msp_audio >> 8) == unit ) { + bktr->slow_msp_audio = (bt848_slow_msp_audio & 0xff); + } + } + + switch ( FUNCTION( minor(dev) ) ) { + case VIDEO_DEV: + return( video_open( bktr ) ); + case TUNER_DEV: + return( tuner_open( bktr ) ); + case VBI_DEV: + return( vbi_open( bktr ) ); + } + return( ENXIO ); +} + + +/* + * + */ +int +bktr_close( dev_t dev, int flags, int fmt, struct proc *p ) +{ + bktr_ptr_t bktr; + int unit; + + unit = UNIT( minor(dev) ); + if (unit >= NBKTR) /* unit out of range */ + return( ENXIO ); + + bktr = &(brooktree[ unit ]); + + switch ( FUNCTION( minor(dev) ) ) { + case VIDEO_DEV: + return( video_close( bktr ) ); + case TUNER_DEV: + return( tuner_close( bktr ) ); + case VBI_DEV: + return( vbi_close( bktr ) ); + } + + return( ENXIO ); +} + +/* + * + */ +int +bktr_read( dev_t dev, struct uio *uio, int ioflag ) +{ + bktr_ptr_t bktr; + int unit; + + unit = UNIT(minor(dev)); + if (unit >= NBKTR) /* unit out of range */ + return( ENXIO ); + + bktr = &(brooktree[unit]); + + switch ( FUNCTION( minor(dev) ) ) { + case VIDEO_DEV: + return( video_read( bktr, unit, dev, uio ) ); + case VBI_DEV: + return( vbi_read( bktr, uio, ioflag ) ); + } + return( ENXIO ); +} + + +/* + * + */ +int +bktr_write( dev_t dev, struct uio *uio, int ioflag ) +{ + return( EINVAL ); /* XXX or ENXIO ? */ +} + +/* + * + */ +int +bktr_ioctl( dev_t dev, ioctl_cmd_t cmd, caddr_t arg, int flag, struct proc* pr ) +{ + bktr_ptr_t bktr; + int unit; + + 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 ); + + switch ( FUNCTION( minor(dev) ) ) { + case VIDEO_DEV: + return( video_ioctl( bktr, unit, cmd, arg, pr ) ); + case TUNER_DEV: + return( tuner_ioctl( bktr, unit, cmd, arg, pr ) ); + } + + return( ENXIO ); +} + +/* + * bktr_mmap. + * Note: 2.2.5/2.2.6/2.2.7/3.0 users must manually + * edit the line below and change "vm_offset_t" to "int" + */ +int bktr_mmap( dev_t dev, vm_offset_t offset, int nprot ) + +{ + int unit; + bktr_ptr_t bktr; + + unit = UNIT(minor(dev)); + + if (unit >= NBKTR || FUNCTION(minor(dev)) > 0) + return( -1 ); + + bktr = &(brooktree[ unit ]); + + if (nprot & PROT_EXEC) + return( -1 ); + + if (offset < 0) + return( -1 ); + + if (offset >= bktr->alloc_pages * PAGE_SIZE) + return( -1 ); + + return( i386_btop(vtophys(bktr->bigbuf) + offset) ); +} + +int bktr_poll( dev_t dev, int events, struct proc *p) +{ + int unit; + bktr_ptr_t bktr; + int revents = 0; + + unit = UNIT(minor(dev)); + + if (unit >= NBKTR) + return( -1 ); + + bktr = &(brooktree[ unit ]); + + disable_intr(); + + if (events & (POLLIN | POLLRDNORM)) { + + switch ( FUNCTION( minor(dev) ) ) { + case VBI_DEV: + if(bktr->vbisize == 0) + selrecord(p, &bktr->vbi_select); + else + revents |= events & (POLLIN | POLLRDNORM); + break; + } + } + + enable_intr(); + + return (revents); +} + + +#endif /* FreeBSD 2.2.x and 3.x specific kernel interface routines */ + + +/*****************/ +/* *** BSDI *** */ +/*****************/ + +#if defined(__bsdi__) +#endif /* __bsdi__ BSDI specific kernel interface routines */ + + +/*****************************/ +/* *** OpenBSD / NetBSD *** */ +/*****************************/ +#if defined(__NetBSD__) || defined(__OpenBSD__) + +#define IPL_VIDEO IPL_BIO /* XXX */ + +static int bktr_intr(void *arg) { return common_bktr_intr(arg); } + +#define bktr_open bktropen +#define bktr_close bktrclose +#define bktr_read bktrread +#define bktr_write bktrwrite +#define bktr_ioctl bktrioctl +#define bktr_mmap bktrmmap + +#ifdef __OpenBSD__ +int bktr_open __P((dev_t, int, int, struct proc *)); +int bktr_close __P((dev_t, int, int, struct proc *)); +int bktr_read __P((dev_t, struct uio *, int)); +int bktr_write __P((dev_t, struct uio *, int)); +int bktr_ioctl __P((dev_t, ioctl_cmd_t, caddr_t, int, struct proc *)); +paddr_t bktr_mmap __P((dev_t, off_t, int)); +#endif + +vm_offset_t vm_page_alloc_contig(vm_offset_t, vm_offset_t, + vm_offset_t, vm_offset_t); + +#if defined(__OpenBSD__) +static int bktr_probe __P((struct device *, void *, void *)); +#else +static int bktr_probe __P((struct device *, struct cfdata *, void *)); +#endif +static void bktr_attach __P((struct device *, struct device *, void *)); + +struct cfattach bktr_ca = { + sizeof(struct bktr_softc), bktr_probe, bktr_attach +}; + +#if defined(__NetBSD__) +extern struct cfdriver bktr_cd; +#else +struct cfdriver bktr_cd = { + NULL, "bktr", DV_DULL +}; +#endif + +int +bktr_probe(parent, match, aux) + struct device *parent; +#if defined(__OpenBSD__) + void *match; +#else + struct cfdata *match; +#endif + void *aux; +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_BROOKTREE && + (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_BROOKTREE_BT848 || + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_BROOKTREE_BT849 || + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_BROOKTREE_BT878 || + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_BROOKTREE_BT879)) + return 1; + + return 0; +} + + +/* + * the attach routine. + */ +static void +bktr_attach(struct device *parent, struct device *self, void *aux) +{ + bktr_ptr_t bktr; + u_long latency; + u_long fun; + unsigned int rev; + struct pci_attach_args *pa = aux; + pci_intr_handle_t ih; + const char *intrstr; + int retval; + int unit; + + bktr = (bktr_ptr_t)self; + unit = bktr->bktr_dev.dv_unit; + bktr->dmat = pa->pa_dmat; + +#ifndef __OpenBSD__ + printf("\n"); +#endif + + /* + * map memory + */ + retval = pci_mapreg_map(pa, PCI_MAPREG_START, + PCI_MAPREG_TYPE_MEM + | PCI_MAPREG_MEM_TYPE_32BIT, 0, + &bktr->memt, &bktr->memh, NULL, + &bktr->obmemsz); + DPR(("pci_mapreg_map: memt %x, memh %x, size %x\n", + bktr->memt, (u_int)bktr->memh, (u_int)bktr->obmemsz)); + if (retval) { + printf("%s: couldn't map memory\n", bktr_name(bktr)); + return; + } + + /* + * Disable the brooktree device + */ + OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED); + OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED); + + /* + * map interrupt + */ + if (pci_intr_map(pa->pa_pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih)) { + printf("%s: couldn't map interrupt\n", + bktr_name(bktr)); + return; + } + intrstr = pci_intr_string(pa->pa_pc, ih); + bktr->ih = pci_intr_establish(pa->pa_pc, ih, IPL_VIDEO, + bktr_intr, bktr +#ifdef __OpenBSD__ + , bktr->bktr_dev.dv_xname +#endif + ); + if (bktr->ih == NULL) { + printf("%s: couldn't establish interrupt", + bktr_name(bktr)); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + if (intrstr != NULL) +#ifdef __NetBSD__ + printf("%s: interrupting at %s\n", bktr_name(bktr), + intrstr); +#else + printf(": %s\n", intrstr); +#endif + +/* + * PCI latency timer. 32 is a good value for 4 bus mastering slots, if + * you have more than four, then 16 would probably be a better value. + */ +#ifndef BROOKTREE_DEF_LATENCY_VALUE +#define BROOKTREE_DEF_LATENCY_VALUE 10 +#endif + latency = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_LATENCY_TIMER); + latency = (latency >> 8) & 0xff; + + if (!latency) { + if (bootverbose) { + printf("%s: PCI bus latency was 0 changing to %d", + bktr_name(bktr), BROOKTREE_DEF_LATENCY_VALUE); + } + latency = BROOKTREE_DEF_LATENCY_VALUE; + pci_conf_write(pa->pa_pc, pa->pa_tag, + PCI_LATENCY_TIMER, latency<<8); + } + + + /* Enabled Bus Master + XXX: check if all old DMA is stopped first (e.g. after warm + boot) */ + fun = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, + fun | PCI_COMMAND_MASTER_ENABLE); + + /* read the pci id and determine the card type */ + fun = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ID_REG); + rev = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_CLASS_REG) & 0x000000ff; + + common_bktr_attach(bktr, unit, fun, rev); +} + + +/* + * Special Memory Allocation + */ +vm_offset_t +get_bktr_mem(bktr, dmapp, size) + bktr_ptr_t bktr; + bus_dmamap_t *dmapp; + unsigned int size; +{ + bus_dma_tag_t dmat = bktr->dmat; + bus_dma_segment_t seg; + bus_size_t align; + int rseg; + caddr_t kva; + + /* + * Allocate a DMA area + */ + align = 1 << 24; + if (bus_dmamem_alloc(dmat, size, align, 0, &seg, 1, + &rseg, BUS_DMA_NOWAIT)) { + align = PAGE_SIZE; + if (bus_dmamem_alloc(dmat, size, align, 0, &seg, 1, + &rseg, BUS_DMA_NOWAIT)) { + printf("%s: Unable to dmamem_alloc of %d bytes\n", + bktr_name(bktr), size); + return 0; + } + } + if (bus_dmamem_map(dmat, &seg, rseg, size, + &kva, BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) { + printf("%s: Unable to dmamem_map of %d bytes\n", + bktr_name(bktr), size); + bus_dmamem_free(dmat, &seg, rseg); + return 0; + } +#ifdef __OpenBSD__ + bktr->dm_mapsize = size; +#endif + /* + * Create and locd the DMA map for the DMA area + */ + if (bus_dmamap_create(dmat, size, 1, size, 0, BUS_DMA_NOWAIT, dmapp)) { + printf("%s: Unable to dmamap_create of %d bytes\n", + bktr_name(bktr), size); + bus_dmamem_unmap(dmat, kva, size); + bus_dmamem_free(dmat, &seg, rseg); + return 0; + } + if (bus_dmamap_load(dmat, *dmapp, kva, size, NULL, BUS_DMA_NOWAIT)) { + printf("%s: Unable to dmamap_load of %d bytes\n", + bktr_name(bktr), size); + bus_dmamem_unmap(dmat, kva, size); + bus_dmamem_free(dmat, &seg, rseg); + bus_dmamap_destroy(dmat, *dmapp); + return 0; + } + return (vm_offset_t)kva; +} + +void +free_bktr_mem(bktr, dmap, kva) + bktr_ptr_t bktr; + bus_dmamap_t dmap; + vm_offset_t kva; +{ + bus_dma_tag_t dmat = bktr->dmat; + +#ifdef __NetBSD__ + bus_dmamem_unmap(dmat, (caddr_t)kva, dmap->dm_mapsize); +#else + bus_dmamem_unmap(dmat, (caddr_t)kva, bktr->dm_mapsize); +#endif + bus_dmamem_free(dmat, dmap->dm_segs, 1); + bus_dmamap_destroy(dmat, dmap); +} + + +/*--------------------------------------------------------- +** +** BrookTree 848 character device driver routines +** +**--------------------------------------------------------- +*/ + + +#define VIDEO_DEV 0x00 +#define TUNER_DEV 0x01 +#define VBI_DEV 0x02 + +#define UNIT(x) (minor((x) & 0x0f)) +#define FUNCTION(x) (minor((x >> 4) & 0x0f)) + +/* + * + */ +int +bktr_open(dev_t dev, int flags, int fmt, struct proc *p) +{ + bktr_ptr_t bktr; + int unit; + + unit = UNIT(dev); + + /* unit out of range */ + if ((unit > bktr_cd.cd_ndevs) || (bktr_cd.cd_devs[unit] == NULL)) + return(ENXIO); + + bktr = bktr_cd.cd_devs[unit]; + + if (!(bktr->flags & METEOR_INITALIZED)) /* device not found */ + return(ENXIO); + + switch (FUNCTION(dev)) { + case VIDEO_DEV: + return(video_open(bktr)); + case TUNER_DEV: + return(tuner_open(bktr)); + case VBI_DEV: + return(vbi_open(bktr)); + } + + return(ENXIO); +} + + +/* + * + */ +int +bktr_close(dev_t dev, int flags, int fmt, struct proc *p) +{ + bktr_ptr_t bktr; + int unit; + + unit = UNIT(dev); + + bktr = bktr_cd.cd_devs[unit]; + + switch (FUNCTION(dev)) { + case VIDEO_DEV: + return(video_close(bktr)); + case TUNER_DEV: + return(tuner_close(bktr)); + case VBI_DEV: + return(vbi_close(bktr)); + } + + return(ENXIO); +} + +/* + * + */ +int +bktr_read(dev_t dev, struct uio *uio, int ioflag) +{ + bktr_ptr_t bktr; + int unit; + + unit = UNIT(dev); + + bktr = bktr_cd.cd_devs[unit]; + + switch (FUNCTION(dev)) { + case VIDEO_DEV: + return(video_read(bktr, unit, dev, uio)); + case VBI_DEV: + return(vbi_read(bktr, uio, ioflag)); + } + + return(ENXIO); +} + + +/* + * + */ +int +bktr_write(dev_t dev, struct uio *uio, int ioflag) +{ + /* operation not supported */ + return(EOPNOTSUPP); +} + +/* + * + */ +int +bktr_ioctl(dev_t dev, ioctl_cmd_t cmd, caddr_t arg, int flag, struct proc* pr) +{ + bktr_ptr_t bktr; + int unit; + + unit = UNIT(dev); + + bktr = bktr_cd.cd_devs[unit]; + + if (bktr->bigbuf == 0) /* no frame buffer allocated (ioctl failed) */ + return(ENOMEM); + + switch (FUNCTION(dev)) { + case VIDEO_DEV: + return(video_ioctl(bktr, unit, cmd, arg, pr)); + case TUNER_DEV: + return(tuner_ioctl(bktr, unit, cmd, arg, pr)); + } + + return(ENXIO); +} + +/* + * + */ +paddr_t +bktr_mmap(dev_t dev, off_t offset, int nprot) +{ + int unit; + bktr_ptr_t bktr; + + unit = UNIT(dev); + + if (FUNCTION(dev) > 0) /* only allow mmap on /dev/bktr[n] */ + return(-1); + + bktr = bktr_cd.cd_devs[unit]; + + if ((vaddr_t)offset < 0) + return(-1); + + if ((vaddr_t)offset >= bktr->alloc_pages * PAGE_SIZE) + return(-1); + + return (bus_dmamem_mmap(bktr->dmat, bktr->dm_mem->dm_segs, 1, + (vaddr_t)offset, nprot, BUS_DMA_WAITOK)); +} + +#endif /* __NetBSD__ || __OpenBSD__ */ diff --git a/sys/dev/pci/bktr/bktr_os.h b/sys/dev/pci/bktr/bktr_os.h new file mode 100644 index 00000000000..a20a4a35cfb --- /dev/null +++ b/sys/dev/pci/bktr/bktr_os.h @@ -0,0 +1,73 @@ +/* $OpenBSD: bktr_os.h,v 1.1 2001/03/28 03:27:09 fgsch Exp $ */ +/* $FreeBSD: src/sys/dev/bktr/bktr_os.h,v 1.4 2000/04/16 07:56:58 roger Exp $ */ + +/* + * This is part of the Driver for Video Capture Cards (Frame grabbers) + * and TV Tuner cards using the Brooktree Bt848, Bt848A, Bt849A, Bt878, Bt879 + * chipset. + * Copyright Roger Hardiman and Amancio Hasty. + * + * bktr_os : This has all the Operating System dependant code. + * + */ + +/* + * 1. Redistributions of source code must retain the + * Copyright (c) 1997 Amancio Hasty, 1999 Roger Hardiman + * 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 and + * Roger Hardiman + * 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. + */ + + +/******************************/ +/* *** Memory Allocation *** */ +/******************************/ +#if (defined(__FreeBSD__) || defined(__bsdi__)) +vm_offset_t get_bktr_mem( int unit, unsigned size ); +#endif + +#if (defined(__NetBSD__) || defined(__OpenBSD__)) +vm_offset_t get_bktr_mem(bktr_ptr_t, bus_dmamap_t *, unsigned size); +void free_bktr_mem(bktr_ptr_t, bus_dmamap_t, vm_offset_t); +#endif + +/************************************/ +/* *** Interrupt Enable/Disable *** */ +/************************************/ +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#define DECLARE_INTR_MASK(s) intrmask_t s +#define DISABLE_INTR(s) s=spltty() +#define ENABLE_INTR(s) splx(s) +#else +#define DECLARE_INTR_MASK(s) /* no need to declare 's' */ +#define DISABLE_INTR(s) disable_intr() +#define ENABLE_INTR(s) enable_intr() +#endif + + diff --git a/sys/dev/pci/bktr/bktr_reg.h b/sys/dev/pci/bktr/bktr_reg.h new file mode 100644 index 00000000000..f6e43ca60c5 --- /dev/null +++ b/sys/dev/pci/bktr/bktr_reg.h @@ -0,0 +1,729 @@ +/* $OpenBSD: bktr_reg.h,v 1.1 2001/03/28 03:27:10 fgsch Exp $ */ +/* + * $FreeBSD: src/sys/dev/bktr/bktr_reg.h,v 1.42 2000/10/31 13:09:56 roger Exp $ + * + * Copyright (c) 1999 Roger Hardiman + * Copyright (c) 1998 Amancio Hasty + * 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. + * + */ + +#ifdef __FreeBSD__ +# if (__FreeBSD_version >= 310000) +# include "smbus.h" +# else +# define NSMBUS 0 /* FreeBSD before 3.1 does not have SMBUS */ +# endif +# if (NSMBUS > 0) +# define BKTR_USE_FREEBSD_SMBUS +# endif +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <machine/bus.h> /* struct device */ +#include <sys/device.h> +#include <sys/select.h> /* struct selinfo */ +# ifdef DEBUG +# define bootverbose 1 +# else +# define bootverbose 0 +# endif +#endif + +/* + * The kernel options for the driver now all begin with BKTR. + * Support the older kernel options on FreeBSD and OpenBSD. + * + */ +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#if defined(BROOKTREE_ALLOC_PAGES) +#define BKTR_ALLOC_PAGES BROOKTREE_ALLOC_PAGES +#endif + +#if defined(BROOKTREE_SYSTEM_DEFAULT) +#define BKTR_SYSTEM_DEFAULT BROOKTREE_SYSTEM_DEFAULT +#endif + +#if defined(OVERRIDE_CARD) +#define BKTR_OVERRIDE_CARD OVERRIDE_CARD +#endif + +#if defined(OVERRIDE_TUNER) +#define BKTR_OVERRIDE_TUNER OVERRIDE_TUNER +#endif + +#if defined(OVERRIDE_DBX) +#define BKTR_OVERRIDE_DBX OVERRIDE_DBX +#endif + +#if defined(OVERRIDE_MSP) +#define BKTR_OVERRIDE_MSP OVERRIDE_MSP +#endif + +#endif + + +#ifndef PCI_LATENCY_TIMER +#define PCI_LATENCY_TIMER 0x0c /* pci timer register */ +#endif + +/* + * Definitions for the Brooktree 848/878 video capture to pci interface. + */ +#ifndef __NetBSD__ +#ifdef __OpenBSD__ +#include <dev/pci/pcivar.h> +#else +#define PCI_VENDOR_SHIFT 0 +#define PCI_VENDOR_MASK 0xffff +#define PCI_VENDOR(id) \ + (((id) >> PCI_VENDOR_SHIFT) & PCI_VENDOR_MASK) + +#define PCI_PRODUCT_SHIFT 16 +#define PCI_PRODUCT_MASK 0xffff +#define PCI_PRODUCT(id) \ + (((id) >> PCI_PRODUCT_SHIFT) & PCI_PRODUCT_MASK) +#endif + +/* PCI vendor ID */ +#define PCI_VENDOR_BROOKTREE 0x109e /* Brooktree */ +/* Brooktree products */ +#define PCI_PRODUCT_BROOKTREE_BT848 0x0350 /* Bt848 Video Capture */ +#define PCI_PRODUCT_BROOKTREE_BT849 0x0351 /* Bt849 Video Capture */ +#define PCI_PRODUCT_BROOKTREE_BT878 0x036e /* Bt878 Video Capture */ +#define PCI_PRODUCT_BROOKTREE_BT879 0x036f /* Bt879 Video Capture */ +#endif + +#define BROOKTREE_848 1 +#define BROOKTREE_848A 2 +#define BROOKTREE_849A 3 +#define BROOKTREE_878 4 +#define BROOKTREE_879 5 + +typedef volatile u_int bregister_t; +/* + * if other persuasion endian, then compiler will probably require that + * these next + * macros be reversed + */ +#define BTBYTE(what) bregister_t what:8; int :24 +#define BTWORD(what) bregister_t what:16; int: 16 +#define BTLONG(what) bregister_t what:32 + +struct bt848_registers { + BTBYTE (dstatus); /* 0, 1,2,3 */ +#define BT848_DSTATUS_PRES (1<<7) +#define BT848_DSTATUS_HLOC (1<<6) +#define BT848_DSTATUS_FIELD (1<<5) +#define BT848_DSTATUS_NUML (1<<4) +#define BT848_DSTATUS_CSEL (1<<3) +#define BT848_DSTATUS_PLOCK (1<<2) +#define BT848_DSTATUS_LOF (1<<1) +#define BT848_DSTATUS_COF (1<<0) + BTBYTE (iform); /* 4, 5,6,7 */ +#define BT848_IFORM_MUXSEL (0x3<<5) +# define BT848_IFORM_M_MUX1 (0x03<<5) +# define BT848_IFORM_M_MUX0 (0x02<<5) +# define BT848_IFORM_M_MUX2 (0x01<<5) +# define BT848_IFORM_M_MUX3 (0x0) +# define BT848_IFORM_M_RSVD (0x00<<5) +#define BT848_IFORM_XTSEL (0x3<<3) +# define BT848_IFORM_X_AUTO (0x03<<3) +# define BT848_IFORM_X_XT1 (0x02<<3) +# define BT848_IFORM_X_XT0 (0x01<<3) +# define BT848_IFORM_X_RSVD (0x00<<3) + BTBYTE (tdec); /* 8, 9,a,b */ + BTBYTE (e_crop); /* c, d,e,f */ + BTBYTE (e_vdelay_lo); /* 10, 11,12,13 */ + BTBYTE (e_vactive_lo); /* 14, 15,16,17 */ + BTBYTE (e_delay_lo); /* 18, 19,1a,1b */ + BTBYTE (e_hactive_lo); /* 1c, 1d,1e,1f */ + BTBYTE (e_hscale_hi); /* 20, 21,22,23 */ + BTBYTE (e_hscale_lo); /* 24, 25,26,27 */ + BTBYTE (bright); /* 28, 29,2a,2b */ + BTBYTE (e_control); /* 2c, 2d,2e,2f */ +#define BT848_E_CONTROL_LNOTCH (1<<7) +#define BT848_E_CONTROL_COMP (1<<6) +#define BT848_E_CONTROL_LDEC (1<<5) +#define BT848_E_CONTROL_CBSENSE (1<<4) +#define BT848_E_CONTROL_RSVD (1<<3) +#define BT848_E_CONTROL_CON_MSB (1<<2) +#define BT848_E_CONTROL_SAT_U_MSB (1<<1) +#define BT848_E_CONTROL_SAT_V_MSB (1<<0) + BTBYTE (contrast_lo); /* 30, 31,32,33 */ + BTBYTE (sat_u_lo); /* 34, 35,36,37 */ + BTBYTE (sat_v_lo); /* 38, 39,3a,3b */ + BTBYTE (hue); /* 3c, 3d,3e,3f */ + BTBYTE (e_scloop); /* 40, 41,42,43 */ +#define BT848_E_SCLOOP_RSVD1 (1<<7) +#define BT848_E_SCLOOP_CAGC (1<<6) +#define BT848_E_SCLOOP_CKILL (1<<5) +#define BT848_E_SCLOOP_HFILT (0x3<<3) +# define BT848_E_SCLOOP_HFILT_ICON (0x3<<3) +# define BT848_E_SCLOOP_HFILT_QCIF (0x2<<3) +# define BT848_E_SCLOOP_HFILT_CIF (0x1<<3) +# define BT848_E_SCLOOP_HFILT_AUTO (0x0<<3) +#define BT848_E_SCLOOP_RSVD0 (0x7<<0) + int :32; /* 44, 45,46,47 */ + BTBYTE (oform); /* 48, 49,4a,4b */ + BTBYTE (e_vscale_hi); /* 4c, 4d,4e,4f */ + BTBYTE (e_vscale_lo); /* 50, 51,52,53 */ + BTBYTE (test); /* 54, 55,56,57 */ + int :32; /* 58, 59,5a,5b */ + int :32; /* 5c, 5d,5e,5f */ + BTLONG (adelay); /* 60, 61,62,63 */ + BTBYTE (bdelay); /* 64, 65,66,67 */ + BTBYTE (adc); /* 68, 69,6a,6b */ +#define BT848_ADC_RESERVED (0x80) /* required pattern */ +#define BT848_ADC_SYNC_T (1<<5) +#define BT848_ADC_AGC_EN (1<<4) +#define BT848_ADC_CLK_SLEEP (1<<3) +#define BT848_ADC_Y_SLEEP (1<<2) +#define BT848_ADC_C_SLEEP (1<<1) +#define BT848_ADC_CRUSH (1<<0) + BTBYTE (e_vtc); /* 6c, 6d,6e,6f */ + int :32; /* 70, 71,72,73 */ + int :32; /* 74, 75,76,77 */ + int :32; /* 78, 79,7a,7b */ + BTLONG (sreset); /* 7c, 7d,7e,7f */ + u_char filler1[0x84-0x80]; + BTBYTE (tgctrl); /* 84, 85,86,87 */ +#define BT848_TGCTRL_TGCKI (3<<3) +#define BT848_TGCTRL_TGCKI_XTAL (0<<3) +#define BT848_TGCTRL_TGCKI_PLL (1<<3) +#define BT848_TGCTRL_TGCKI_GPCLK (2<<3) +#define BT848_TGCTRL_TGCKI_GPCLK_I (3<<3) + u_char filler[0x8c-0x88]; + BTBYTE (o_crop); /* 8c, 8d,8e,8f */ + BTBYTE (o_vdelay_lo); /* 90, 91,92,93 */ + BTBYTE (o_vactive_lo); /* 94, 95,96,97 */ + BTBYTE (o_delay_lo); /* 98, 99,9a,9b */ + BTBYTE (o_hactive_lo); /* 9c, 9d,9e,9f */ + BTBYTE (o_hscale_hi); /* a0, a1,a2,a3 */ + BTBYTE (o_hscale_lo); /* a4, a5,a6,a7 */ + int :32; /* a8, a9,aa,ab */ + BTBYTE (o_control); /* ac, ad,ae,af */ +#define BT848_O_CONTROL_LNOTCH (1<<7) +#define BT848_O_CONTROL_COMP (1<<6) +#define BT848_O_CONTROL_LDEC (1<<5) +#define BT848_O_CONTROL_CBSENSE (1<<4) +#define BT848_O_CONTROL_RSVD (1<<3) +#define BT848_O_CONTROL_CON_MSB (1<<2) +#define BT848_O_CONTROL_SAT_U_MSB (1<<1) +#define BT848_O_CONTROL_SAT_V_MSB (1<<0) + u_char fillter4[16]; + BTBYTE (o_scloop); /* c0, c1,c2,c3 */ +#define BT848_O_SCLOOP_RSVD1 (1<<7) +#define BT848_O_SCLOOP_CAGC (1<<6) +#define BT848_O_SCLOOP_CKILL (1<<5) +#define BT848_O_SCLOOP_HFILT (0x3<<3) +#define BT848_O_SCLOOP_HFILT_ICON (0x3<<3) +#define BT848_O_SCLOOP_HFILT_QCIF (0x2<<3) +#define BT848_O_SCLOOP_HFILT_CIF (0x1<<3) +#define BT848_O_SCLOOP_HFILT_AUTO (0x0<<3) +#define BT848_O_SCLOOP_RSVD0 (0x7<<0) + int :32; /* c4, c5,c6,c7 */ + int :32; /* c8, c9,ca,cb */ + BTBYTE (o_vscale_hi); /* cc, cd,ce,cf */ + BTBYTE (o_vscale_lo); /* d0, d1,d2,d3 */ + BTBYTE (color_fmt); /* d4, d5,d6,d7 */ + bregister_t color_ctl_swap :4; /* d8 */ +#define BT848_COLOR_CTL_WSWAP_ODD (1<<3) +#define BT848_COLOR_CTL_WSWAP_EVEN (1<<2) +#define BT848_COLOR_CTL_BSWAP_ODD (1<<1) +#define BT848_COLOR_CTL_BSWAP_EVEN (1<<0) + bregister_t color_ctl_gamma :1; + bregister_t color_ctl_rgb_ded :1; + bregister_t color_ctl_color_bars :1; + bregister_t color_ctl_ext_frmrate :1; +#define BT848_COLOR_CTL_GAMMA (1<<4) +#define BT848_COLOR_CTL_RGB_DED (1<<5) +#define BT848_COLOR_CTL_COLOR_BARS (1<<6) +#define BT848_COLOR_CTL_EXT_FRMRATE (1<<7) + int :24; /* d9,da,db */ + BTBYTE (cap_ctl); /* dc, dd,de,df */ +#define BT848_CAP_CTL_DITH_FRAME (1<<4) +#define BT848_CAP_CTL_VBI_ODD (1<<3) +#define BT848_CAP_CTL_VBI_EVEN (1<<2) +#define BT848_CAP_CTL_ODD (1<<1) +#define BT848_CAP_CTL_EVEN (1<<0) + BTBYTE (vbi_pack_size); /* e0, e1,e2,e3 */ + BTBYTE (vbi_pack_del); /* e4, e5,e6,e7 */ + int :32; /* e8, e9,ea,eb */ + BTBYTE (o_vtc); /* ec, ed,ee,ef */ + BTBYTE (pll_f_lo); /* f0, f1,f2,f3 */ + BTBYTE (pll_f_hi); /* f4, f5,f6,f7 */ + BTBYTE (pll_f_xci); /* f8, f9,fa,fb */ +#define BT848_PLL_F_C (1<<6) +#define BT848_PLL_F_X (1<<7) + u_char filler2[0x100-0xfc]; + BTLONG (int_stat); /* 100, 101,102,103 */ + BTLONG (int_mask); /* 104, 105,106,107 */ +#define BT848_INT_RISCS (0xf<<28) +#define BT848_INT_RISC_EN (1<<27) +#define BT848_INT_RACK (1<<25) +#define BT848_INT_FIELD (1<<24) +#define BT848_INT_MYSTERYBIT (1<<23) +#define BT848_INT_SCERR (1<<19) +#define BT848_INT_OCERR (1<<18) +#define BT848_INT_PABORT (1<<17) +#define BT848_INT_RIPERR (1<<16) +#define BT848_INT_PPERR (1<<15) +#define BT848_INT_FDSR (1<<14) +#define BT848_INT_FTRGT (1<<13) +#define BT848_INT_FBUS (1<<12) +#define BT848_INT_RISCI (1<<11) +#define BT848_INT_GPINT (1<<9) +#define BT848_INT_I2CDONE (1<<8) +#define BT848_INT_RSV1 (1<<7) +#define BT848_INT_RSV0 (1<<6) +#define BT848_INT_VPRES (1<<5) +#define BT848_INT_HLOCK (1<<4) +#define BT848_INT_OFLOW (1<<3) +#define BT848_INT_HSYNC (1<<2) +#define BT848_INT_VSYNC (1<<1) +#define BT848_INT_FMTCHG (1<<0) + int :32; /* 108, 109,10a,10b */ + BTWORD (gpio_dma_ctl); /* 10c, 10d,10e,10f */ +#define BT848_DMA_CTL_PL23TP4 (0<<6) /* planar1 trigger 4 */ +#define BT848_DMA_CTL_PL23TP8 (1<<6) /* planar1 trigger 8 */ +#define BT848_DMA_CTL_PL23TP16 (2<<6) /* planar1 trigger 16 */ +#define BT848_DMA_CTL_PL23TP32 (3<<6) /* planar1 trigger 32 */ +#define BT848_DMA_CTL_PL1TP4 (0<<4) /* planar1 trigger 4 */ +#define BT848_DMA_CTL_PL1TP8 (1<<4) /* planar1 trigger 8 */ +#define BT848_DMA_CTL_PL1TP16 (2<<4) /* planar1 trigger 16 */ +#define BT848_DMA_CTL_PL1TP32 (3<<4) /* planar1 trigger 32 */ +#define BT848_DMA_CTL_PKTP4 (0<<2) /* packed trigger 4 */ +#define BT848_DMA_CTL_PKTP8 (1<<2) /* packed trigger 8 */ +#define BT848_DMA_CTL_PKTP16 (2<<2) /* packed trigger 16 */ +#define BT848_DMA_CTL_PKTP32 (3<<2) /* packed trigger 32 */ +#define BT848_DMA_CTL_RISC_EN (1<<1) +#define BT848_DMA_CTL_FIFO_EN (1<<0) + BTLONG (i2c_data_ctl); /* 110, 111,112,113 */ +#define BT848_DATA_CTL_I2CDIV (0xf<<4) +#define BT848_DATA_CTL_I2CSYNC (1<<3) +#define BT848_DATA_CTL_I2CW3B (1<<2) +#define BT848_DATA_CTL_I2CSCL (1<<1) +#define BT848_DATA_CTL_I2CSDA (1<<0) + BTLONG (risc_strt_add); /* 114, 115,116,117 */ + BTLONG (gpio_out_en); /* 118, 119,11a,11b */ /* really 24 bits */ + BTLONG (gpio_reg_inp); /* 11c, 11d,11e,11f */ /* really 24 bits */ + BTLONG (risc_count); /* 120, 121,122,123 */ + u_char filler3[0x200-0x124]; + BTLONG (gpio_data); /* 200, 201,202,203 */ /* really 24 bits */ +}; + + +#define BKTR_DSTATUS 0x000 +#define BKTR_IFORM 0x004 +#define BKTR_TDEC 0x008 +#define BKTR_E_CROP 0x00C +#define BKTR_O_CROP 0x08C +#define BKTR_E_VDELAY_LO 0x010 +#define BKTR_O_VDELAY_LO 0x090 +#define BKTR_E_VACTIVE_LO 0x014 +#define BKTR_O_VACTIVE_LO 0x094 +#define BKTR_E_DELAY_LO 0x018 +#define BKTR_O_DELAY_LO 0x098 +#define BKTR_E_HACTIVE_LO 0x01C +#define BKTR_O_HACTIVE_LO 0x09C +#define BKTR_E_HSCALE_HI 0x020 +#define BKTR_O_HSCALE_HI 0x0A0 +#define BKTR_E_HSCALE_LO 0x024 +#define BKTR_O_HSCALE_LO 0x0A4 +#define BKTR_BRIGHT 0x028 +#define BKTR_E_CONTROL 0x02C +#define BKTR_O_CONTROL 0x0AC +#define BKTR_CONTRAST_LO 0x030 +#define BKTR_SAT_U_LO 0x034 +#define BKTR_SAT_V_LO 0x038 +#define BKTR_HUE 0x03C +#define BKTR_E_SCLOOP 0x040 +#define BKTR_O_SCLOOP 0x0C0 +#define BKTR_OFORM 0x048 +#define BKTR_E_VSCALE_HI 0x04C +#define BKTR_O_VSCALE_HI 0x0CC +#define BKTR_E_VSCALE_LO 0x050 +#define BKTR_O_VSCALE_LO 0x0D0 +#define BKTR_TEST 0x054 +#define BKTR_ADELAY 0x060 +#define BKTR_BDELAY 0x064 +#define BKTR_ADC 0x068 +#define BKTR_E_VTC 0x06C +#define BKTR_O_VTC 0x0EC +#define BKTR_SRESET 0x07C +#define BKTR_COLOR_FMT 0x0D4 +#define BKTR_COLOR_CTL 0x0D8 +#define BKTR_CAP_CTL 0x0DC +#define BKTR_VBI_PACK_SIZE 0x0E0 +#define BKTR_VBI_PACK_DEL 0x0E4 +#define BKTR_INT_STAT 0x100 +#define BKTR_INT_MASK 0x104 +#define BKTR_RISC_COUNT 0x120 +#define BKTR_RISC_STRT_ADD 0x114 +#define BKTR_GPIO_DMA_CTL 0x10C +#define BKTR_GPIO_OUT_EN 0x118 +#define BKTR_GPIO_REG_INP 0x11C +#define BKTR_GPIO_DATA 0x200 +#define BKTR_I2C_DATA_CTL 0x110 +#define BKTR_TGCTRL 0x084 +#define BKTR_PLL_F_LO 0x0F0 +#define BKTR_PLL_F_HI 0x0F4 +#define BKTR_PLL_F_XCI 0x0F8 + +/* + * device support for onboard tv tuners + */ + +/* description of the LOGICAL tuner */ +struct TVTUNER { + int frequency; + u_char chnlset; + u_char channel; + u_char band; + u_char afc; + u_char radio_mode; /* current mode of the radio mode */ +}; + +/* description of the PHYSICAL tuner */ +struct TUNER { + char* name; + u_char type; + u_char pllControl[4]; + u_char bandLimits[ 2 ]; + u_char bandAddrs[ 4 ]; /* 3 first for the 3 TV + ** bands. Last for radio + ** band (0x00=NoRadio). + */ + +}; + +/* description of the card */ +#define EEPROMBLOCKSIZE 32 +struct CARDTYPE { + unsigned int card_id; /* card id (from #define's) */ + char* name; + const struct TUNER* tuner; /* Tuner details */ + u_char tuner_pllAddr; /* Tuner i2c address */ + u_char dbx; /* Has DBX chip? */ + u_char msp3400c; /* Has msp3400c chip? */ + u_char dpl3518a; /* Has dpl3518a chip? */ + u_char eepromAddr; + u_char eepromSize; /* bytes / EEPROMBLOCKSIZE */ + u_int audiomuxs[ 5 ]; /* tuner, ext (line-in) */ + /* int/unused (radio) */ + /* mute, present */ + u_int gpio_mux_bits; /* GPIO mask for audio mux */ +}; + +struct format_params { + /* Total lines, lines before image, image lines */ + int vtotal, vdelay, vactive; + /* Total unscaled horizontal pixels, pixels before image, image pixels */ + int htotal, hdelay, hactive; + /* Scaled horizontal image pixels, Total Scaled horizontal pixels */ + int scaled_hactive, scaled_htotal; + /* frame rate . for ntsc is 30 frames per second */ + int frame_rate; + /* A-delay and B-delay */ + u_char adelay, bdelay; + /* Iform XTSEL value */ + int iform_xtsel; + /* VBI number of lines per field, and number of samples per line */ + int vbi_num_lines, vbi_num_samples; +}; + +#if defined(BKTR_USE_FREEBSD_SMBUS) +struct bktr_i2c_softc { + device_t iicbus; + device_t smbus; +}; +#endif + + +/* Bt848/878 register access + * The registers can either be access via a memory mapped structure + * or accessed via bus_space. + * bus_0pace access allows cross platform support, where as the + * memory mapped structure method only works on 32 bit processors + * with the right type of endianness. + */ +#if defined(__NetBSD__) || ( defined(__FreeBSD__) && (__FreeBSD_version >=300000) ) +#define INB(bktr,offset) bus_space_read_1((bktr)->memt,(bktr)->memh,(offset)) +#define INW(bktr,offset) bus_space_read_2((bktr)->memt,(bktr)->memh,(offset)) +#define INL(bktr,offset) bus_space_read_4((bktr)->memt,(bktr)->memh,(offset)) +#define OUTB(bktr,offset,value) bus_space_write_1((bktr)->memt,(bktr)->memh,(offset),(value)) +#define OUTW(bktr,offset,value) bus_space_write_2((bktr)->memt,(bktr)->memh,(offset),(value)) +#define OUTL(bktr,offset,value) bus_space_write_4((bktr)->memt,(bktr)->memh,(offset),(value)) +#else +#define INB(bktr,offset) *(volatile unsigned char*) ((int)((bktr)->memh)+(offset)) +#define INW(bktr,offset) *(volatile unsigned short*)((int)((bktr)->memh)+(offset)) +#define INL(bktr,offset) *(volatile unsigned int*) ((int)((bktr)->memh)+(offset)) +#define OUTB(bktr,offset,value) *(volatile unsigned char*) ((int)((bktr)->memh)+(offset)) = (value) +#define OUTW(bktr,offset,value) *(volatile unsigned short*)((int)((bktr)->memh)+(offset)) = (value) +#define OUTL(bktr,offset,value) *(volatile unsigned int*) ((int)((bktr)->memh)+(offset)) = (value) +#endif + + +typedef struct bktr_clip bktr_clip_t; + +/* + * BrookTree 848 info structure, one per bt848 card installed. + */ +struct bktr_softc { + +#if defined (__bsdi__) + struct device bktr_dev; /* base device */ + struct isadev bktr_id; /* ISA device */ + struct intrhand bktr_ih; /* interrupt vectoring */ + #define pcici_t pci_devaddr_t +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) + struct device bktr_dev; /* base device */ + bus_dma_tag_t dmat; /* DMA tag */ + bus_space_tag_t memt; + bus_space_handle_t memh; + bus_size_t obmemsz; /* size of en card (bytes) */ + void *ih; + bus_dmamap_t dm_prog; + bus_dmamap_t dm_oprog; + bus_dmamap_t dm_mem; + bus_dmamap_t dm_vbidata; + bus_dmamap_t dm_vbibuffer; +#ifdef __OpenBSD__ + size_t dm_mapsize; +#endif +#endif + +#if defined (__FreeBSD__) + #if (__FreeBSD_version < 400000) + vm_offset_t phys_base; /* 2.x Bt848 register physical address */ + pcici_t tag; /* 2.x PCI tag, for doing PCI commands */ + #endif + #if (__FreeBSD_version >= 400000) + int mem_rid; /* 4.x resource id */ + struct resource *res_mem; /* 4.x resource descriptor for registers */ + int irq_rid; /* 4.x resource id */ + struct resource *res_irq; /* 4.x resource descriptor for interrupt */ + void *res_ih; /* 4.x newbus interrupt handler cookie */ + dev_t bktrdev; /* 4.x device entry for /dev/bktrN */ + dev_t tunerdev; /* 4.x device entry for /dev/tunerN */ + dev_t vbidev; /* 4.x device entry for /dev/vbiN */ + dev_t bktrdev_alias; /* alias /dev/bktr to /dev/bktr0 */ + dev_t tunerdev_alias; /* alias /dev/tuner to /dev/tuner0 */ + dev_t vbidev_alias; /* alias /dev/vbi to /dev/vbi0 */ + #endif + #if (__FreeBSD_version >= 310000) + bus_space_tag_t memt; /* Bus space register access functions */ + bus_space_handle_t memh; /* Bus space register access functions */ + bus_size_t obmemsz;/* Size of card (bytes) */ + #endif + #if (NSMBUS > 0) + struct bktr_i2c_softc i2c_sc; /* bt848_i2c device */ + #endif + char bktr_xname[7]; /* device name and unit number */ +#endif + + + /* The following definitions are for the contiguous memory */ +#ifdef __NetBSD__ + vaddr_t bigbuf; /* buffer that holds the captured image */ + vaddr_t vbidata; /* RISC program puts VBI data from the current frame here */ + vaddr_t vbibuffer; /* Circular buffer holding VBI data for the user */ + vaddr_t dma_prog; /* RISC prog for single and/or even field capture*/ + vaddr_t odd_dma_prog; /* RISC program for Odd field capture */ +#else + vm_offset_t bigbuf; /* buffer that holds the captured image */ + vm_offset_t vbidata; /* RISC program puts VBI data from the current frame here */ + vm_offset_t vbibuffer; /* Circular buffer holding VBI data for the user */ + vm_offset_t dma_prog; /* RISC prog for single and/or even field capture*/ + vm_offset_t odd_dma_prog;/* RISC program for Odd field capture */ +#endif + + + /* the following definitions are common over all platforms */ + int alloc_pages; /* number of pages in bigbuf */ + int vbiinsert; /* Position for next write into circular buffer */ + int vbistart; /* Position of last read from circular buffer */ + int vbisize; /* Number of bytes in the circular buffer */ + u_long vbi_sequence_number; /* sequence number for VBI */ + int vbi_read_blocked; /* user process blocked on read() from /dev/vbi */ + struct selinfo vbi_select; /* Data used by select() on /dev/vbi */ + + + struct proc *proc; /* process to receive raised signal */ + int signal; /* signal to send to process */ + int clr_on_start; /* clear cap buf on capture start? */ +#define METEOR_SIG_MODE_MASK 0xffff0000 +#define METEOR_SIG_FIELD_MODE 0x00010000 +#define METEOR_SIG_FRAME_MODE 0x00000000 + char dma_prog_loaded; + struct meteor_mem *mem; /* used to control sync. multi-frame output */ + u_long synch_wait; /* wait for free buffer before continuing */ + short current; /* frame number in buffer (1-frames) */ + short rows; /* number of rows in a frame */ + short cols; /* number of columns in a frame */ + int capture_area_x_offset; /* Usually the full 640x480(NTSC) image is */ + int capture_area_y_offset; /* captured. The capture area allows for */ + int capture_area_x_size; /* example 320x200 pixels from the centre */ + int capture_area_y_size; /* of the video image to be captured. */ + char capture_area_enabled; /* When TRUE use user's capture area. */ + int pixfmt; /* active pixel format (idx into fmt tbl) */ + int pixfmt_compat; /* Y/N - in meteor pix fmt compat mode */ + u_long format; /* frame format rgb, yuv, etc.. */ + short frames; /* number of frames allocated */ + int frame_size; /* number of bytes in a frame */ + u_long fifo_errors; /* number of fifo capture errors since open */ + u_long dma_errors; /* number of DMA capture errors since open */ + u_long frames_captured;/* number of frames captured since open */ + u_long even_fields_captured; /* number of even fields captured */ + u_long odd_fields_captured; /* number of odd fields captured */ + u_long range_enable; /* enable range checking ?? */ + u_short capcontrol; /* reg 0xdc capture control */ + u_short bktr_cap_ctl; + volatile u_int flags; +#define METEOR_INITALIZED 0x00000001 +#define METEOR_OPEN 0x00000002 +#define METEOR_MMAP 0x00000004 +#define METEOR_INTR 0x00000008 +#define METEOR_READ 0x00000010 /* XXX never gets referenced */ +#define METEOR_SINGLE 0x00000020 /* get single frame */ +#define METEOR_CONTIN 0x00000040 /* continuously get frames */ +#define METEOR_SYNCAP 0x00000080 /* synchronously get frames */ +#define METEOR_CAP_MASK 0x000000f0 +#define METEOR_NTSC 0x00000100 +#define METEOR_PAL 0x00000200 +#define METEOR_SECAM 0x00000400 +#define BROOKTREE_NTSC 0x00000100 /* used in video open() and */ +#define BROOKTREE_PAL 0x00000200 /* in the kernel config */ +#define BROOKTREE_SECAM 0x00000400 /* file */ +#define METEOR_AUTOMODE 0x00000800 +#define METEOR_FORM_MASK 0x00000f00 +#define METEOR_DEV0 0x00001000 +#define METEOR_DEV1 0x00002000 +#define METEOR_DEV2 0x00004000 +#define METEOR_DEV3 0x00008000 +#define METEOR_DEV_SVIDEO 0x00006000 +#define METEOR_DEV_RGB 0x0000a000 +#define METEOR_DEV_MASK 0x0000f000 +#define METEOR_RGB16 0x00010000 +#define METEOR_RGB24 0x00020000 +#define METEOR_YUV_PACKED 0x00040000 +#define METEOR_YUV_PLANAR 0x00080000 +#define METEOR_WANT_EVEN 0x00100000 /* want even frame */ +#define METEOR_WANT_ODD 0x00200000 /* want odd frame */ +#define METEOR_WANT_MASK 0x00300000 +#define METEOR_ONLY_EVEN_FIELDS 0x01000000 +#define METEOR_ONLY_ODD_FIELDS 0x02000000 +#define METEOR_ONLY_FIELDS_MASK 0x03000000 +#define METEOR_YUV_422 0x04000000 +#define METEOR_OUTPUT_FMT_MASK 0x040f0000 +#define METEOR_WANT_TS 0x08000000 /* time-stamp a frame */ +#define METEOR_RGB 0x20000000 /* meteor rgb unit */ +#define METEOR_FIELD_MODE 0x80000000 + u_char tflags; /* Tuner flags (/dev/tuner) */ +#define TUNER_INITALIZED 0x00000001 +#define TUNER_OPEN 0x00000002 + u_char vbiflags; /* VBI flags (/dev/vbi) */ +#define VBI_INITALIZED 0x00000001 +#define VBI_OPEN 0x00000002 +#define VBI_CAPTURE 0x00000004 + u_short fps; /* frames per second */ + struct meteor_video video; + struct TVTUNER tuner; + struct CARDTYPE card; + u_char audio_mux_select; /* current mode of the audio */ + u_char audio_mute_state; /* mute state of the audio */ + u_char format_params; + u_long current_sol; + u_long current_col; + int clip_start; + int line_length; + int last_y; + int y; + int y2; + int yclip; + int yclip2; + int max_clip_node; + bktr_clip_t clip_list[100]; + int reverse_mute; /* Swap the GPIO values for Mute and TV Audio */ + int bt848_tuner; + int bt848_card; + u_long id; +#define BT848_USE_XTALS 0 +#define BT848_USE_PLL 1 + int xtal_pll_mode; /* Use XTAL or PLL mode for PAL/SECAM */ + int remote_control; /* remote control detected */ + int remote_control_addr; /* remote control i2c address */ + char msp_version_string[9]; /* MSP version string 34xxx-xx */ + int msp_addr; /* MSP i2c address */ + char dpl_version_string[9]; /* DPL version string 35xxx-xx */ + int dpl_addr; /* DPL i2c address */ + int slow_msp_audio; /* 0 = use fast MSP3410/3415 programming sequence */ + /* 1 = use slow MSP3410/3415 programming sequence */ + /* 2 = use Tuner's Mono audio output via the MSP chip */ + int msp_use_mono_source; /* use Tuner's Mono audio output via the MSP chip */ + int audio_mux_present; /* 1 = has audio mux on GPIO lines, 0 = no audio mux */ + int msp_source_selected; /* 0 = TV source, 1 = Line In source, 2 = FM Radio Source */ + +}; + +typedef struct bktr_softc bktr_reg_t; +typedef struct bktr_softc* bktr_ptr_t; + +#define Bt848_MAX_SIGN 16 + +struct bt848_card_sig { + int card; + int tuner; + u_char signature[Bt848_MAX_SIGN]; +}; + + +/***********************************************************/ +/* ioctl_cmd_t int on old versions, u_long on new versions */ +/***********************************************************/ + +#if (__FreeBSD__ == 2) +typedef int ioctl_cmd_t; +#endif + +#if defined(__FreeBSD__) +#if (__FreeBSD_version >= 300000) +typedef u_long ioctl_cmd_t; +#endif +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +typedef u_long ioctl_cmd_t; +#endif + + diff --git a/sys/dev/pci/bktr/bktr_tuner.c b/sys/dev/pci/bktr/bktr_tuner.c new file mode 100644 index 00000000000..e4b2fd3c998 --- /dev/null +++ b/sys/dev/pci/bktr/bktr_tuner.c @@ -0,0 +1,1017 @@ +/* $OpenBSD: bktr_tuner.c,v 1.1 2001/03/28 03:27:10 fgsch Exp $ */ +/* $FreeBSD: src/sys/dev/bktr/bktr_tuner.c,v 1.9 2000/10/19 07:33:28 roger Exp $ */ + +/* + * This is part of the Driver for Video Capture Cards (Frame grabbers) + * and TV Tuner cards using the Brooktree Bt848, Bt848A, Bt849A, Bt878, Bt879 + * chipset. + * Copyright Roger Hardiman and Amancio Hasty. + * + * bktr_tuner : This deals with controlling the tuner fitted to TV cards. + * + */ + +/* + * 1. Redistributions of source code must retain the + * Copyright (c) 1997 Amancio Hasty, 1999 Roger Hardiman + * 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 and + * Roger Hardiman + * 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. + */ + + + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/vnode.h> +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <sys/proc.h> +#endif + +#ifdef __FreeBSD__ +#include <pci/pcivar.h> + +#if (__FreeBSD_version < 500000) +#include <machine/clock.h> /* for DELAY */ +#endif + +#if (__FreeBSD_version >=300000) +#include <machine/bus_memio.h> /* for bus space */ +#include <machine/bus.h> +#include <sys/bus.h> +#endif +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <dev/ic/bt8xx.h> /* NetBSD .h file location */ +#include <dev/pci/bktr/bktr_reg.h> +#include <dev/pci/bktr/bktr_tuner.h> +#include <dev/pci/bktr/bktr_card.h> +#include <dev/pci/bktr/bktr_core.h> +#else +#include <machine/ioctl_meteor.h> /* Traditional .h file location */ +#include <machine/ioctl_bt848.h> /* extensions to ioctl_meteor.h */ +#include <dev/bktr/bktr_reg.h> +#include <dev/bktr/bktr_tuner.h> +#include <dev/bktr/bktr_card.h> +#include <dev/bktr/bktr_core.h> +#endif + + + +#if defined( TUNER_AFC ) +#define AFC_DELAY 10000 /* 10 millisend delay */ +#define AFC_BITS 0x07 +#define AFC_FREQ_MINUS_125 0x00 +#define AFC_FREQ_MINUS_62 0x01 +#define AFC_FREQ_CENTERED 0x02 +#define AFC_FREQ_PLUS_62 0x03 +#define AFC_FREQ_PLUS_125 0x04 +#define AFC_MAX_STEP (5 * FREQFACTOR) /* no more than 5 MHz */ +#endif /* TUNER_AFC */ + + +#define TTYPE_XXX 0 +#define TTYPE_NTSC 1 +#define TTYPE_NTSC_J 2 +#define TTYPE_PAL 3 +#define TTYPE_PAL_M 4 +#define TTYPE_PAL_N 5 +#define TTYPE_SECAM 6 + +#define TSA552x_CB_MSB (0x80) +#define TSA552x_CB_CP (1<<6) /* set this for fast tuning */ +#define TSA552x_CB_T2 (1<<5) /* test mode - Normally set to 0 */ +#define TSA552x_CB_T1 (1<<4) /* test mode - Normally set to 0 */ +#define TSA552x_CB_T0 (1<<3) /* test mode - Normally set to 1 */ +#define TSA552x_CB_RSA (1<<2) /* 0 for 31.25 khz, 1 for 62.5 kHz */ +#define TSA552x_CB_RSB (1<<1) /* 0 for FM 50kHz steps, 1 = Use RSA*/ +#define TSA552x_CB_OS (1<<0) /* Set to 0 for normal operation */ + +#define TSA552x_RADIO (TSA552x_CB_MSB | \ + TSA552x_CB_T0) + +/* raise the charge pump voltage for fast tuning */ +#define TSA552x_FCONTROL (TSA552x_CB_MSB | \ + TSA552x_CB_CP | \ + TSA552x_CB_T0 | \ + TSA552x_CB_RSA | \ + TSA552x_CB_RSB) + +/* lower the charge pump voltage for better residual oscillator FM */ +#define TSA552x_SCONTROL (TSA552x_CB_MSB | \ + TSA552x_CB_T0 | \ + TSA552x_CB_RSA | \ + TSA552x_CB_RSB) + +/* The control value for the ALPS TSCH5 Tuner */ +#define TSCH5_FCONTROL 0x82 +#define TSCH5_RADIO 0x86 + +/* The control value for the ALPS TSBH1 Tuner */ +#define TSBH1_FCONTROL 0xce + + +static const struct TUNER tuners[] = { +/* XXX FIXME: fill in the band-switch crosspoints */ + /* NO_TUNER */ + { "<no>", /* the 'name' */ + TTYPE_XXX, /* input type */ + { 0x00, /* control byte for Tuner PLL */ + 0x00, + 0x00, + 0x00 }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0x00, 0x00, 0x00,0x00} }, /* the band-switch values */ + + /* TEMIC_NTSC */ + { "Temic NTSC", /* the 'name' */ + TTYPE_NTSC, /* input type */ + { TSA552x_SCONTROL, /* control byte for Tuner PLL */ + TSA552x_SCONTROL, + TSA552x_SCONTROL, + 0x00 }, + { 0x00, 0x00}, /* band-switch crosspoints */ + { 0x02, 0x04, 0x01, 0x00 } }, /* the band-switch values */ + + /* TEMIC_PAL */ + { "Temic PAL", /* the 'name' */ + TTYPE_PAL, /* input type */ + { TSA552x_SCONTROL, /* control byte for Tuner PLL */ + TSA552x_SCONTROL, + TSA552x_SCONTROL, + 0x00 }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0x02, 0x04, 0x01, 0x00 } }, /* the band-switch values */ + + /* TEMIC_SECAM */ + { "Temic SECAM", /* the 'name' */ + TTYPE_SECAM, /* input type */ + { TSA552x_SCONTROL, /* control byte for Tuner PLL */ + TSA552x_SCONTROL, + TSA552x_SCONTROL, + 0x00 }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0x02, 0x04, 0x01,0x00 } }, /* the band-switch values */ + + /* PHILIPS_NTSC */ + { "Philips NTSC", /* the 'name' */ + TTYPE_NTSC, /* input type */ + { TSA552x_SCONTROL, /* control byte for Tuner PLL */ + TSA552x_SCONTROL, + TSA552x_SCONTROL, + 0x00 }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0xa0, 0x90, 0x30, 0x00 } }, /* the band-switch values */ + + /* PHILIPS_PAL */ + { "Philips PAL", /* the 'name' */ + TTYPE_PAL, /* input type */ + { TSA552x_SCONTROL, /* control byte for Tuner PLL */ + TSA552x_SCONTROL, + TSA552x_SCONTROL, + 0x00 }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0xa0, 0x90, 0x30, 0x00 } }, /* the band-switch values */ + + /* PHILIPS_SECAM */ + { "Philips SECAM", /* the 'name' */ + TTYPE_SECAM, /* input type */ + { TSA552x_SCONTROL, /* control byte for Tuner PLL */ + TSA552x_SCONTROL, + TSA552x_SCONTROL, + 0x00 }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0xa7, 0x97, 0x37, 0x00 } }, /* the band-switch values */ + + /* TEMIC_PAL I */ + { "Temic PAL I", /* the 'name' */ + TTYPE_PAL, /* input type */ + { TSA552x_SCONTROL, /* control byte for Tuner PLL */ + TSA552x_SCONTROL, + TSA552x_SCONTROL, + 0x00 }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0x02, 0x04, 0x01,0x00 } }, /* the band-switch values */ + + /* PHILIPS_PALI */ + { "Philips PAL I", /* the 'name' */ + TTYPE_PAL, /* input type */ + { TSA552x_SCONTROL, /* control byte for Tuner PLL */ + TSA552x_SCONTROL, + TSA552x_SCONTROL, + 0x00 }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0xa0, 0x90, 0x30,0x00 } }, /* the band-switch values */ + + /* PHILIPS_FR1236_NTSC */ + { "Philips FR1236 NTSC FM", /* the 'name' */ + TTYPE_NTSC, /* input type */ + { TSA552x_FCONTROL, /* control byte for Tuner PLL */ + TSA552x_FCONTROL, + TSA552x_FCONTROL, + TSA552x_RADIO }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0xa0, 0x90, 0x30,0xa4 } }, /* the band-switch values */ + + /* PHILIPS_FR1216_PAL */ + { "Philips FR1216 PAL FM" , /* the 'name' */ + TTYPE_PAL, /* input type */ + { TSA552x_FCONTROL, /* control byte for Tuner PLL */ + TSA552x_FCONTROL, + TSA552x_FCONTROL, + TSA552x_RADIO }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0xa0, 0x90, 0x30, 0xa4 } }, /* the band-switch values */ + + /* PHILIPS_FR1236_SECAM */ + { "Philips FR1236 SECAM FM", /* the 'name' */ + TTYPE_SECAM, /* input type */ + { TSA552x_FCONTROL, /* control byte for Tuner PLL */ + TSA552x_FCONTROL, + TSA552x_FCONTROL, + TSA552x_RADIO }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0xa7, 0x97, 0x37, 0xa4 } }, /* the band-switch values */ + + /* ALPS TSCH5 NTSC */ + { "ALPS TSCH5 NTSC FM", /* the 'name' */ + TTYPE_NTSC, /* input type */ + { TSCH5_FCONTROL, /* control byte for Tuner PLL */ + TSCH5_FCONTROL, + TSCH5_FCONTROL, + TSCH5_RADIO }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0x14, 0x12, 0x11, 0x04 } }, /* the band-switch values */ + + /* ALPS TSBH1 NTSC */ + { "ALPS TSBH1 NTSC", /* the 'name' */ + TTYPE_NTSC, /* input type */ + { TSBH1_FCONTROL, /* control byte for Tuner PLL */ + TSBH1_FCONTROL, + TSBH1_FCONTROL, + 0x00 }, + { 0x00, 0x00 }, /* band-switch crosspoints */ + { 0x01, 0x02, 0x08, 0x00 } } /* the band-switch values */ +}; + + +/* 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 +static 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 +static int irccable[] = { + 116, (int)( 45.75 * FREQFACTOR), 0, + 100, (int)(649.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 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: + * + * 2: 54 mHz - 4: 66 mHz + * 5: 78 mHz - 6: 84 mHz + * 7: 174 mHz - 13: 210 mHz + * 14: 120 mHz - 22: 168 mHz + * 23: 216 mHz - 94: 642 mHz + * 95: 90 mHz - 99: 114 mHz + * + * IF freq: 45.75 mHz + */ +#define OFFSET 6.00 +static int hrccable[] = { + 116, (int)( 45.75 * FREQFACTOR), 0, + 100, (int)(648.00 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 95, (int)( 90.00 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 23, (int)(216.00 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 14, (int)(120.00 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 7, (int)(174.00 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 5, (int)( 78.00 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 2, (int)( 54.00 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 0 +}; +#undef OFFSET + +/* + * Western European broadcast channels: + * + * (there are others that appear to vary between countries - rmt) + * + * here's the table Philips provides: + * caution, some of the offsets don't compute... + * + * 1 4525 700 N21 + * + * 2 4825 700 E2 + * 3 5525 700 E3 + * 4 6225 700 E4 + * + * 5 17525 700 E5 + * 6 18225 700 E6 + * 7 18925 700 E7 + * 8 19625 700 E8 + * 9 20325 700 E9 + * 10 21025 700 E10 + * 11 21725 700 E11 + * 12 22425 700 E12 + * + * 13 5375 700 ITA + * 14 6225 700 ITB + * + * 15 8225 700 ITC + * + * 16 17525 700 ITD + * 17 18325 700 ITE + * + * 18 19225 700 ITF + * 19 20125 700 ITG + * 20 21025 700 ITH + * + * 21 47125 800 E21 + * 22 47925 800 E22 + * 23 48725 800 E23 + * 24 49525 800 E24 + * 25 50325 800 E25 + * 26 51125 800 E26 + * 27 51925 800 E27 + * 28 52725 800 E28 + * 29 53525 800 E29 + * 30 54325 800 E30 + * 31 55125 800 E31 + * 32 55925 800 E32 + * 33 56725 800 E33 + * 34 57525 800 E34 + * 35 58325 800 E35 + * 36 59125 800 E36 + * 37 59925 800 E37 + * 38 60725 800 E38 + * 39 61525 800 E39 + * 40 62325 800 E40 + * 41 63125 800 E41 + * 42 63925 800 E42 + * 43 64725 800 E43 + * 44 65525 800 E44 + * 45 66325 800 E45 + * 46 67125 800 E46 + * 47 67925 800 E47 + * 48 68725 800 E48 + * 49 69525 800 E49 + * 50 70325 800 E50 + * 51 71125 800 E51 + * 52 71925 800 E52 + * 53 72725 800 E53 + * 54 73525 800 E54 + * 55 74325 800 E55 + * 56 75125 800 E56 + * 57 75925 800 E57 + * 58 76725 800 E58 + * 59 77525 800 E59 + * 60 78325 800 E60 + * 61 79125 800 E61 + * 62 79925 800 E62 + * 63 80725 800 E63 + * 64 81525 800 E64 + * 65 82325 800 E65 + * 66 83125 800 E66 + * 67 83925 800 E67 + * 68 84725 800 E68 + * 69 85525 800 E69 + * + * 70 4575 800 IA + * 71 5375 800 IB + * 72 6175 800 IC + * + * 74 6925 700 S01 + * 75 7625 700 S02 + * 76 8325 700 S03 + * + * 80 10525 700 S1 + * 81 11225 700 S2 + * 82 11925 700 S3 + * 83 12625 700 S4 + * 84 13325 700 S5 + * 85 14025 700 S6 + * 86 14725 700 S7 + * 87 15425 700 S8 + * 88 16125 700 S9 + * 89 16825 700 S10 + * 90 23125 700 S11 + * 91 23825 700 S12 + * 92 24525 700 S13 + * 93 25225 700 S14 + * 94 25925 700 S15 + * 95 26625 700 S16 + * 96 27325 700 S17 + * 97 28025 700 S18 + * 98 28725 700 S19 + * 99 29425 700 S20 + * + * + * Channels S21 - S41 are taken from + * http://gemma.apple.com:80/dev/technotes/tn/tn1012.html + * + * 100 30325 800 S21 + * 101 31125 800 S22 + * 102 31925 800 S23 + * 103 32725 800 S24 + * 104 33525 800 S25 + * 105 34325 800 S26 + * 106 35125 800 S27 + * 107 35925 800 S28 + * 108 36725 800 S29 + * 109 37525 800 S30 + * 110 38325 800 S31 + * 111 39125 800 S32 + * 112 39925 800 S33 + * 113 40725 800 S34 + * 114 41525 800 S35 + * 115 42325 800 S36 + * 116 43125 800 S37 + * 117 43925 800 S38 + * 118 44725 800 S39 + * 119 45525 800 S40 + * 120 46325 800 S41 + * + * 121 3890 000 IFFREQ + * + */ +static int weurope[] = { + 121, (int)( 38.90 * FREQFACTOR), 0, + 100, (int)(303.25 * FREQFACTOR), (int)(8.00 * FREQFACTOR), + 90, (int)(231.25 * FREQFACTOR), (int)(7.00 * FREQFACTOR), + 80, (int)(105.25 * FREQFACTOR), (int)(7.00 * FREQFACTOR), + 74, (int)( 69.25 * FREQFACTOR), (int)(7.00 * FREQFACTOR), + 21, (int)(471.25 * FREQFACTOR), (int)(8.00 * FREQFACTOR), + 17, (int)(183.25 * FREQFACTOR), (int)(9.00 * FREQFACTOR), + 16, (int)(175.25 * FREQFACTOR), (int)(9.00 * FREQFACTOR), + 15, (int)(82.25 * FREQFACTOR), (int)(8.50 * FREQFACTOR), + 13, (int)(53.75 * FREQFACTOR), (int)(8.50 * FREQFACTOR), + 5, (int)(175.25 * FREQFACTOR), (int)(7.00 * FREQFACTOR), + 2, (int)(48.25 * FREQFACTOR), (int)(7.00 * FREQFACTOR), + 0 +}; + +/* + * Japanese Broadcast Channels: + * + * 1: 91.25MHz - 3: 103.25MHz + * 4: 171.25MHz - 7: 189.25MHz + * 8: 193.25MHz - 12: 217.25MHz (VHF) + * 13: 471.25MHz - 62: 765.25MHz (UHF) + * + * IF freq: 45.75 mHz + * OR + * IF freq: 58.75 mHz + */ +#define OFFSET 6.00 +#define IF_FREQ 45.75 +static int jpnbcst[] = { + 62, (int)(IF_FREQ * FREQFACTOR), 0, + 13, (int)(471.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 8, (int)(193.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 4, (int)(171.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 1, (int)( 91.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 0 +}; +#undef IF_FREQ +#undef OFFSET + +/* + * Japanese Cable Channels: + * + * 1: 91.25MHz - 3: 103.25MHz + * 4: 171.25MHz - 7: 189.25MHz + * 8: 193.25MHz - 12: 217.25MHz + * 13: 109.25MHz - 21: 157.25MHz + * 22: 165.25MHz + * 23: 223.25MHz - 63: 463.25MHz + * + * IF freq: 45.75 mHz + */ +#define OFFSET 6.00 +#define IF_FREQ 45.75 +static int jpncable[] = { + 63, (int)(IF_FREQ * FREQFACTOR), 0, + 23, (int)(223.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 22, (int)(165.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 13, (int)(109.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 8, (int)(193.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 4, (int)(171.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 1, (int)( 91.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 0 +}; +#undef IF_FREQ +#undef OFFSET + +/* + * xUSSR Broadcast Channels: + * + * 1: 49.75MHz - 2: 59.25MHz + * 3: 77.25MHz - 5: 93.25MHz + * 6: 175.25MHz - 12: 223.25MHz + * 13-20 - not exist + * 21: 471.25MHz - 34: 575.25MHz + * 35: 583.25MHz - 69: 855.25MHz + * + * Cable channels + * + * 70: 111.25MHz - 77: 167.25MHz + * 78: 231.25MHz -107: 463.25MHz + * + * IF freq: 38.90 MHz + */ +#define IF_FREQ 38.90 +static int xussr[] = { + 107, (int)(IF_FREQ * FREQFACTOR), 0, + 78, (int)(231.25 * FREQFACTOR), (int)(8.00 * FREQFACTOR), + 70, (int)(111.25 * FREQFACTOR), (int)(8.00 * FREQFACTOR), + 35, (int)(583.25 * FREQFACTOR), (int)(8.00 * FREQFACTOR), + 21, (int)(471.25 * FREQFACTOR), (int)(8.00 * FREQFACTOR), + 6, (int)(175.25 * FREQFACTOR), (int)(8.00 * FREQFACTOR), + 3, (int)( 77.25 * FREQFACTOR), (int)(8.00 * FREQFACTOR), + 1, (int)( 49.75 * FREQFACTOR), (int)(9.50 * FREQFACTOR), + 0 +}; +#undef IF_FREQ + +/* + * Australian broadcast channels + */ +#define OFFSET 7.00 +#define IF_FREQ 38.90 +static int australia[] = { + 83, (int)(IF_FREQ * FREQFACTOR), 0, + 28, (int)(527.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 10, (int)(209.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 6, (int)(175.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 4, (int)( 95.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 3, (int)( 86.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 1, (int)( 57.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), + 0 +}; +#undef OFFSET +#undef IF_FREQ + +/* + * France broadcast channels + */ +#define OFFSET 8.00 +#define IF_FREQ 38.90 +static int france[] = { + 69, (int)(IF_FREQ * FREQFACTOR), 0, + 21, (int)(471.25 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), /* 21 -> 69 */ + 5, (int)(176.00 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), /* 5 -> 10 */ + 4, (int)( 63.75 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), /* 4 */ + 3, (int)( 60.50 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), /* 3 */ + 1, (int)( 47.75 * FREQFACTOR), (int)(OFFSET * FREQFACTOR), /* 1 2 */ + 0 +}; +#undef OFFSET +#undef IF_FREQ + +static struct { + int *ptr; + char name[BT848_MAX_CHNLSET_NAME_LEN]; +} freqTable[] = { + {NULL, ""}, + {nabcst, "nabcst"}, + {irccable, "cableirc"}, + {hrccable, "cablehrc"}, + {weurope, "weurope"}, + {jpnbcst, "jpnbcst"}, + {jpncable, "jpncable"}, + {xussr, "xussr"}, + {australia, "australia"}, + {france, "france"}, + +}; + +#define TBL_CHNL freqTable[ bktr->tuner.chnlset ].ptr[ x ] +#define TBL_BASE_FREQ freqTable[ bktr->tuner.chnlset ].ptr[ x + 1 ] +#define TBL_OFFSET freqTable[ bktr->tuner.chnlset ].ptr[ x + 2 ] +static int +frequency_lookup( bktr_ptr_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 ].ptr[ 1 ] + + +/* Initialise the tuner structures in the bktr_softc */ +/* This is needed as the tuner details are no longer globally declared */ + +void select_tuner( bktr_ptr_t bktr, int tuner_type ) { + if (tuner_type < Bt848_MAX_TUNER) { + bktr->card.tuner = &tuners[ tuner_type ]; + } else { + bktr->card.tuner = NULL; + } +} + +/* + * Tuner Notes: + * Programming the tuner properly is quite complicated. + * Here are some notes, based on a FM1246 data sheet for a PAL-I tuner. + * The tuner (front end) covers 45.75 Mhz - 855.25 Mhz and an FM band of + * 87.5 Mhz to 108.0 Mhz. + * + * RF and IF. RF = radio frequencies, it is the transmitted signal. + * IF is the Intermediate Frequency (the offset from the base + * signal where the video, color, audio and NICAM signals are. + * + * Eg, Picture at 38.9 Mhz, Colour at 34.47 MHz, sound at 32.9 MHz + * NICAM at 32.348 Mhz. + * Strangely enough, there is an IF (intermediate frequency) for + * FM Radio which is 10.7 Mhz. + * + * The tuner also works in Bands. Philips bands are + * FM radio band 87.50 to 108.00 MHz + * Low band 45.75 to 170.00 MHz + * Mid band 170.00 to 450.00 MHz + * High band 450.00 to 855.25 MHz + * + * + * Now we need to set the PLL on the tuner to the required freuqncy. + * It has a programmable divisor. + * For TV we want + * N = 16 (freq RF(pc) + freq IF(pc)) pc is picture carrier and RF and IF + * are in MHz. + + * For RADIO we want a different equation. + * freq IF is 10.70 MHz (so the data sheet tells me) + * N = (freq RF + freq IF) / step size + * The step size must be set to 50 khz (so the data sheet tells me) + * (note this is 50 kHz, the other things are in MHz) + * so we end up with N = 20x(freq RF + 10.7) + * + */ + +#define LOW_BAND 0 +#define MID_BAND 1 +#define HIGH_BAND 2 +#define FM_RADIO_BAND 3 + + +/* Check if these are correct for other than Philips PAL */ +#define STATUSBIT_COLD 0x80 +#define STATUSBIT_LOCK 0x40 +#define STATUSBIT_TV 0x20 +#define STATUSBIT_STEREO 0x10 /* valid if FM (aka not TV) */ +#define STATUSBIT_ADC 0x07 + +/* + * set the frequency of the tuner + * If 'type' is TV_FREQUENCY, the frequency is freq MHz*16 + * If 'type' is FM_RADIO_FREQUENCY, the frequency is freq MHz * 100 + * (note *16 gives is 4 bits of fraction, eg steps of nnn.0625) + * + */ +int +tv_freq( bktr_ptr_t bktr, int frequency, int type ) +{ + const struct TUNER* tuner; + u_char addr; + u_char control; + u_char band; + int N; + int band_select = 0; +#if defined( TEST_TUNER_AFC ) + int oldFrequency, afcDelta; +#endif + + tuner = bktr->card.tuner; + if ( tuner == NULL ) + return( -1 ); + + if (type == TV_FREQUENCY) { + /* + * select the band based on frequency + * XXX FIXME: get the cross-over points from the tuner struct + */ + if ( frequency < (160 * FREQFACTOR ) ) + band_select = LOW_BAND; + else if ( frequency < (454 * FREQFACTOR ) ) + band_select = MID_BAND; + else + band_select = HIGH_BAND; + +#if defined( TEST_TUNER_AFC ) + if ( bktr->tuner.afc ) + frequency -= 4; +#endif + /* + * N = 16 * { fRF(pc) + fIF(pc) } + * or N = 16* fRF(pc) + 16*fIF(pc) } + * where: + * pc is picture carrier, fRF & fIF are in MHz + * + * fortunatly, frequency is passed in as MHz * 16 + * and the TBL_IF frequency is also stored in MHz * 16 + */ + N = frequency + TBL_IF; + + /* set the address of the PLL */ + addr = bktr->card.tuner_pllAddr; + control = tuner->pllControl[ band_select ]; + band = tuner->bandAddrs[ band_select ]; + + if(!(band && control)) /* Don't try to set un- */ + return(-1); /* supported modes. */ + + if ( frequency > bktr->tuner.frequency ) { + i2cWrite( bktr, addr, (N>>8) & 0x7f, N & 0xff ); + i2cWrite( bktr, addr, control, band ); + } + else { + i2cWrite( bktr, addr, control, band ); + i2cWrite( bktr, addr, (N>>8) & 0x7f, N & 0xff ); + } + +#if defined( TUNER_AFC ) + if ( bktr->tuner.afc == TRUE ) { +#if defined( TEST_TUNER_AFC ) + oldFrequency = frequency; +#endif + if ( (N = do_afc( bktr, addr, N )) < 0 ) { + /* AFC failed, restore requested frequency */ + N = frequency + TBL_IF; +#if defined( TEST_TUNER_AFC ) + printf("%s: do_afc: failed to lock\n", + bktr_name(bktr)); +#endif + i2cWrite( bktr, addr, (N>>8) & 0x7f, N & 0xff ); + } + else + frequency = N - TBL_IF; +#if defined( TEST_TUNER_AFC ) + printf("%s: do_afc: returned freq %d (%d %% %d)\n", bktr_name(bktr), frequency, frequency / 16, frequency % 16); + afcDelta = frequency - oldFrequency; + printf("%s: changed by: %d clicks (%d mod %d)\n", bktr_name(bktr), afcDelta, afcDelta / 16, afcDelta % 16); +#endif + } +#endif /* TUNER_AFC */ + + bktr->tuner.frequency = frequency; + } + + if ( type == FM_RADIO_FREQUENCY ) { + band_select = FM_RADIO_BAND; + + /* + * N = { fRF(pc) + fIF(pc) }/step_size + * The step size is 50kHz for FM radio. + * (eg after 102.35MHz comes 102.40 MHz) + * fIF is 10.7 MHz (as detailed in the specs) + * + * frequency is passed in as MHz * 100 + * + * So, we have N = (frequency/100 + 10.70) /(50/1000) + */ + N = (frequency + 1070)/5; + + /* set the address of the PLL */ + addr = bktr->card.tuner_pllAddr; + control = tuner->pllControl[ band_select ]; + band = tuner->bandAddrs[ band_select ]; + + if(!(band && control)) /* Don't try to set un- */ + return(-1); /* supported modes. */ + + band |= bktr->tuner.radio_mode; /* tuner.radio_mode is set in + * the ioctls RADIO_SETMODE + * and RADIO_GETMODE */ + + i2cWrite( bktr, addr, control, band ); + i2cWrite( bktr, addr, (N>>8) & 0x7f, N & 0xff ); + + bktr->tuner.frequency = (N * 5) - 1070; + + + } + + + return( 0 ); +} + + + +#if defined( TUNER_AFC ) +/* + * + */ +int +do_afc( bktr_ptr_t bktr, int addr, int frequency ) +{ + int step; + int status; + int origFrequency; + + origFrequency = frequency; + + /* wait for first setting to take effect */ + tsleep( BKTR_SLEEP, PZERO, "tuning", hz/8 ); + + if ( (status = i2cRead( bktr, addr + 1 )) < 0 ) + return( -1 ); + +#if defined( TEST_TUNER_AFC ) + printf( "%s: Original freq: %d, status: 0x%02x\n", bktr_name(bktr), frequency, status ); +#endif + for ( step = 0; step < AFC_MAX_STEP; ++step ) { + if ( (status = i2cRead( bktr, addr + 1 )) < 0 ) + goto fubar; + if ( !(status & 0x40) ) { +#if defined( TEST_TUNER_AFC ) + printf( "%s: no lock!\n", bktr_name(bktr) ); +#endif + goto fubar; + } + + switch( status & AFC_BITS ) { + case AFC_FREQ_CENTERED: +#if defined( TEST_TUNER_AFC ) + printf( "%s: Centered, freq: %d, status: 0x%02x\n", bktr_name(bktr), frequency, status ); +#endif + return( frequency ); + + case AFC_FREQ_MINUS_125: + case AFC_FREQ_MINUS_62: +#if defined( TEST_TUNER_AFC ) + printf( "%s: Low, freq: %d, status: 0x%02x\n", bktr_name(bktr), frequency, status ); +#endif + --frequency; + break; + + case AFC_FREQ_PLUS_62: + case AFC_FREQ_PLUS_125: +#if defined( TEST_TUNER_AFC ) + printf( "%s: Hi, freq: %d, status: 0x%02x\n", bktr_name(bktr), frequency, status ); +#endif + ++frequency; + break; + } + + i2cWrite( bktr, addr, + (frequency>>8) & 0x7f, frequency & 0xff ); + DELAY( AFC_DELAY ); + } + + fubar: + i2cWrite( bktr, addr, + (origFrequency>>8) & 0x7f, origFrequency & 0xff ); + + return( -1 ); +} +#endif /* TUNER_AFC */ +#undef TBL_IF + + +/* + * Get the Tuner status and signal strength + */ +int get_tuner_status( bktr_ptr_t bktr ) { + return i2cRead( bktr, bktr->card.tuner_pllAddr + 1 ); +} + +/* + * set the channel of the tuner + */ +int +tv_channel( bktr_ptr_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, TV_FREQUENCY ) < 0 ) + return( -1 ); + + /* OK to update records */ + return( (bktr->tuner.channel = channel) ); +} + +/* + * get channelset name + */ +int +tuner_getchnlset(struct bktr_chnlset *chnlset) +{ + if (( chnlset->index < CHNLSET_MIN ) || + ( chnlset->index > CHNLSET_MAX )) + return( EINVAL ); + + memcpy(&chnlset->name, &freqTable[chnlset->index].name, + BT848_MAX_CHNLSET_NAME_LEN); + + chnlset->max_channel=freqTable[chnlset->index].ptr[0]; + return( 0 ); +} diff --git a/sys/dev/pci/bktr/bktr_tuner.h b/sys/dev/pci/bktr/bktr_tuner.h new file mode 100644 index 00000000000..5ca817d26e7 --- /dev/null +++ b/sys/dev/pci/bktr/bktr_tuner.h @@ -0,0 +1,104 @@ +/* $OpenBSD: bktr_tuner.h,v 1.1 2001/03/28 03:27:10 fgsch Exp $ */ +/* $FreeBSD: src/sys/dev/bktr/bktr_tuner.h,v 1.1 1999/09/26 22:06:20 roger Exp $ */ + +/* + * This is part of the Driver for Video Capture Cards (Frame grabbers) + * and TV Tuner cards using the Brooktree Bt848, Bt848A, Bt849A, Bt878, Bt879 + * chipset. + * Copyright Roger Hardiman and Amancio Hasty. + * + * bktr_tuner : This deals with controlling the tuner fitted to TV cards. + * + */ + +/* + * 1. Redistributions of source code must retain the + * Copyright (c) 1997 Amancio Hasty, 1999 Roger Hardiman + * 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 and + * Roger Hardiman + * 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. + */ + +/* Definitions for Tuners */ + +#define NO_TUNER 0 +#define TEMIC_NTSC 1 +#define TEMIC_PAL 2 +#define TEMIC_SECAM 3 +#define PHILIPS_NTSC 4 +#define PHILIPS_PAL 5 +#define PHILIPS_SECAM 6 +#define TEMIC_PALI 7 +#define PHILIPS_PALI 8 +#define PHILIPS_FR1236_NTSC 9 /* These have FM radio support */ +#define PHILIPS_FR1216_PAL 10 /* These have FM radio support */ +#define PHILIPS_FR1236_SECAM 11 /* These have FM radio support */ +#define ALPS_TSCH5 12 +#define ALPS_TSBH1 13 +#define Bt848_MAX_TUNER 14 + +/* experimental code for Automatic Frequency Control */ +#define TUNER_AFC + +/* + * Fill in the tuner entries in the bktr_softc based on the selected tuner + * type (from the list of tuners above) + */ +void select_tuner( bktr_ptr_t bktr, int tuner_type ); + + +/* + * The Channel Set maps TV channels eg Ch 36, Ch 51, onto frequencies + * and is country specific. + */ +int tuner_getchnlset( struct bktr_chnlset *chnlset ); + +/* + * tv_channel sets the tuner to channel 'n' using the current Channel Set + * tv_freq sets the tuner to a specific frequency for TV or for FM Radio + * get_tuner_status can be used to get the signal strength. + */ +#define TV_FREQUENCY 0 +#define FM_RADIO_FREQUENCY 1 +int tv_channel( bktr_ptr_t bktr, int channel ); +int tv_freq( bktr_ptr_t bktr, int frequency, int type ); +int get_tuner_status( bktr_ptr_t bktr ); + +#if defined( TUNER_AFC ) +int do_afc( bktr_ptr_t bktr, int addr, int frequency ); +#endif /* TUNER_AFC */ + + +/* + * This is for start-up convenience only, NOT mandatory. + */ +#if !defined( DEFAULT_CHNLSET ) +#define DEFAULT_CHNLSET CHNLSET_WEUROPE +#endif + + |