diff options
author | Michael Krufky <devnull@localhost> | 2005-12-25 16:23:58 +0000 |
---|---|---|
committer | Michael Krufky <devnull@localhost> | 2005-12-25 16:23:58 +0000 |
commit | 5a56df6ff9f4bcd4e00171de24974426a2ef861a (patch) | |
tree | 399386bf9bd2283d9578dbd52e1506e76fb8dafe /v4l_experimental/v3tv/v3tv.c | |
parent | cf32dac4b871fc3ff7fffdeb8f1af87c57ddabbc (diff) | |
download | mediapointer-dvb-s2-5a56df6ff9f4bcd4e00171de24974426a2ef861a.tar.gz mediapointer-dvb-s2-5a56df6ff9f4bcd4e00171de24974426a2ef861a.tar.bz2 |
Merge V3TV driver from http://www.gilfillan.org/v3tv/v3tv-v4l2/
From: Perry Gilfillan <perrye@linuxmail.org>
- Merge V3TV driver from http://www.gilfillan.org/v3tv/v3tv-v4l2/
Signed-off-by: Michael Krufky <mkrufky@m1k.net>
Diffstat (limited to 'v4l_experimental/v3tv/v3tv.c')
-rw-r--r-- | v4l_experimental/v3tv/v3tv.c | 3354 |
1 files changed, 3354 insertions, 0 deletions
diff --git a/v4l_experimental/v3tv/v3tv.c b/v4l_experimental/v3tv/v3tv.c new file mode 100644 index 000000000..a29569c6a --- /dev/null +++ b/v4l_experimental/v3tv/v3tv.c @@ -0,0 +1,3354 @@ + +#define __NO_VERSION__ +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/videodev.h> +#include <linux/video_decoder.h> +#include <linux/spinlock.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <asm/io.h> +#include <asm/pgtable.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#include <media/tuner.h> +#include <media/audiochip.h> +#else +#include "/usr/src/linux/drivers/media/video/i2c-compat.h" +#include "/usr/src/linux/drivers/media/video/audiochip.h" +#include "/usr/src/linux/drivers/media/video/tuner.h" +#endif + +//#ifndef I2C_DRIVERID_VPX3224 +//#include "vpx-common.h" +//#endif + + +//#include <linux/i2c.h> +//#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +//#include "../linux/drivers/media/video/i2c-compat.h" +//#endif + + +#define UNSET (-1U) + +#define V3_VERSION "0.1-Multistandard for V4L2" + +/* i2c defines */ +/* bit locations in the register */ +#define DDC_ENAB 0x00040000 +#define DDC_SCL_OUT 0x00080000 +#define DDC_SDA_OUT 0x00100000 +#define DDC_SCL_IN 0x00200000 +#define DDC_SDA_IN 0x00400000 +#define I2C_ENAB 0x00800000 +#define I2C_SCL_OUT 0x01000000 +#define I2C_SDA_OUT 0x02000000 +#define I2C_SCL_IN 0x04000000 +#define I2C_SDA_IN 0x08000000 + +/* initialization states */ +#define I_i2c 0x2 /* i2c-interface initialized */ +#define I_ddc 0x4 /* ddc-interface initialized */ +#define I_v4l2 0x8 /* v4l devices registered */ +#define I_irq 0x10 /* irq active */ + +/* delays */ +#define CYCLE_DELAY 10 +#define TIMEOUT 50 + +/* the only registers we use for i2c control*/ +#define REG 0x78 +#define REG2 0x70 + +/* Voodoo avenger register offsets */ +#define V3REG_vidTvOutBlankVCount 0x3c /* 31:0 R/W TV Out Vertical Active Start/End */ +#define V3REG_vidMaxRgbDelta 0x58 /* 23:0 R/W Maximum delta values for video filtering */ +#define V3REG_vidProcCfg 0x5c /* 31:0 R/W Video Processor configuration */ +#define V3REG_hwCurPatAddr 0x60 /* 23:0 R/W Cursor Pattern Address */ +#define V3REG_hwCurLoc 0x64 /* 26:0 R/W X and Y location of HW cursor */ +#define V3REG_hwCurC0 0x68 /* 23:0 R/W Hw cursor color 0 */ +#define V3REG_hwCurC1 0x6c /* 23:0 R/W Hw cursor color 1 */ +#define V3REG_vidInFormat 0x70 /* 31:0 R/W Video In Format */ +#define V3REG_vidTvOutBlankHCount 0x74 /* 31:0 R/W TV Out Horizontal Active Start/End */ +#define V3REG_vidSerialParallelPort 0x78 /* 31:0 R/W Serial and Parallel Ports */ +#define V3REG_vidInXDecimDeltas 0x7c /* 31:0 R/W Video In horizontal decimation delta 1 & 2. */ +#define V3REG_vidInDecimInitErrs 0x80 /* 31:0 R/W Video In horizontal and vertical decimation initial error term */ +#define V3REG_vidInYDecimDeltas 0x84 /* 31:0 R/W Video In vertical decimation delta 1 & 2 */ +#define V3REG_vidPixelBufThold 0x88 /* 17:0 R/W Video Pixel Buffer Threshold */ +#define V3REG_vidChromaMin 0x8c /* 31:0 R/W Chroma Key minimum value */ +#define V3REG_vidChromaMax 0x90 /* 31:0 R/W Chroma Key maximum value */ +#define V3REG_vidInStatusCurrentLine 0x94 /* 18:0 R Video In Status and Current Scan line */ +#define V3REG_vidScreenSize 0x98 /* 23:0 R/W Screen resolution */ +#define V3REG_vidOverlayStartCoords 0x9c /* 31:0 R/W Start Surface Coordinates [31:28] Overlay Start Screen Coordinates */ +#define V3REG_vidOverlayEndScreenCoord 0xa0 /* 23:0 R/W Overlay End Screen Coordinates */ +#define V3REG_vidOverlayDudx 0xa4 /* 19:0 R/W Overlay horizontal magnification factor */ +#define V3REG_vidOverlayDudxOffsetSrcWidth 0xa8 /* 31:0 R/W Overlay horizontal magnification factor initial offset (bit 18:0) Overlay source surface width (bit 31:19) */ +#define V3REG_vidOverlayDvdy 0xac /* 19:0 R/W Overlay vertical magnification factor */ +#define V3REG_vidOverlayDvdyOffset 0xe0 /* 18:0 R/W Overlay vertical magnification factor initial offset */ +#define V3REG_vidDesktopStartAddr 0xe4 /* 23:0 R/W Desktop start address */ +#define V3REG_vidDesktopOverlayStride 0xe8 /* 31:0 R/W Desktop and Overlay strides (linear or tile) */ +#define V3REG_vidInAddr0 0xec /* 23:0 R/W Video In Buffer 0 starting address */ +#define V3REG_vidInAddr1 0xf0 /* 23:0 R/W Video In Buffer 1 starting address */ +#define V3REG_vidInAddr2 0xf4 /* 23:0 R/W Video In Buffer 2 starting address */ +#define V3REG_vidInStride 0xf8 /* 14:0 R/W Video In Buffer stride (linear or tile) */ +#define V3REG_vidCurrOverlayStartAddr 0xfc /* 23:0 R Current overlay start address in use */ + +/* 2D registers */ +#define V3REG_IO_BASE_1 0x100000 + +#define V3REG_status V3REG_IO_BASE_1 + 0x000 /* 31:0 R Avenger status register */ +#define V3REG_intrCtrl V3REG_IO_BASE_1 + 0x004 /* 31:0 R/W Interrupt control and status */ +#define V3REG_clip0Min V3REG_IO_BASE_1 + 0x008 /* 28:0 R/W Min X & Y clip values when clip select is 0 */ +#define V3REG_clip0Max V3REG_IO_BASE_1 + 0x00c /* 28:0 R/W Max X & Y clip values when clip select is 0 */ +#define V3REG_dstBaseAddr V3REG_IO_BASE_1 + 0x010 /* 23:0 R/W Destination base address */ +#define V3REG_dstFormat V3REG_IO_BASE_1 + 0x014 /* 17:0 R/W Destination stride and bits per pixel */ +#define V3REG_srcColorkeyMin V3REG_IO_BASE_1 + 0x018 /* 23:0 R/W Source Colorkey range (min) */ +#define V3REG_srcColorkeyMax V3REG_IO_BASE_1 + 0x01c /* 23:0 R/W Source Colorkey range (max) */ +#define V3REG_dstColorkeyMin V3REG_IO_BASE_1 + 0x020 /* 23:0 R/W Destination Colorkey range (min) */ +#define V3REG_dstColorkeyMax V3REG_IO_BASE_1 + 0x024 /* 23:0 R/W Destination Colorkey range (max) */ +#define V3REG_bresError0 V3REG_IO_BASE_1 + 0x028 /* 31:0 R/W Initial error for lines, right edges & stretch blt x */ +#define V3REG_bresError1 V3REG_IO_BASE_1 + 0x02c /* 31:0 R/W Initial error for left poly edges & stretch blt y */ +#define V3REG_rop V3REG_IO_BASE_1 + 0x030 /* 31:0 R/W 4 Ternary Raster operations */ +#define V3REG_srcBaseAddr V3REG_IO_BASE_1 + 0x034 /* 23:0 R/W Source base address */ +#define V3REG_commandExtra V3REG_IO_BASE_1 + 0x038 /* 31:0 R/W Extra control bits */ +#define V3REG_lineStipple V3REG_IO_BASE_1 + 0x03c /* 31:0 R/W Monochrome pattern for lines */ +#define V3REG_lineStyle V3REG_IO_BASE_1 + 0x040 /* 28:0 R/W Style register for lines */ +#define V3REG_pattern0Alias V3REG_IO_BASE_1 + 0x044 /* 31:0 R/W Alias to colorPattern(0) */ +#define V3REG_pattern1Alias V3REG_IO_BASE_1 + 0x048 /* 31:0 R/W Alias to colorPattern(1) */ +#define V3REG_clip1Min V3REG_IO_BASE_1 + 0x04c /* 28:0 R/W Min X & Y clip values when clip select is 1 */ +#define V3REG_clip1Max V3REG_IO_BASE_1 + 0x050 /* 28:0 R/W Max X & Y clip values when clip select is 1 */ +#define V3REG_srcFormat V3REG_IO_BASE_1 + 0x054 /* 18:0 R/W Source stride and bits per pixel */ +#define V3REG_srcSize V3REG_IO_BASE_1 + 0x058 /* 28:0 R/W Height and width of source for stretch blts */ +#define V3REG_srcXY V3REG_IO_BASE_1 + 0x05c /* 28:0 R/W Starting pixel of blt source data */ +#define V3REG_colorBack V3REG_IO_BASE_1 + 0x060 /* 31:0 R/W Background color */ +#define V3REG_colorFore V3REG_IO_BASE_1 + 0x064 /* 31:0 R/W Foreground color */ +#define V3REG_dstSize V3REG_IO_BASE_1 + 0x068 /* 28:0 R/W Destination width and height for blts and rectangle fills */ +#define V3REG_dstXY V3REG_IO_BASE_1 + 0x06c /* 28:0 R/W Starting X and Y of destination for blts */ +#define V3REG_command V3REG_IO_BASE_1 + 0x070 /* 31:0 R/W 2D command mode & control bits */ +#define V3REG_launchArea V3REG_IO_BASE_1 + 0x080 /* 31:0 R Initiates 2D commands */ + +/* 3D registers */ +#define V3REG_IO_BASE_2 0x200000 + +#define V3REG_swapbufferCMD V3REG_IO_BASE_2 + 0x128 /*8:0 FBI W Yes/Yes Execute SWAPBUFFER command */ +#define V3REG_swapPending V3REG_IO_BASE_2 + 0x24C /*na FBI W No / No Swap buffer pending */ +#define V3REG_leftOverlayBuf V3REG_IO_BASE_2 + 0x250 /*31:0 FBI W No / Yes Left Overlay address */ +#define V3REG_rightOverlayBuf V3REG_IO_BASE_2 + 0x254 /*31:0 FBI W No / Yes Right Overlay address */ + +/* External variables: Data struct */ +struct voodoo_data *voodoo; + +/* Global data struct. + * We can have only a single voodoo3 3500 TV card anyway, + * because there aren't any dual AGP slot motherboards, right? + */ +struct voodoo_data *voodoo = NULL; + +/* Module parameters */ +#ifdef INCLUDE_I2C +int ddc = 1; +#endif +int debug = 0; +int model = 0; +int video_nr = -1; +int vbi_nr = -1; +int radio_nr = -1; + + + + +/* + * Many declarations for v3tv drivers. + * + * $Id: v3tv.c,v 1.1 2005/12/25 16:23:58 mkrufky Exp $ + */ + +/* 3DFX defines */ +#ifndef PCI_DEVICE_ID_3DFX_VOODOO3 +#define PCI_DEVICE_ID_3DFX_VOODOO3 0x05 +#endif + +#ifndef VID_HARDWARE_V3TV +#define VID_HARDWARE_V3TV_RADIO 0x49 +#define VID_HARDWARE_V3TV 0x50 +#endif + +/* Different versions of the Voodoo3 3500 TV */ +#define VOODOO3_MODEL_NTSC 0x60 +#define VOODOO3_MODEL_PAL 0x61 +#define VOODOO3_MODEL_SECAM 0x62 + +#define V3_MAX_I2C_CLIENTS 10 + +#define VOODOO3_2D_SAVE 0 +#define VOODOO3_2D_RESTORE 1 + +struct voodoo_data { + /* locking */ +// spinlock_t s_lock; +// struct semaphore lock; + + struct pci_dev *dev; + + unsigned char *io_base; /* memory mapped registers */ + unsigned char *fb_base; /* frame buffer memory */ +#ifdef INCLUDE_I2C + struct i2c_client *i2c_clients[V3_MAX_I2C_CLIENTS]; +#else + struct i2c_adapter *voodoo_i2c_adapter; +#endif + struct video_picture picture_settings; + struct video_window capture_win; + struct video_buffer fb; + + struct video_device *video_dev; + struct video_device *radio_dev; + struct video_device *vbi_dev; + + int buffering; /* 1 = single, 2=double, 3=triple */ + + u32 vidInAddr0; + u32 vidInAddr1; + u32 vidInAddr2; + u32 captureBuf; + u32 desktopAddr; + + int gbuffers; + int gbufsize; + unsigned char *fbuffer; + + int vidOverlayStartX; + int vidOverlayStartY; + int vidOverlayOffsetX; + int vidOverlayOffsetY; + + int vidOverlayEndX; + int vidOverlayEndY; + + int vidOrigWidth; + int vidOrigHeight; + + int vidInStride; + int vidOverlayStride; + int vidScanlineBytes; + + int desktopStride; + int desktopWidth; + int desktopHeight; + + int overlayWidth, overlayHeight; + int dudx; + int dvdy; + int XDecim; + int YDecim; + + char name[32]; + int model; + int norm; + int type; + int input; + int tuner_type; + int channel; + unsigned long freq; + unsigned long chroma; + + int initialized; + int decoder_enabled; +}; + +struct conf_struct { + unsigned int reg; + int value; + unsigned char endbit, startbit; +}; + +#define CONF_SIZE(a) (sizeof(a) / sizeof (struct conf_struct)) + +/* FIXME: + * The chromakey register has different formats, depending on the desktop color bits. + * This means, that the chromakey will be interpreted differently in different video modes. + */ + +// static struct conf_struct Chromakey_Disable[] = +// { +// { V3REG_vidProcCfg, 0, 5, 5} +// }; + +static struct conf_struct voodoo3_init_reg[] = { + {V3REG_vidInFormat, -1, 0, 0}, /* Reserved - leave defaults */ + {V3REG_vidInFormat, 6, 3, 1}, /* 110 = 8bit YCbCr 4:2:2 UYVY */ + {V3REG_vidInFormat, 0, 4, 4}, /* No De-interlacing */ + {V3REG_vidInFormat, -1, 7, 5}, /* Polarities - leave defaults */ + {V3REG_vidInFormat, 1, 8, 8}, /* Brooktree tv-out */ + {V3REG_vidInFormat, 2, 10, 9}, /* VideoIn triple buffering (FIXME:BUGGY???) */ + {V3REG_vidInFormat, 0, 11, 11}, /* VideoInBuffer linear space */ + {V3REG_vidInFormat, -1, 13, 12}, /* polarities - leave defaults */ + {V3REG_vidInFormat, -1, 14, 14}, /* VMI-interface enable (not yet) */ + {V3REG_vidInFormat, 1, 15, 15}, /* TV-Out-interface enable */ + {V3REG_vidInFormat, 0, 16, 16}, /* Genlock disable(VMI-slave) */ + {V3REG_vidInFormat, 0, 17, 17}, /* Use vga timing */ + {V3REG_vidInFormat, -1, 18, 18}, /* Genlock source - with bit16=0 no effect */ + {V3REG_vidInFormat, -1, 19, 19}, /* Use display_ena for driving tv-out */ + {V3REG_vidInFormat, 0, 20, 20}, /* VideoIn Horizontal Decimation disable */ + {V3REG_vidInFormat, 0, 21, 21}, /* VideoIn Vertical Decimation disable */ + {V3REG_vidInFormat, -1, 31, 22}, /* Reserved - leave defaults */ + {V3REG_vidSerialParallelPort, 0, 0, 0}, /* VMI parallel port disable */ + {V3REG_vidSerialParallelPort, -1, 1, 1}, /* VMI Chip Select (default) */ + {V3REG_vidSerialParallelPort, -1, 4, 2}, /* VMI send/rcv data ctrl - leave defaults */ + {V3REG_vidSerialParallelPort, 0, 5, 5}, /* VMI data output disable */ + {V3REG_vidSerialParallelPort, -1, 13, 6}, /* VMI send/rcv data - leave defaults */ + {V3REG_vidSerialParallelPort, -1, 17, 14}, /* VMI send/rcv addrs - leave defaults */ +#ifdef INCLUDE_I2C + {V3REG_vidSerialParallelPort, 1, 22, 18}, /* Activate ddc-interface(i2c for monitor) */ + {V3REG_vidSerialParallelPort, 1, 27, 23}, /* Activate i2c-interface(i2c on card) */ +#else + {V3REG_vidSerialParallelPort, -1, 22, 18}, /* Activate ddc-interface(i2c for monitor) */ + {V3REG_vidSerialParallelPort, -1, 27, 23}, /* Activate i2c-interface(i2c on card) */ +#endif + {V3REG_vidSerialParallelPort, 1, 28, 28}, /* VMI normal operation */ + {V3REG_vidSerialParallelPort, 1, 29, 29}, /* GPIO[1] - VMI sync_oe */ + {V3REG_vidSerialParallelPort, -1, 30, 30}, /* GPIO[2] - Input only */ + {V3REG_vidSerialParallelPort, 1, 31, 31}, /* TV out normal operation */ +}; + +static struct conf_struct Chromakey_Enable[] = + { {V3REG_vidProcCfg, 1, 5, 5} }; +static struct conf_struct Horizontal_Decimation_Enable[] = + { {V3REG_vidInFormat, 1, 20, 20} }; /* Horizontal Decimation enable */ +static struct conf_struct Horizontal_Decimation_Disable[] = + { {V3REG_vidInFormat, 0, 20, 20} }; /* Horizontal Decimation disable */ +static struct conf_struct Vertical_Decimation_Enable[] = + { {V3REG_vidInFormat, 1, 21, 21} }; /* Vertical Decimation enable */ +static struct conf_struct Vertical_Decimation_Disable[] = + { {V3REG_vidInFormat, 0, 21, 21} }; /* Vertical Decimation enable */ +static struct conf_struct Horizontal_Magnification_Enable[] = + { {V3REG_vidProcCfg, 1, 14, 14} }; /* Horizontal Scaling enable */ +static struct conf_struct Horizontal_Magnification_Disable[] = + { {V3REG_vidProcCfg, 0, 14, 14} }; /* Horizontal Scaling disable */ +static struct conf_struct Vertical_Magnification_Enable[] = + { {V3REG_vidProcCfg, 1, 15, 15} }; /* Vertical Scaling enable */ +static struct conf_struct Vertical_Magnification_Disable[] = + { {V3REG_vidProcCfg, 0, 15, 15} }; /* Vertical Scaling disable */ + +static struct conf_struct Overlay_Disable[] = + { {V3REG_vidProcCfg, 0, 8, 8}, + {V3REG_vidProcCfg, 0, 9, 9} }; +static struct conf_struct Overlay_Enable[] = { + {V3REG_vidProcCfg, -1, 5, 5}, /* Disable Chroma Key */ + {V3REG_vidProcCfg, 1, 8, 8}, /* Overlay surface enable */ + {V3REG_vidProcCfg, 1, 9, 9}, /* Use vidInAddr as overlay buffer */ + {V3REG_vidProcCfg, 1, 11, 11}, /* Overlay ramdac-clut bypass */ + {V3REG_vidProcCfg, -1, 13, 13}, /* Overlay clut don't care */ + {V3REG_vidProcCfg, -1, 14, 14}, /* overlay horizontal scaling */ + {V3REG_vidProcCfg, -1, 15, 15}, /* overlay vertical scaling */ + {V3REG_vidProcCfg, 3, 17, 16}, /* overlay filter mode */ + {V3REG_vidProcCfg, 6, 23, 21}, /* overlay pixel format (422 UYVY) */ + {V3REG_vidProcCfg, 0, 25, 25}, /* overlay tile space (0=linear) */ + {V3REG_vidProcCfg, -1, 30, 30}, /* Reserved */ + {V3REG_vidProcCfg, 0, 31, 31}, /* No overlay backend de-interlacing */ + {V3REG_vidInFormat, 1, 14, 14}, /* VMI interface enable */ + {V3REG_vidSerialParallelPort, 1, 0, 0} }; /* VMI host interface enable */ +static struct conf_struct Overlay_Filter_Off[] = { + {V3REG_vidProcCfg, 0, 17, 16} }; /* overlay filter OFF */ +static struct conf_struct Overlay_Filter_On[] = { + {V3REG_vidProcCfg, 3, 17, 16} }; /* overlay filter ON */ + +static struct conf_struct Disable_VMI_Interrupt[] = { + {V3REG_intrCtrl, 0, 21, 21}, /* VMI Interrupt disable */ + {V3REG_intrCtrl, 0, 23, 23}, /* clear VMI Interrupt generated */ + {V3REG_intrCtrl, 1, 31, 31} }; /* clear PCI interrupt active */ + +static struct conf_struct VMI_Enable[] = { + {V3REG_vidInFormat, 1, 14, 14}, /* VMI interafce enable */ + {V3REG_vidSerialParallelPort, 1, 0, 0} }; /* VMI parallel host interface enable */ + +static struct conf_struct VMI_Disable[] = { + {V3REG_vidInFormat, 0, 14, 14}, /* VMI interafce disable */ + {V3REG_vidSerialParallelPort, 0, 0, 0} }; /* VMI parallel host interface disable */ + +// static struct conf_struct VMI_Disable[] = { +// {V3REG_vidInFormat, 0, 14, 14}, +// {V3REG_vidSerialParallelPort, 0, 0, 0} }; + +#if 0 +static struct conf_struct Enable_VMI_Interrupt[] = { + {V3REG_intrCtrl, 1, 21, 21}, + {V3REG_intrCtrl, 0, 23, 23} }; +static struct conf_struct Disable_VMI_Interrupt[] = { + {V3REG_intrCtrl, 0, 21, 21}, + {V3REG_intrCtrl, 0, 23, 23} }; +static struct conf_struct Clear_VMI_Interrupt[] = { + {V3REG_intrCtrl, 0, 23, 23} }; +static struct conf_struct Overlay_Setup[] = { + {V3REG_vidProcCfg, 1, 8, 8}, + {V3REG_vidProcCfg, 1, 9, 9} }; +#endif + + +/* C function prototypes */ +void v3tv_call_i2c_client(unsigned int DriverID, unsigned int cmd, + void *arg); +int v3tv_i2c_setup(int arg); +// static int v3tv_probe(void); +// int v3tv_init_module(void); +// void v3tv_cleanup_module(void); + +// int v3tv_attach_adapter(struct i2c_adapter *adapter); +// int v3tv_detach_client(struct i2c_client *client); + +void v3tv_set_chromakey(struct voodoo_data *voodoo); +// void v3tv_calc_scale(struct voodoo_data *voodoo) +// long do_fraction(int numBits, unsigned a, unsigned b); +// void v3tv_update_overlay_setup (struct voodoo_data *voodoo); +void v3tv_set_overlay(struct voodoo_data *voodoo); +void v3tv_overlay_setup(struct voodoo_data *voodoo); +void v3tv_overlay(struct voodoo_data *voodoo, int i); +// int v3tv_MakeRoom(int num); +// void v3tv_2dreg(int i); +void v3tv_capture_frame(struct voodoo_data *voodoo_tv); + +// static int v3tv_radio_open (struct video_device *dev, int mode); +// static void v3tv_radio_close (struct video_device *dev); +// static int v3tv_radio_ioctl (struct video_device *dev, unsigned int cmd, void *arg); +// static irqreturn_t v3tv_voodoo_irq(int irq, void *dev_id, struct pt_regs *regs); +void v3tv_set_palette(struct video_picture *picture_settings); +// static int v3tv_voodoo_open(struct video_device *dev,int flags); +void v3tv_voodoo_close(struct video_device *dev); +int v3tv_voodoo_mmap(struct file *file, struct vm_area_struct *vma); +// static long v3tv_voodoo_read(struct video_device *dev, char *buf, unsigned long count, int nonblock); +// static int v3tv_voodoo_ioctl(struct video_device *dev, unsigned int cmd, void *arg); +int v3tv_video_init(struct voodoo_data *voodoo); +void v3tv_video_cleanup(struct voodoo_data *voodoo); + +// inline void v3tv_regwrite(unsigned int reg, unsigned int data); +// inline unsigned int v3tv_regread(unsigned int reg); +// static inline unsigned int do_mask(struct conf_struct *conf); +// static inline unsigned int do_value(unsigned int orig, struct conf_struct *conf); +void v3tv_conf(struct conf_struct *conf, int num_entries); +// static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr); +// static inline unsigned long uvirt_to_bus(unsigned long adr); +// static inline unsigned long kvirt_to_bus(unsigned long adr); +// inline unsigned long kvirt_to_pa(unsigned long adr); +// void *rvmalloc(signed long size); +// void rvfree(void * mem, signed long size); + + + +/* + * Here's some pretty complicated stuff... + * We probably need to start inserting spinlock-stuff here, + * or in the functions that call these ones. + * + * $Id: v3tv.c,v 1.1 2005/12/25 16:23:58 mkrufky Exp $ + */ + +inline void v3tv_regwrite(unsigned int reg, unsigned int data) +{ + if (!voodoo || !voodoo->io_base) + return; + + *((unsigned int *)(voodoo->io_base + reg)) = data; +} + +/* +inline static void voodoo3_regor(unsigned int reg, unsigned int data, unsigned int mask) +{ + unsigned int *reg_ptr; + + if (!voodoo || !voodoo->io_base) + return; + + reg_ptr = (unsigned int *) voodoo->io_base + reg; + *reg_ptr = (*reg_ptr & ~mask) | (data & mask); +} +*/ + +inline unsigned int v3tv_regread(unsigned int reg) +{ + if (!voodoo || !voodoo->io_base) + return 0; + + return *((unsigned int *)(voodoo->io_base + reg)); +} + +/*******************************/ +/* Memory management functions */ +/* these are from bttv */ +/*******************************/ + +#define MDEBUG(x) do { } while(0) /* Debug memory management */ + +/* [DaveM] I've recoded most of this so that: + * 1) It's easier to tell what is happening + * 2) It's more portable, especially for translating things + * out of vmalloc mapped areas in the kernel. + * 3) Less unnecessary translations happen. + * + * The code used to assume that the kernel vmalloc mappings + * existed in the page tables of every process, this is simply + * not guarenteed. We now use pgd_offset_k which is the + * defined way to get at the kernel page tables. + */ + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +#if 0 +static inline unsigned long uvirt_to_kva(pgd_t * pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset_map(pmd, adr); + pte = *ptep; + if (pte_present(pte)) { + ret = + (unsigned long)page_address(pte_page(pte)); + ret |= (adr & (PAGE_SIZE - 1)); + } + } + } + MDEBUG(printk("v3tv: uv2kva(%lx-->%lx)", adr, ret)); + return ret; +} +#endif + +static inline unsigned long uvirt_to_kva(pgd_t * pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { +#if defined (pte_offset_map) /* 2.4-rmap or 2.5-2.6 */ + ptep = pte_offset_map(pmd, adr); + pte = *ptep; + pte_unmap(ptep); +#else +#if defined (pte_offset_atomic) /* 2.4-aa */ + ptep = pte_offset_atomic(pmd, adr); + pte = *ptep; + pte_kunmap(ptep); +#else + ptep = NULL; + pte = *pte_offset(pmd, adr); +#endif +#endif + if (pte_present(pte)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + ret = + (unsigned long)page_address(pte_page(pte)); +#else + ret = pte_page(pte); +#endif + ret |= (adr & (PAGE_SIZE - 1)); + } + } + } + MDEBUG(printk("v3tv: uv2kva(%lx-->%lx)", adr, ret)); + return ret; +} + +/* +static inline unsigned long uvirt_to_bus(unsigned long adr) +{ + unsigned long kva, ret; + + kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("v3tv: uv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long kvirt_to_bus(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = ((unsigned long) (adr)); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("v3tv: kv2b(%lx-->%lx)", adr, ret)); + return ret; +} +*/ + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = ((unsigned long) (adr)); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + MDEBUG(printk("v3tv: kv2pa(%lx-->%lx)", adr, ret)); + return ret; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) +#include <linux/wrapper.h> +#else +#define mem_map_reserve(p) set_bit(PG_reserved, &((p)->flags)) +#define mem_map_unreserve(p) clear_bit(PG_reserved, &((p)->flags)) +#endif + +void inline *rvmalloc(signed long size) +{ + void *mem; + unsigned long adr, page; + + mem = vmalloc(size); + if (mem) { + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long)mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_reserve(virt_to_page(__va(page))); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + } + return mem; +} + +/* +void inline rvfree(void *mem, signed long size) +{ + unsigned long adr, page; + + if (mem) { + adr = (unsigned long)mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_unreserve(virt_to_page(__va(page))); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); + } +} +*/ + +static inline unsigned int do_mask(struct conf_struct *conf) +{ + unsigned int mask = 0; + int i; + for (i = conf->startbit; i <= conf->endbit; i++) + mask |= (1 << i); + + return mask; +} + +static inline unsigned int do_value(unsigned int orig, struct conf_struct *conf) +{ + unsigned int mask = 0; + + mask = do_mask(conf); + + return ((conf->value << conf->startbit) & mask) | (orig & (~mask)); +} + +void v3tv_conf(struct conf_struct *conf, int num_entries) +{ + int i; + int lastReg = 0; + unsigned int cur_value = 0; + spinlock_t v3conf_lock = SPIN_LOCK_UNLOCKED; + + if (num_entries < 1) { + printk("v3tv: Empty config data in voodoo3_conf()\n"); + return; + } + + lastReg = conf[0].reg; + cur_value = v3tv_regread(lastReg); + + if (debug >= 2) + printk("v3tv: register 0x%x: 0x%x", lastReg, cur_value); + + spin_lock(&v3conf_lock); + + for (i = 0; i < num_entries; i++) { + if (lastReg != conf[i].reg) { + /* Write Register with mask */ + v3tv_regwrite(lastReg, cur_value); + if (debug >= 2) + printk(" -> 0x%x\n", cur_value); + + /* Set lastreg to point to current reg */ + lastReg = conf[i].reg; + cur_value = v3tv_regread(lastReg); + if (debug >= 2) + printk("v3tv: register 0x%x: 0x%x", + lastReg, cur_value); + } + + if (conf[i].value != -1) { + cur_value = do_value(cur_value, &conf[i]); + } + } + + v3tv_regwrite(lastReg, cur_value); + + spin_unlock(&v3conf_lock); + + if (debug >= 2) + printk(" -> 0x%x\n", cur_value); +} + + +/* + * voodoo-i2c.c - Part of v4l voodoo3 drivers + * Copyright (c) 2000 Juha Valkama <jusva@yahoo.com> + * + * Based on work by (at least) the following people: + * Frodo Looijaard <frodol@dds.nl> + * Philip Edelbrock <phil@netroedge.com> + * Ralph Metzler <rjkm@thp.uni-koeln.de> + * Mark D. Studebaker <mdsxyz123@yahoo.com> + * + * $Id: v3tv.c,v 1.1 2005/12/25 16:23:58 mkrufky Exp $ + */ + +int v3tv_get_adapter(struct i2c_adapter *adapter) +{ + if (debug) + printk(KERN_INFO "v3tv: v3tv_get_adapter trying: %s\n", + adapter->name); +// if (I2C_CLASS_TV_ANALOG != adapter->class) +// return 0; +#ifdef I2C_CLASS_TV_ANALOG + if (!(adapter->class & I2C_CLASS_TV_ANALOG)) + return 0; +#else + switch (adapter->id) { + case I2C_ALGO_BIT | I2C_HW_B_VOO: + if (!strnicmp(adapter->name, "I2C\0", 3)) + break; + default: + return 0; + } +#endif + if (debug) + printk(KERN_INFO "v3tv: i2c_attach_client found adapter: %s\n", + adapter->name); + + if (voodoo->voodoo_i2c_adapter == adapter) { + if (debug) + printk(KERN_INFO "v3tv: i2c_attach_client releasing adapter: %s\n", + adapter->name); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + module_put(voodoo->voodoo_i2c_adapter->owner); +#else + __MOD_DEC_USE_COUNT(voodoo->voodoo_i2c_adapter->owner); +#endif + voodoo->voodoo_i2c_adapter = NULL; + return 0; + } + + voodoo->voodoo_i2c_adapter = adapter; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + if (!try_module_get(adapter->owner)) { + printk("v3tv: Unable to get module %s\n", + adapter->owner->name); + return -ENODEV; + } +#else + __MOD_INC_USE_COUNT(voodoo->voodoo_i2c_adapter->owner); +#endif + printk ("v3tv: i2c dummy driver registered: adapter: 0x%p\n", + voodoo->voodoo_i2c_adapter); + + return 0; +} + +struct i2c_driver voodoo_i2c_driver = { + .owner = THIS_MODULE, + .name = "v3tv i2c dummy driver", + .id = 0, +/* FIXME: the 2.6 kernel does not have _DUMMY, use .detach_adapter */ +#ifdef I2C_DF_DUMMY + .flags = I2C_DF_DUMMY, +#else + .flags = I2C_DF_NOTIFY, + .detach_adapter = v3tv_get_adapter, +#endif + .attach_adapter = v3tv_get_adapter, + .detach_client = NULL, + .command = NULL, +}; + +int v3tv_i2c_setup(int arg) +{ + if (arg) { /* get i2c adapter pointer */ + i2c_add_driver(&voodoo_i2c_driver); + if (NULL == voodoo->voodoo_i2c_adapter) { + printk + ("v3tv: i2c registration failed, module not inserted\n"); + return -ENODEV; + } + } else { /* release adapter */ + if (voodoo->voodoo_i2c_adapter) { + i2c_del_driver(&voodoo_i2c_driver); + } + } + return 0; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ +/* + * voodoo-overlay.c + * + * Video overlay handling routines for Voodoo3 3500 TV. + * + * $Id: v3tv.c,v 1.1 2005/12/25 16:23:58 mkrufky Exp $ + */ + +struct video_capture video_standard[] = { +/* x, y, width, height, decimation, flags */ + {0, 0, 768, 288, 0, 0}, /* PAL */ + {0, 0, 640, 240, 0, 0}, /* NTSC */ + {0, 0, 768, 288, 0, 0}, /* SECAM */ + {0, 0, 768, 288, 0, 0} /* AUTO *//* we should never get here */ +// { 32, 24, 768, 288, 0, 0 }, /* PAL */ +// { 32, 24, 640, 240, 0, 0 }, /* NTSC */ +// { 32, 24, 768, 288, 0, 0 }, /* SECAM */ +// { 0, 0, 768, 288, 0, 0 } /* AUTO */ /* we should never get here */ +}; + +/* FIXME: Todo stuff. + * - Background thread to monitor Voodoo3 for setting changes. + * - Reset video processor on resolution switch (must figure out how to reset first). + * - Update overlay window position when the virtual desktop scrolls. + */ + +/****************************** + * Overlay handling functions * + *****************************/ + + + +void v3tv_set_chromakey(struct voodoo_data *voodoo) +{ + if (debug >= 2) + printk("v3tv: set_chromakey (0x%lx)\n", voodoo->chroma); + + v3tv_conf(Chromakey_Enable, CONF_SIZE(Chromakey_Enable)); + + v3tv_regwrite(V3REG_vidChromaMin, voodoo->chroma); + v3tv_regwrite(V3REG_vidChromaMax, voodoo->chroma); +} + +/* do a fraction a/b in numBits format is 0.numbits*/ +long do_fraction(int numBits, unsigned a, unsigned b) +{ + /* This is _not_ the "official" algorithm from voodoo3_spec.pdf + * This should work in typical cases. + */ + + u32 num; + + if ((0xFFFFFFFF << (32 - numBits)) & a) { + printk("v3tv: Overflow in do_fraction (%d, %d, %d)\n", + numBits, a, b); + } + + num = ((u32) a << numBits) / b; + + if (debug >= 2) { + printk("v3tv: do_fraction (0x%lx) -> 0x%lx\n", + (unsigned long)a << numBits, (unsigned long)num); + } + return num; +} + +/* Update overlay window size and strides + * FIXME: + * This could possibly be made simpler, if we used constantly sized input video. + * I'll need to get back to it... + */ + +void v3tv_calc_scale(struct voodoo_data *voodoo) +{ + if (voodoo->vidOrigWidth <= voodoo->overlayWidth) { + if (voodoo->vidOrigWidth < voodoo->overlayWidth) { + voodoo->dudx = + do_fraction(20, voodoo->vidOrigWidth, + voodoo->overlayWidth); + if (debug) + printk + ("v3tv: Horizontal Magnification %d\n", + voodoo->dudx); + } + + voodoo->vidOverlayStride = voodoo->vidInStride; + } else { + voodoo->XDecim = (voodoo->overlayWidth & 0xFFF) | + (((voodoo->vidOrigWidth - + voodoo->overlayWidth) & 0xFFF) << 16); + if (debug) + printk("v3tv: Horizontal Decimation %d\n", + voodoo->XDecim); + voodoo->vidInStride = voodoo->overlayWidth * 2; + voodoo->vidOverlayStride = voodoo->vidInStride; + } + + if (voodoo->vidOrigHeight <= voodoo->overlayHeight) { + if (voodoo->vidOrigHeight < voodoo->overlayHeight) { + voodoo->dvdy = + do_fraction(20, voodoo->vidOrigHeight, + voodoo->overlayHeight); + if (debug) + printk("v3tv: Vertical Magnification %d\n", + voodoo->dvdy); + } + } else { + voodoo->YDecim = (voodoo->overlayHeight & 0xFFF) | + (((voodoo->vidOrigHeight - + voodoo->overlayHeight) & 0xFFF) << 16); + if (debug) + printk("v3tv: Vertical Decimation %d\n", + voodoo->YDecim); + } +} + +void v3tv_update_overlay_setup(struct voodoo_data *voodoo) +{ + unsigned int value = 0; + voodoo->dudx = voodoo->dvdy = voodoo->XDecim = voodoo->YDecim = 0; + + if (!voodoo) + return; + + voodoo->vidInStride = voodoo->vidOrigWidth * 2; + voodoo->overlayWidth = + voodoo->vidOverlayEndX - voodoo->vidOverlayStartX; + voodoo->overlayHeight = + voodoo->vidOverlayEndY - voodoo->vidOverlayStartY; + + voodoo->vidOverlayStride = voodoo->vidInStride; + voodoo->vidScanlineBytes = voodoo->vidInStride; + + v3tv_calc_scale(voodoo); + + value = voodoo->vidOverlayStartX & 0xFFF; + value |= ((voodoo->vidOverlayStartY << 12) & 0xFFF000); + v3tv_regwrite(V3REG_vidOverlayStartCoords, value); + + value = voodoo->vidOverlayEndX & 0xFFF; + value |= ((voodoo->vidOverlayEndY << 12) & 0xFFF000); + v3tv_regwrite(V3REG_vidOverlayEndScreenCoord, value); + + value = v3tv_regread(V3REG_vidDesktopOverlayStride); + value = + (value & 0x8000FFFF) | ((voodoo->vidOverlayStride << 16) & + 0x7FFF0000); + v3tv_regwrite(V3REG_vidDesktopOverlayStride, value); + + v3tv_regwrite(V3REG_vidInStride, voodoo->vidInStride & 0x7FFF); + + v3tv_regwrite(V3REG_vidOverlayDudxOffsetSrcWidth, + voodoo->vidScanlineBytes << 19); + + v3tv_regwrite(V3REG_vidOverlayDudx, voodoo->dudx); + v3tv_regwrite(V3REG_vidOverlayDvdy, voodoo->dvdy); + + v3tv_regwrite(V3REG_vidInXDecimDeltas, voodoo->XDecim); + v3tv_regwrite(V3REG_vidInYDecimDeltas, voodoo->YDecim); + + if (debug) + printk + ("v3tv: overlay start x: %d y: %d end x: %d y: %d (W: %d H: %d)\n", + voodoo->vidOverlayStartX, voodoo->vidOverlayStartY, + voodoo->vidOverlayEndX, voodoo->vidOverlayEndY, + voodoo->overlayWidth, voodoo->overlayHeight); + if (debug) + printk + ("v3tv: Overlay Stride: %d vidInStride: %d Bytes: %d\n", + voodoo->vidOverlayStride, voodoo->vidInStride, + voodoo->vidScanlineBytes); + + if (voodoo->XDecim) { + + v3tv_conf(Horizontal_Decimation_Enable, + CONF_SIZE(Horizontal_Decimation_Enable)); + } else { + v3tv_conf(Horizontal_Decimation_Disable, + CONF_SIZE(Horizontal_Decimation_Disable)); + } + + if (voodoo->YDecim) { + v3tv_conf(Vertical_Decimation_Enable, + CONF_SIZE(Vertical_Decimation_Enable)); + } else { + v3tv_conf(Vertical_Decimation_Disable, + CONF_SIZE(Vertical_Decimation_Disable)); + } + + if (voodoo->dudx) { + v3tv_conf(Horizontal_Magnification_Enable, + CONF_SIZE(Horizontal_Magnification_Enable)); + } else { + v3tv_conf(Horizontal_Magnification_Disable, + CONF_SIZE(Horizontal_Magnification_Disable)); + } + + if (voodoo->dvdy) { + v3tv_conf(Vertical_Magnification_Enable, + CONF_SIZE(Vertical_Magnification_Enable)); + } else { + v3tv_conf(Vertical_Magnification_Disable, + CONF_SIZE(Vertical_Magnification_Disable)); + } +} + +void v3tv_set_overlay(struct voodoo_data *voodoo) +{ + voodoo->vidOverlayStartX = voodoo->capture_win.x; + voodoo->vidOverlayStartY = voodoo->capture_win.y; + voodoo->vidOverlayEndX = + voodoo->capture_win.x + voodoo->capture_win.width; + voodoo->vidOverlayEndY = + voodoo->capture_win.y + voodoo->capture_win.height; + voodoo->vidOrigWidth = video_standard[voodoo->norm].width; + voodoo->vidOrigHeight = video_standard[voodoo->norm].height; + v3tv_update_overlay_setup(voodoo); +} + +void v3tv_overlay_setup(struct voodoo_data *voodoo) +{ + unsigned int value = 0; + static int dstFormat; + + voodoo->vidInStride = video_standard[voodoo->norm].width * 2; + + value = v3tv_regread(V3REG_vidScreenSize); + voodoo->desktopWidth = value & 0xFFF; + voodoo->desktopHeight = (value >> 12) & 0xFFF; + + voodoo->desktopAddr = + v3tv_regread(V3REG_vidDesktopStartAddr) & 0xFFFFFF; + voodoo->desktopStride = + v3tv_regread(V3REG_vidDesktopOverlayStride) & 0x7FFF; + + /* + * Allocate memory for video capture and triple buffering overlay + * Start from the top of video memory, but leave 4K left for the hw mouse cursor. + * FIXME: We need to make this more intelligent, because the X-server might use + * memory differently in the future... + */ + voodoo->vidInAddr2 = + 0xFFF000 - + video_standard[voodoo->norm].width * + (video_standard[voodoo->norm].height * 2); + voodoo->vidInAddr1 = + voodoo->vidInAddr2 - + video_standard[voodoo->norm].width * + (video_standard[voodoo->norm].height * 2); + voodoo->vidInAddr0 = + voodoo->vidInAddr1 - + video_standard[voodoo->norm].width * + (video_standard[voodoo->norm].height * 2); + voodoo->captureBuf = + voodoo->vidInAddr0 - + (video_standard[voodoo->norm].width * 2) * + (video_standard[voodoo->norm].height * 4) * 3; + + v3tv_regwrite(V3REG_vidInAddr0, voodoo->vidInAddr0); + v3tv_regwrite(V3REG_vidInAddr1, voodoo->vidInAddr1); + v3tv_regwrite(V3REG_vidInAddr2, voodoo->vidInAddr2); + + dstFormat = (v3tv_regread(V3REG_dstFormat) >> 16) & 0x07; + + if (debug) { + printk + ("v3tv: %d x %d sized %sbpp desktop at vidmem address 0x%x\n", + voodoo->desktopWidth, voodoo->desktopHeight, + (dstFormat == 1) ? "8" : (dstFormat == + 3) ? "16" : (dstFormat == + 4) ? "24" + : (dstFormat == 5) ? "32" : "unknown-", + voodoo->desktopAddr); + printk("v3tv: Desktop stride is %d bytes.\n", + voodoo->desktopStride); + printk("v3tv: Video in stride is %d bytes.\n", + voodoo->vidInStride); + printk("v3tv: Capture buffer at vidmem 0x%x\n", + voodoo->captureBuf); + printk("v3tv: Triple buffers at 0x%x, 0x%x, 0x%x\n", + voodoo->vidInAddr0, voodoo->vidInAddr1, + voodoo->vidInAddr2); + } + + /* FIXME: Sometimes, the OverlayStride is incorrect. Check for this */ + value = v3tv_regread(V3REG_vidDesktopOverlayStride); + value = + (value & 0x8000FFFF) + + ((voodoo->vidInStride << 16) & 0x7FFF0000); + + v3tv_regwrite(V3REG_vidDesktopOverlayStride, value); + v3tv_regwrite(V3REG_vidOverlayDudxOffsetSrcWidth, + (voodoo->vidInStride) << 19); + + v3tv_set_overlay(voodoo); +} + +void v3tv_overlay(struct voodoo_data *voodoo, int i) +{ + int tempReg; + if (i == 0) { + v3tv_conf(Overlay_Disable, CONF_SIZE(Overlay_Disable)); +// voodoo3_conf(Disable_VMI_Interrupt, CONF_SIZE(Disable_VMI_Interrupt)); + + tempReg = v3tv_regread(V3REG_intrCtrl); + if (debug) + printk("v3tv: intrCtrl: %x\n", tempReg); + tempReg &= ~(1 << 21); + if (debug) + printk("v3tv: write back intrCtrl: %x " + "VMI interupts disabled\n", tempReg); + v3tv_regwrite(V3REG_intrCtrl, tempReg); + } else { + v3tv_conf(Overlay_Enable, CONF_SIZE(Overlay_Enable)); + + /* Overlay filtering must be turned off, if video processor + * is set to 2x mode. This makes the overlay look bad, + * so we disable filtering only when really needed. + */ + if (v3tv_regread(V3REG_vidProcCfg) & (1 << 26)) { + v3tv_conf(Overlay_Filter_Off, + CONF_SIZE(Overlay_Filter_Off)); + if (debug) + printk(KERN_WARNING + "v3tv: Overlay filter disabled\n"); + } else { + v3tv_conf(Overlay_Filter_On, + CONF_SIZE(Overlay_Filter_On)); + if (debug) + printk(KERN_WARNING + "v3tv: Overlay filter enabled\n"); + } +// voodoo3_conf(Enable_VMI_Interrupt, CONF_SIZE(Enable_VMI_Interrupt)); + tempReg = v3tv_regread(V3REG_intrCtrl); + if (debug) + printk("v3tv: intrCtrl: %x\n", tempReg); + tempReg |= (1 << 21); + tempReg |= (1 << 31); + if (debug) + printk("v3tv: write back intrCtrl: %x " + "VMI interupts enable; PCI interupt is inactive\n", + tempReg); + v3tv_regwrite(V3REG_intrCtrl, tempReg); + + } + if (debug) + printk(KERN_INFO "v3tv: Overlay %s\n", + (i) ? "Enabled" : "Disabled"); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ +/* + * voodoo-capture.c + * + * Video capture routines for Voodoo3 3500 TV. + * + * $Id: v3tv.c,v 1.1 2005/12/25 16:23:58 mkrufky Exp $ + */ + +/* FIXME + * Here's a loop that is somewhat CPU speed dependent. + */ +/* Make room (wait for it to become available) in the PCI FIFO buffer */ +int v3tv_MakeRoom(int num) +{ + int i = 0; + + while ((v3tv_regread(V3REG_status) & 0x1F) < num) { + if (i++ > 0x100000) { + printk("v3tv: no room in voodoo3 FIFO: breakout!!!"); + return 0; + } + } + return 1; +} + +static int clip0Min; +static int clip0Max; +static int dstBaseAddr; +static int dstFormat; +static int commandExtra; +static int srcFormat; +static int srcSize; +static int srcXY; +static int dstSize; +static int dstXY; +static int command; +static int srcBaseAddr; + +/* FIXME + * This _will_ interfere with the X-server, at least on SMP boxes. + */ +void v3tv_2dreg(int i) +{ + if (i == VOODOO3_2D_SAVE) { + clip0Min = v3tv_regread(V3REG_clip0Min); + clip0Max = v3tv_regread(V3REG_clip0Max); + dstBaseAddr = v3tv_regread(V3REG_dstBaseAddr); + dstFormat = v3tv_regread(V3REG_dstFormat); + commandExtra = v3tv_regread(V3REG_commandExtra); + srcFormat = v3tv_regread(V3REG_srcFormat); + srcSize = v3tv_regread(V3REG_srcSize); + srcXY = v3tv_regread(V3REG_srcXY); + dstSize = v3tv_regread(V3REG_dstSize); + dstXY = v3tv_regread(V3REG_dstXY); + command = v3tv_regread(V3REG_command); + srcBaseAddr = v3tv_regread(V3REG_srcBaseAddr); + } else if (i == VOODOO3_2D_RESTORE) { + if (!v3tv_MakeRoom(12)) + printk("Error!!!\n"); + + v3tv_regwrite(V3REG_clip0Min, clip0Min); + v3tv_regwrite(V3REG_clip0Max, clip0Max); + v3tv_regwrite(V3REG_dstBaseAddr, dstBaseAddr); + v3tv_regwrite(V3REG_dstFormat, dstFormat); + v3tv_regwrite(V3REG_commandExtra, commandExtra); + v3tv_regwrite(V3REG_srcFormat, srcFormat); + v3tv_regwrite(V3REG_srcSize, srcSize); + v3tv_regwrite(V3REG_srcXY, srcXY); + v3tv_regwrite(V3REG_dstSize, dstSize); + v3tv_regwrite(V3REG_dstXY, dstXY); + v3tv_regwrite(V3REG_command, command); + v3tv_regwrite(V3REG_srcBaseAddr, srcBaseAddr); + } +} + +void v3tv_capture_frame(struct voodoo_data *voodoo_tv) +{ + int vidInStatus; + int isOdd; + u32 vidBuffer; + int srcSize; + int dstSize; + int cmd; + + vidInStatus = v3tv_regread(V3REG_vidInStatusCurrentLine); + vidInStatus >>= 16; + vidInStatus &= 7; + + isOdd = vidInStatus & 4; + + /* Only use odd frames for now */ + + switch (vidInStatus & 0x3) { + case 0: + /* Finished writing to vidInAddr0 */ +// if(isOdd) + vidBuffer = voodoo_tv->vidInAddr0; +// else +// vidBuffer = voodoo_tv->vidInAddr2; + break; + case 1: + /* Finished writing to vidInAddr1 */ +// if(isOdd) + vidBuffer = voodoo_tv->vidInAddr1; +// else +// vidBuffer = voodoo_tv->vidInAddr0; + break; + case 2: + /* Finished writing to vidInAddr2 */ +// if(isOdd) + vidBuffer = voodoo_tv->vidInAddr2; +// else +// vidBuffer = voodoo_tv->vidInAddr1; + break; + default: + /* Error */ + vidBuffer = 0; + break; + } + + if (!vidBuffer) + return; + + v3tv_2dreg(VOODOO3_2D_SAVE); + + if (!v3tv_MakeRoom(14)) { + printk("Error2\n"); + return; + } + + v3tv_set_palette(&voodoo_tv->picture_settings); + printk("v3tv: capture frame, palette=%d, V3REG_status= %x\n", + voodoo_tv->picture_settings.palette, v3tv_regread(V3REG_status)); + + v3tv_regwrite(V3REG_srcBaseAddr, (unsigned long)vidBuffer); + v3tv_regwrite(V3REG_dstBaseAddr, (unsigned long)voodoo_tv->captureBuf); +// v3tv_regwrite(V3REG_dstBaseAddr, 0); + + srcSize = + ((voodoo_tv-> + vidOrigWidth) & 0xFFF) | (((voodoo_tv-> + vidOrigHeight) << 16) & 0xFFF0000); + dstSize = + ((voodoo_tv->vidOverlayEndX - + voodoo_tv->vidOverlayStartX) & 0xFFF) | (((voodoo_tv->vidOverlayEndY - + voodoo_tv-> + vidOverlayStartY) << 16) & + 0xFFF0000); + + /* [16:9] = 9 == packed 4:2:2 UYVY */ + v3tv_regwrite(V3REG_srcFormat, + (voodoo_tv->vidInStride & 0xFFF) | (9 << 16)); + + v3tv_regwrite(V3REG_srcSize, srcSize); + v3tv_regwrite(V3REG_dstSize, dstSize); + v3tv_regwrite(V3REG_srcXY, 0); + v3tv_regwrite(V3REG_dstXY, 0); + +// v3tv_regwrite(V3REG_dstFormat, (2048 & 0xFFF) | (3 << 16)); + v3tv_regwrite(V3REG_clip0Min, 0); + v3tv_regwrite(V3REG_clip0Max, 0xFFFFFFFF); + + v3tv_regwrite(V3REG_commandExtra, 0); + cmd = 0xCC000002; + v3tv_regwrite(V3REG_command, cmd); /* Command: screen to screen stretch blt */ + + v3tv_regwrite(V3REG_launchArea, 0); + v3tv_2dreg(VOODOO3_2D_RESTORE); +} + +#if 0 +static int fbuffer_alloc() +{ + if (!voodoo->fbuffer) + voodoo->fbuffer = + (unsigned char *)rvmalloc(voodoo->gbuffers * + voodoo->gbufsize); + else + printk(KERN_ERR "v4l-v3tv: Double alloc of fbuffer!\n"); + + if (!voodoo->fbuffer) + return -ENOBUFS; + return 0; +} +#endif + +/* + * This maps the vmalloced and reserved fbuffer to user space. + * + * FIXME: + * - PAGE_READONLY should suffice!? + * - remap_page_range is kind of inefficient for page by page remapping. + * But e.g. pte_alloc() does not work in modules ... :-( + */ + +#if 0 +static int do_voodoo_mmap(const char *adr, unsigned long size) +{ + unsigned long start = (unsigned long)adr; + unsigned long page, pos; + + if (size > voodoo->gbuffers * voodoo->gbufsize) + return -EINVAL; + + if (!voodoo->fbuffer) { + if (fbuffer_alloc()) + return -EINVAL; + } + pos = (unsigned long)voodoo->fbuffer; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + return 0; +} +#endif + + /* + * Video4Linux functions for Voodoo3 3500 TV drivers. + * + * $Id: v3tv.c,v 1.1 2005/12/25 16:23:58 mkrufky Exp $ + */ + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) +#define iminor(inode) minor(inode->i_rdev) +#endif + +/* External variables: Data struct */ +//extern struct voodoo_data *voodoo; +//extern struct video_capture video_standard[]; + +static struct video_device v3tv_vdev_radio; +static struct video_device v3tv_vdev_video; + +static atomic_t users = { + counter:0 +}; /* Common for radio and TV, only one at a time possible */ + +/* Module parameters */ +//extern int debug; + +int video_nr; +int radio_nr; +int vbi_nr; + +static const char *v4l1_ioctls[] = { + "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", + "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", + "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", + "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", + "SMICROCODE", "GVBIFMT", "SVBIFMT" }; +#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) + +/* ----------------------------------------------------------------------- */ + +static struct v4l2_input v3tv_radio_input = { + .name = "Voodoo3 TV 3500 FM Tuner", + .type = V4L2_INPUT_TYPE_TUNER, + .audioset = 1, + .tuner = 1 +}; + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_queryctrl no_ctl = { + .name = "42", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; +static const struct v4l2_queryctrl v3tv_ctls[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 65535, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_BALANCE, + .name = "Balance", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_BASS, + .name = "Bass", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_TREBLE, + .name = "Treble", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + +const int V3TV_CTLS = ARRAY_SIZE(v3tv_ctls); + +static int get_control(struct voodoo_data *voodoo, struct v4l2_control *c) +{ + struct video_audio va; + int i; + + for (i = 0; i < V3TV_CTLS; i++) + if (v3tv_ctls[i].id == c->id) + break; + if (i == V3TV_CTLS) + return -EINVAL; + if (i >= 4 && i <= 10) { + memset(&va,0,sizeof(va)); + i2c_clients_command(voodoo->voodoo_i2c_adapter, VIDIOCGAUDIO, &va); + } + switch (c->id) { +/* + case V4L2_CID_BRIGHTNESS: + c->value = voodoo->bright; + break; + case V4L2_CID_HUE: + c->value = voodoo->hue; + break; + case V4L2_CID_CONTRAST: + c->value = voodoo->contrast; + break; + case V4L2_CID_SATURATION: + c->value = voodoo->saturation; + break; +*/ + case V4L2_CID_AUDIO_MUTE: + c->value = (VIDEO_AUDIO_MUTE & va.flags) ? 1 : 0; + break; + case V4L2_CID_AUDIO_VOLUME: + c->value = va.volume; + break; + case V4L2_CID_AUDIO_BALANCE: + c->value = va.balance; + break; + case V4L2_CID_AUDIO_BASS: + c->value = va.bass; + break; + case V4L2_CID_AUDIO_TREBLE: + c->value = va.treble; + break; +// case V4L2_CID_AUDIO_LOUDNESS: +// c->value = voodoo->loudness; +// break; + default: + return -EINVAL; + } + return 0; +} + +static int set_control(struct voodoo_data *voodoo, struct v4l2_control *c) +{ + struct video_audio va; + int i; + + for (i = 0; i < V3TV_CTLS; i++) + if (v3tv_ctls[i].id == c->id) + break; + if (i == V3TV_CTLS) + return -EINVAL; + if (i >= 4 && i <= 10) { + memset(&va,0,sizeof(va)); + i2c_clients_command(voodoo->voodoo_i2c_adapter, VIDIOCGAUDIO, &va); + } + switch (c->id) { +/* + case V4L2_CID_BRIGHTNESS: + bt848_bright(voodoo,c->value); + break; + case V4L2_CID_HUE: + bt848_hue(voodoo,c->value); + break; + case V4L2_CID_CONTRAST: + bt848_contrast(voodoo,c->value); + break; + case V4L2_CID_SATURATION: + bt848_sat(voodoo,c->value); + break; +*/ + case V4L2_CID_AUDIO_MUTE: + if (c->value) + va.flags |= VIDEO_AUDIO_MUTE; + else + va.flags &= ~VIDEO_AUDIO_MUTE; + break; + + case V4L2_CID_AUDIO_VOLUME: + va.volume = c->value; + break; + case V4L2_CID_AUDIO_BALANCE: + va.balance = c->value; + break; + case V4L2_CID_AUDIO_BASS: + va.bass = c->value; + break; + case V4L2_CID_AUDIO_TREBLE: + va.treble = c->value; + break; + default: + return -EINVAL; + } + if (i >= 4 && i <= 10) + i2c_clients_command(voodoo->voodoo_i2c_adapter, + VIDIOCSAUDIO, &va); + return 0; +} + +static void +i2c_vidiocschan(struct voodoo_data *voodoo) +{ + struct video_channel c; + + memset(&c,0,sizeof(c)); + c.norm = voodoo->norm; + c.channel = voodoo->input; + i2c_clients_command(voodoo->voodoo_i2c_adapter, VIDIOCSCHAN, &c); +} + +/* ----------------------------------------------------------------------- */ + +static int v3tv_radio_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct voodoo_data *voodoo_radio = file->private_data; + + switch (cmd) { + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *i = arg; + + printk("v3tv: VIDIOC_ENUMINPUT %d.\n",i->index); + if( i->index != 0 ) { + return -EINVAL; + } + memcpy(i, &v3tv_radio_input, sizeof(struct v4l2_input)); + + return 0; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *c = arg; + int i; + + printk("v3tv: VIDIOC_QUERYCTRL\n"); + if (c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) /* 'if (( ...' && + (c->id < V4L2_CID_PRIVATE_BASE || + c->id >= V4L2_CID_PRIVATE_LASTP1)) */ + return -EINVAL; + for (i = 0; i < V3TV_CTLS; i++) + if (v3tv_ctls[i].id == c->id) + break; + if (i == V3TV_CTLS) { + *c = no_ctl; + return 0; + } + *c = v3tv_ctls[i]; + if (i >= 4 && i <= 8) { + struct video_audio va; + memset(&va,0,sizeof(va)); + i2c_clients_command(voodoo_radio->voodoo_i2c_adapter, + VIDIOCGAUDIO, &va); + switch (v3tv_ctls[i].id) { + case V4L2_CID_AUDIO_VOLUME: + if (!(va.flags & VIDEO_AUDIO_VOLUME)) + *c = no_ctl; + break; + case V4L2_CID_AUDIO_BALANCE: + if (!(va.flags & VIDEO_AUDIO_BALANCE)) + *c = no_ctl; + break; + case V4L2_CID_AUDIO_BASS: + if (!(va.flags & VIDEO_AUDIO_BASS)) + *c = no_ctl; + break; + case V4L2_CID_AUDIO_TREBLE: + if (!(va.flags & VIDEO_AUDIO_TREBLE)) + *c = no_ctl; + break; + } + } + return 0; + } + case VIDIOC_G_CTRL: + { + printk("v3tv: VIDIOC_G_CTRL\n"); + return get_control(voodoo_radio,arg); + } + case VIDIOC_S_CTRL: + { + printk("v3tv: VIDIOC_S_CTRL\n"); + return set_control(voodoo_radio,arg); + } + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + printk("v3tv: VIDIOC_QUERYCAP\n"); + + strcpy(cap->driver,"v3tv radio"); + strcpy(cap->card,"Voodoo3 TV 3500"); + strcpy(cap->bus_info,"AGP Slot 0"); + cap->version = KERNEL_VERSION(0,1,0); + cap->capabilities = + V4L2_CAP_TUNER | + V4L2_CAP_RADIO; + return 0; + } + case VIDIOC_G_TUNER: + { + struct video_tuner vt; + struct video_audio va; + struct v4l2_tuner *t = arg; + + printk("v3tv: VIDIOC_G_TUNER\n"); + + if (0 != t->index) + return -EINVAL; +// down(&voodoo_radio->lock); + memset(t, 0, sizeof(struct v4l2_tuner)); + if (VOODOO3_MODEL_NTSC == voodoo_radio->model) + strcpy(t->name, "FM Radio"); + else + strcpy(t->name, "TV Tweaked to FM Radio"); + t->type = V4L2_TUNER_RADIO; + /* japan: 76.0 MHz - 89.9 MHz + * western europe: 87.5 MHz - 108.0 MHz + * russia: 65.0 MHz - 108.0 MHz */ + t->rangelow = 87 * 16; + t->rangehigh = 108 * 16; + t->capability = //V4L2_TUNER_CAP_LOW | + V4L2_TUNER_CAP_NORM | + V4L2_TUNER_CAP_STEREO; +// t->rxsubchans = V4L2_TUNER_SUB_MONO; + i2c_clients_command(voodoo_radio->voodoo_i2c_adapter, + VIDIOCGTUNER, &vt); + t->signal = vt.signal; + + /* Hmmm ... */ + /* FIXME: fill capability+audmode */ + memset(&va, 0, sizeof(struct video_audio)); + i2c_clients_command(voodoo_radio->voodoo_i2c_adapter, + VIDIOCGAUDIO, &va); + if(va.mode & VIDEO_SOUND_STEREO) { + t->audmode = V4L2_TUNER_MODE_STEREO; +// t->rxsubchans |= V4L2_TUNER_SUB_STEREO; + } + if(va.mode & VIDEO_SOUND_LANG1) { + t->audmode = V4L2_TUNER_MODE_LANG1; +// t->rxsubchans = V4L2_TUNER_SUB_LANG1 +// | V4L2_TUNER_SUB_LANG2; + } +// up(&voodoo_radio->lock); + return 0; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *t = arg; + + printk("v3tv: VIDIOC_S_TUNER\n"); + + if (0 != t->index) + return -EINVAL; +// down(&voodoo_radio->lock); + { + struct video_audio va; + memset(&va, 0, sizeof(struct video_audio)); + i2c_clients_command(voodoo_radio->voodoo_i2c_adapter, + VIDIOCGAUDIO, &va); + if (t->audmode == V4L2_TUNER_MODE_MONO) + va.mode = VIDEO_SOUND_MONO; + else if (t->audmode == V4L2_TUNER_MODE_STEREO) + va.mode = VIDEO_SOUND_STEREO; + else if (t->audmode == V4L2_TUNER_MODE_LANG1) + va.mode = VIDEO_SOUND_LANG1; + else if (t->audmode == V4L2_TUNER_MODE_LANG2) + va.mode = VIDEO_SOUND_LANG2; + i2c_clients_command(voodoo_radio->voodoo_i2c_adapter, + VIDIOCSAUDIO, &va); + } +// up(&voodoo_radio->lock); + return 0; + } + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + printk("v3tv: VIDIOC_G_FREQUENCY\n"); + + memset(f,0,sizeof(struct v4l2_frequency)); + f->type = V4L2_TUNER_RADIO; + f->frequency = voodoo_radio->freq; + if (copy_to_user(arg, &f, sizeof(struct v4l2_frequency))) + return -EFAULT; + return 0; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = arg; + int zero = 0; + + printk("v3tv: VIDIOC_S_FREQUENCY\n"); + + if (unlikely(f->tuner != 0)) + return -EINVAL; + if (unlikely(f->type != V4L2_TUNER_RADIO)) + return -EINVAL; +#if 0 + if (f->frequency < 45 * 16000) { +// if (f->frequency < 696000) { + printk ("v3tv: Radio program does not support the VIDEO_TUNER_LOW flag\n"); + printk("v3tv: Multiplying by 1000 to get %d\n", 1000 * (f->frequency / 16)); + f->frequency *= 1000; + } + voodoo_radio->freq = f->frequency/1000; +#endif + voodoo_radio->freq = f->frequency; + printk("v3tv: radio frequency %ld\n", voodoo_radio->freq); + +// down(&voodoo_radio->lock); + i2c_clients_command(voodoo_radio->voodoo_i2c_adapter, + AUDC_SET_RADIO, &zero); + i2c_clients_command(voodoo_radio->voodoo_i2c_adapter, + VIDIOCSFREQ, &voodoo_radio->freq); + +// up(&voodoo_radio->lock); + return 0; + } +#if 0 // obsolete v4l1 ioctl's + case VIDIOCGCAP: + { + struct video_capability vcap; + vcap.type = v3tv_vdev_radio.type; + vcap.channels = 1; + vcap.audios = 1; + vcap.maxwidth = 0; + vcap.minwidth = 0; + vcap.maxheight = 0; + vcap.minheight = 0; + strcpy(vcap.name, v3tv_vdev_radio.name); + if (copy_to_user(arg, &vcap, sizeof(vcap))) + return -EFAULT; + if (debug) + printk("v3tv: Getting radio capabilities.\n"); + break; + } + case VIDIOCGTUNER: + { + struct video_tuner vtune; + if (copy_from_user(&vtune, arg, sizeof(vtune))) + return -EFAULT; + if (vtune.tuner) + return -EINVAL; + vtune.rangelow = 87 * 16000; + vtune.rangehigh = 108 * 16000; + vtune.flags = VIDEO_TUNER_LOW; + vtune.mode = VIDEO_MODE_AUTO; + vtune.signal = 0xFFFF; /* FIXME: We should really check the signal */ + if (voodoo_radio->model == VOODOO3_MODEL_NTSC) + strcpy(vtune.name, "FM"); + else + strcpy(vtune.name, "TV Tweaked to FM"); + + if (copy_to_user(arg, &vtune, sizeof(vtune))) + return -EFAULT; + if (debug) + printk + ("v3tv: Getting radio tuner capabilities.\n"); + break; + } + case VIDIOCSTUNER: + { + struct video_tuner vtune; + if (copy_from_user(&vtune, arg, sizeof(vtune))) + return -EFAULT; + if (vtune.tuner) + return -EINVAL; + if (debug) + printk("v3tv: Selecting radio tuner 0.\n"); + break; + } + case VIDIOCGFREQ: + { + if (copy_to_user(arg, &voodoo_radio->freq, sizeof(u32))) + return -EFAULT; + break; + } + case VIDIOCSFREQ: + { + u32 freq; + int zero = 0; + + if (copy_from_user(&freq, arg, sizeof(u32))) + return -EFAULT; + if (freq < 45 * 16000) { + printk ("v3tv: Radio program does not support the VIDEO_TUNER_LOW flag\n"); + printk("v3tv: Multiplying by 1000 to get %d\n", 1000 * (freq / 16)); + freq *= 1000; + } + + voodoo_radio->freq = freq/1000; + /* FIXME. This isn't right! + * We should really use the radio mode of the MSP processor, + * but apparently the PAL version of the Voodoo3 doesn't + * support this. So we use mono FM TV-sound in stead. + * + * FIXME again. The radio mutes when we change frequency. + * + * FIXME x 3: Force tweaked radio to use FM mono 5.5. MHz. + */ + i2c_clients_command(voodoo->voodoo_i2c_adapter, + AUDC_SET_RADIO, &zero); + i2c_clients_command(voodoo->voodoo_i2c_adapter, + VIDIOCSFREQ, &zero); + + i2c_clients_command(voodoo->voodoo_i2c_adapter, + AUDC_SET_RADIO, &zero); + i2c_clients_command(voodoo->voodoo_i2c_adapter, VIDIOCSFREQ, + &voodoo_radio->freq); + + break; + } + case VIDIOCGAUDIO: + { + struct video_audio va; + + if (copy_from_user(&va, arg, sizeof(va))) + return -EFAULT; + if (va.audio) { + if (debug) + printk + ("v3tv: FIXME: VIDIOCGAUDIO: Audio=%d\n", + va.audio); + va.audio = 0; + // return -EINVAL; + } + i2c_clients_command(voodoo->voodoo_i2c_adapter, + VIDIOCGAUDIO, &va); + + va.mode = VIDEO_SOUND_MONO; + va.flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME + | VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE; + // FIXME: Add support for these. + if (copy_to_user(arg, &va, sizeof(va))) + return -EFAULT; + + break; + } + case VIDIOCSAUDIO: + { + struct video_audio va; + + if (copy_from_user(&va, arg, sizeof(va))) + return -EFAULT; + if (va.audio) { + if (debug) + printk + ("v3tv: FIXME: VIDIOSAUDIO: Audio=%d\n", + va.audio); + // FIXME. This should really be checked. + // If only user programs respected it :-( + return -EINVAL; + } + // va.mode = 0; + // FIXME. Force msp3400 _not_ to change stereo mode. + // FIXME. See that muting is handled properly. + i2c_clients_command(voodoo->voodoo_i2c_adapter, + VIDIOCSAUDIO, &va); + + break; + } +#endif + default: + if (debug > 1) { + switch (_IOC_TYPE(cmd)) { + case 'v': + printk("v3tv: unsuported ioctl 0x%x (v4l1, VIDIOC%s)\n", + cmd, (_IOC_NR(cmd) < V4L1_IOCTLS) ? + v4l1_ioctls[_IOC_NR(cmd)] : "???"); + break; + case 'V': + printk("v3tv: unsupported ioctl 0x%x (v4l2, %s)\n", + cmd, v4l2_ioctl_names[_IOC_NR(cmd)]); + break; + default: + printk("v3tv: unknown ioctl 0x%x (\?\?\?)\n", + cmd); + } + } + return -ENOIOCTLCMD; + } + return 0; +} + +static int v3tv_radio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, v3tv_radio_do_ioctl); +} + +static int v3tv_radio_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + + if (debug) + printk(KERN_DEBUG "v3tv: open minor=%d\n",minor); + + if (voodoo->radio_dev->minor != minor) + return -ENODEV; + + if (debug) + printk(KERN_DEBUG "v3tv: open called (radio)\n"); + +// down(&voodoo->lock); + if (users.counter) { + printk("v3tv: Radio device busy\n"); +// up(&voodoo->lock); + return -EBUSY; + } + atomic_inc(&users); + file->private_data = voodoo; + + voodoo->input = 0; + i2c_vidiocschan(voodoo); + + if (voodoo->tuner_type != UNSET) { + struct tuner_setup tun_setup; + + tun_setup.mode_mask = T_RADIO | T_ANALOG_TV; + tun_setup.type = voodoo->tuner_type; + tun_setup.addr = ADDR_UNSET; + + i2c_clients_command(voodoo->voodoo_i2c_adapter, + TUNER_SET_TYPE_ADDR, &tun_setup); + } + + i2c_clients_command(voodoo->voodoo_i2c_adapter, + AUDC_SET_RADIO, &voodoo->tuner_type); +// up(&voodoo->lock); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + try_module_get(THIS_MODULE); +#else + MOD_INC_USE_COUNT; +#endif + printk("v3tv: Radio device opened.\n"); + return 0; +} + +static int v3tv_radio_release(struct inode *inode, struct file *file) +{ +/* Use these in i2c_clients_command() in future */ +// struct video_device *dev = video_devdata(file); +// struct voodoo_data *voodoo_radio = dev->priv; + + atomic_dec(&users); + + file->private_data = NULL; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + module_put(THIS_MODULE); +#else + MOD_DEC_USE_COUNT; +#endif + + printk("v3tv: Radio device closed.\n"); + + return 0; +} + +// static struct wait_queue *capture_wait; +static volatile int capture_ready = 0; + +static volatile int irq_counter = 0; +static volatile int idles = 0; +static volatile int timeout = 0; + +#define DEBUG_LPT 0x378 +#include <asm/delay.h> + +static void v3tv_irq_printk(long tempReg) +{ + /* 1111.0000.0000.0000.0001.1111.1011.1111 f0001fbf * page 188 + 1f * [4] x1f = FIFO emptyi + + 0 * [5] x20 = FIFO busy + 1 * [6] x40 = Vertical retrace inactive (1) + 0 * [7] x80 = FBI grph eng busy + + 0 * [8] x100 = TREX busy + 1 * [9] x200 = Avenger busy + 1 * [10] x400 = 2D busy + 1 * [11] x800 = FIFO 0 busy + + 0 * [12] x1000 = FIFO 1 busy + * [27:13] reserved + + * [30:28] Swap Buffers Pending + * [31] PCI Interupt Generated */ + + printk(KERN_WARNING "v3tv: IRQ: Timeout %d : %d : %d\n", + timeout, idles, irq_counter); + printk(KERN_WARNING "v3tv: IRQ: V3REG_status: 0x%lx FIFO: 0x%lx%s%s\n", + tempReg, tempReg & 0x1f, + (tempReg & 0x20) ? "; FIFO busy" : "", + (tempReg & 0x40) ? "; Vertical retrace inactive" : + "; Vertical retrace active"); + printk(KERN_WARNING "v3tv: IRQ: %s%s%s%s%s%s\n", + (tempReg & 0x80) ? "; FBI grph eng busy" : "", + (tempReg & 0x100) ? "; TREX busy" : "", + (tempReg & 0x200) ? "; Avenger busy" : "", + (tempReg & 0x400) ? "; 2D busy" : "", + (tempReg & 0x800) ? "; FIFO 0 busy" : "", + (tempReg & 0x1000) ? "; FIFO 1 busy" : ""); + printk(KERN_WARNING "v3tv: IRQ: Swap Buffers Pending: 0x%lx; %s\n", + (tempReg >> 28) & 7, + (tempReg & 80000000) ? "PCI Interupt Generated" : ""); +} + +// 2.4.23 void (*handler)(int, void *, struct pt_regs *), +// 2.6.1 irqreturn_t (*handler)(int, void *, struct pt_regs *), + +static irqreturn_t v3tv_video_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + long tempReg; + idles = 0; + timeout = 0; + + capture_ready = 1; + + irq_counter++; + if ((irq_counter % 10000) == 0) + printk(KERN_WARNING "v3tv: IRQ: %d interrupts\n", irq_counter); + + v3tv_regwrite(V3REG_command, 0x100); /* Immediate NOP */ +// voodoo3_regwrite (V3REG_launchArea, 0); +// voodoo3_regwrite (V3REG_IO_BASE_2 + 0x120, 0); + + while (((tempReg = v3tv_regread(V3REG_status)) & 0x7BF) != 0x1F) { + timeout++; + udelay(1000); + if (timeout < 10) + continue; + + v3tv_irq_printk(tempReg); + + timeout = 0; + idles++; + if (idles < 3) + continue; + else { +/* + The Voodoo3 may be in an unstable state, so we try to bailout gracefully. + First disable the VMI Interupt, so we don't get here again. +*/ + capture_ready = 0; + + v3tv_conf(Disable_VMI_Interrupt, CONF_SIZE(Disable_VMI_Interrupt)); + +/* We're not supposed to call free_irq from interrupt context, leave it till _close */ +/* free_irq(voodoo->dev->irq, voodoo->dev); + voodoo->initialized &= ~I_irq; + + voodoo->decoder_enabled = 0; + i2c_clients_command(voodoo->voodoo_i2c_adapter, + DECODER_ENABLE_OUTPUT, &voodoo->decoder_enabled); + + struct video_audio va; + + i2c_clients_command(voodoo->voodoo_i2c_adapter, VIDIOCGAUDIO, &va); + + va.volume = 0; + va.balance = 32768; + i2c_clients_command(voodoo->voodoo_i2c_adapter, VIDIOCSAUDIO, &va); +*/ + +/* Optionaly disable Video In. */ +// v3tv_conf(VMI_Disable, CONF_SIZE(VMI_Disable)); + break; + } + } + + tempReg = v3tv_regread(V3REG_intrCtrl); + if ((tempReg & (1 << 23)) || (capture_ready == 0)) { + tempReg &= ~(1 << 23); + tempReg |= (1 << 31); + v3tv_regwrite(V3REG_intrCtrl, tempReg); + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + return capture_ready; +#else + return; +#endif +} + +void v3tv_set_palette(struct video_picture *picture_settings) +{ + switch ((v3tv_regread(V3REG_dstFormat) >> 16) & 0x07) { + case 4: /* 24bpp */ + case 5: /* 32 bpp - tdfxfb runs at 32 ?? */ + { + picture_settings->palette = VIDEO_PALETTE_RGB24; + picture_settings->depth = 24; + if (debug) + printk + ("v3tv: palette: VIDEO_PALETTE_RGB24 = 24 bpp\n"); + break; + } + case 1: /* 8bpp - we are making an asumption so things can go forward */ + case 3: /* 16bpp */ + default: + { + picture_settings->palette = VIDEO_PALETTE_RGB565; + picture_settings->depth = 16; + if (debug) + printk + ("v3tv: palette: VIDEO_PALETTE_RGB565 = 16 bpp\n"); + break; + } + } +} + +static int v3tv_video_open(struct inode *inode, struct file *file) +{ + int result; + int minor = iminor(inode); + enum v4l2_buf_type type = 0; + + if (debug) + printk(KERN_DEBUG "v3tv: open minor=%d\n",minor); + + if (voodoo->vbi_dev && + voodoo->vbi_dev->minor == minor) + type = V4L2_BUF_TYPE_VBI_CAPTURE; + if (voodoo->video_dev && + voodoo->video_dev->minor == minor) + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (!type) + return -ENODEV; + + if (debug) + printk(KERN_DEBUG "v3tv: open called (type=%s)\n", + v4l2_type_names[type]); + + /* Check if we are already in use */ + if (users.counter) { + printk("v3tv: Video or radio device busy\n"); + return -EBUSY; + } + + /* Get irq. Interrupt is shared and disabled while processing */ + result = request_irq(voodoo->dev->irq, v3tv_video_irq, + SA_SHIRQ | SA_INTERRUPT, "v3tv", voodoo->dev); + + if (result == -EINVAL) { + printk(KERN_ERR "v3tv: Bad irq number or handler\n"); + return result; + } + + if (result == -EBUSY) { + printk(KERN_ERR + "v3tv: IRQ %d busy, change your PnP config in BIOS\n", + voodoo->dev->irq); + return result; + } + + if (result < 0) { + printk(KERN_ERR + "v3tv: Unknown error, code %d when getting interrupt\n", + result); + return -EINVAL; /* Return something that the kernel knows about, or it might Oops */ + } + + atomic_inc(&users); + voodoo->initialized |= I_irq; + + if (voodoo->tuner_type != UNSET) { + struct tuner_setup tun_setup; + + tun_setup.mode_mask = T_RADIO | T_ANALOG_TV; + tun_setup.type = voodoo->tuner_type; + tun_setup.addr = ADDR_UNSET; + + i2c_clients_command(voodoo->voodoo_i2c_adapter, + TUNER_SET_TYPE_ADDR, &tun_setup); + } + +// struct video_audio va; + +// i2c_clients_command(voodoo->voodoo_i2c_adapter, VIDIOCGAUDIO, &va); + +// va.flags = VIDEO_AUDIO_MUTE; +// va.volume = 65535; +// va.balance = 32768; /* FIXME. This shouldn't be necessary */ +// i2c_clients_command(voodoo->voodoo_i2c_adapter, VIDIOCSAUDIO, &va); + + v3tv_conf(VMI_Enable, CONF_SIZE(VMI_Enable)); + + v3tv_set_palette(&voodoo->picture_settings); + i2c_clients_command(voodoo->voodoo_i2c_adapter, DECODER_SET_PICTURE, + &voodoo->picture_settings); + + voodoo->decoder_enabled = 1; + i2c_clients_command(voodoo->voodoo_i2c_adapter, DECODER_ENABLE_OUTPUT, + &voodoo->decoder_enabled); + + file->private_data = voodoo; + if (debug) + printk("v3tv: video device opened.\n"); + return 0; +} + +static int v3tv_video_release(struct inode *inode, struct file *file) +{ + struct voodoo_data *voodoo_tv = file->private_data; + + if (voodoo_tv->initialized & I_irq) { + free_irq(voodoo_tv->dev->irq, voodoo_tv->dev); + voodoo_tv->initialized &= ~I_irq; + } + + voodoo_tv->decoder_enabled = 0; + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, DECODER_ENABLE_OUTPUT, + &voodoo_tv->decoder_enabled); + +// struct video_audio va; + +// i2c_clients_command(voodoo->voodoo_i2c_adapter, VIDIOCGAUDIO, &va); + +// va.volume = 0; +// va.balance = 32768; /* FIXME. This shouldn't be necessary */ +// i2c_clients_command(voodoo->voodoo_i2c_adapter, VIDIOCSAUDIO, &va); + + v3tv_conf(VMI_Disable, CONF_SIZE(VMI_Disable)); + + atomic_dec(&users); + + if (debug) + printk("v3tv: video device closed\n"); + + return 0; +} + +/* No poll now + +static int v4l_voodoo_poll (struct video_device*dev, + structfile *file, struct poll_table *wait) +{ + poll_wait(file, &capture_wait, wait); + + if(capture_read) + return POLLIN|POLLRDNORM; + return 0; +} +*/ + +static ssize_t v3tv_video_read(struct file *file, char *data, + size_t count, loff_t * ppos) +{ + struct voodoo_data *voodoo_tv = file->private_data; + + v3tv_capture_frame(voodoo_tv); +#if 0 + if(copy_to_user(buf, voodoo_2ddtv->fb_base + + (unsigned long) voodoo_tv->captureBuf, count)) + + if (debug) + printk( "v3tv: read count=%d type=%s " + "fb_base=0x%p captureBuf=0x%lx\n", + (int)count, v4l2_type_names[fh->type], voodoo_tv->fb_base, + (unsigned long) voodoo_tv->captureBuf); + return count; +#endif + return 0; +} + +#ifdef JustForRef +static ssize_t bttv_read(struct file *file, char *data, + size_t count, loff_t *ppos) +{ + struct bttv_fh *fh = file->private_data; + int retval = 0; + + if (fh->btv->errors) + bttv_reinit_bt848(fh->btv); + dprintk("bttv%d: read count=%d type=%s\n", + fh->btv->c.nr,(int)count,v4l2_type_names[fh->type]); + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (locked_btres(fh->btv,RESOURCE_VIDEO)) + return -EBUSY; + retval = videobuf_read_one(file, &fh->cap, data, count, ppos); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI)) + return -EBUSY; + retval = videobuf_read_stream(file, &fh->vbi, data, count, ppos, 1); + break; + default: + BUG(); + } + return retval; +} +#endif + +/* +static long v3tv_voodoo_read(struct video_device *dev, char *buf,unsigned long count ) +{ + struct wait_queue wait = { current, NULL}; + u8 *ptr; + int len; + int i; + add_wait_queue(&capture_wait, &wait); + while(!capture_ready) + { + if(file->flags&O_NDELAY) + { + remove_wait_queue(&capture_wait,&wait); + current->state = TASK_RUNNING; + return -EWOULDBLOCK; + } + if(signal_pending(current)) + { + remove_wait_queue(&capture_wait,&wait); + current->state = TASK_RUNNING; + return -ERESTARTSYS; + } + schedule(); + current->state = TASK_INTERRUPTIBLE; + } + + remove_wait_queue(&capture_wait, &wait); + + current->state = TASK_RUNNING; + capture_ready = 0; + ptr = (u8 *)buf; + len=capture_w * 3 * capture_h; + +// 24bit RGB + if(len>count) + len = count; +// Doesn't all fit + for(i=0; i<len; i++) + { + put_user(inb (io+IMAGE_DATA),ptr); + ptr++; + } + hardware_restart_capture(); + return i; +} +*/ + +//int v3tv_voodoo_mmap(struct video_device *dev, const char *adr, +// unsigned long size) +int v3tv_voodoo_mmap(struct file *file, struct vm_area_struct *vma) +{ +#if 0 + struct video_device *dev = video_devdata(file); + struct voodoo_data *voodoo_tv = dev->priv; + int r; + + down(&voodoo_tv->lock); + r = do_voodoo_mmap(adr, size); + up(&voodoo_tv->lock); + return r; +#endif + return -EINVAL; +} + +char *desc_pict_palette[] = { + [ VIDEO_PALETTE_GREY ] = "GREY", + [ VIDEO_PALETTE_HI240 ] = "HI240", + [ VIDEO_PALETTE_RGB565 ] = "RGB565", + [ VIDEO_PALETTE_RGB24 ] = "RGB24", + [ VIDEO_PALETTE_RGB32 ] = "RGB32", + [ VIDEO_PALETTE_RGB555 ] = "RGB555", + [ VIDEO_PALETTE_YUV422 ] = "YUV422", + [ VIDEO_PALETTE_YUYV ] = "YUYV", + [ VIDEO_PALETTE_UYVY ] = "UYVY", + [ VIDEO_PALETTE_YUV420 ] = "YUV420", + [ VIDEO_PALETTE_YUV411 ] = "YUV411", + [ VIDEO_PALETTE_RAW ] = "RAW", + [ VIDEO_PALETTE_YUV422P ] = "YUV422P", + [ VIDEO_PALETTE_YUV411P ] = "YUV411P", + [ VIDEO_PALETTE_YUV420P ] = "YUV420P", + [ VIDEO_PALETTE_YUV410P ] = "YUV410P", +}; + +static struct video_channel voodoo_channels[3] = { + { + .channel = 0, + .name = "TV Tuner", + .tuners = 1, + .flags = VIDEO_VC_TUNER | VIDEO_VC_AUDIO, + .type = VIDEO_TYPE_TV + }, + { + .channel = 1, + .name = "Composite input", + .tuners = 0, + .flags = VIDEO_VC_AUDIO, + .type = VIDEO_TYPE_TV + }, + { + .channel = 2, + .name = "S Video input", + .tuners = 0, + .flags = VIDEO_VC_AUDIO, + .type = VIDEO_TYPE_TV + } +}; + +static int v3tv_video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct voodoo_data *voodoo_tv = file->private_data; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability *vcap = arg; + + if (debug) + printk("v3tv: VIDIOCGCAP\n"); + + vcap->type = v3tv_vdev_video.type; + vcap->channels = 3; + vcap->audios = 0; // FIXME: Add audio support. + vcap->maxwidth = 1280; + vcap->minwidth = 16; + vcap->maxheight = 960; + vcap->minheight = 16; + strcpy(vcap->name, v3tv_vdev_video.name); + + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *vchan = arg; + + if (vchan->channel > 2) + return -EINVAL; + +// vchan->channel = voodoo_channels[vchan->channel].channel; + vchan->tuners = voodoo_channels[vchan->channel].tuners; + vchan->flags = voodoo_channels[vchan->channel].flags; + vchan->type = voodoo_channels[vchan->channel].type; + vchan->norm = voodoo_tv->norm; + strcpy(vchan->name, voodoo_channels[vchan->channel].name); + + if (debug) + printk("v3tv: VIDIOCGCHAN channel=%d %s\n", + vchan->channel, vchan->name); + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *vchan = arg; + __u16 sarg = 0; + + if (debug) + printk + ("v3tv: VIDIOCSCHAN: channel=%d, norm=%d\n", + vchan->channel, vchan->norm); + if (vchan->channel > 2) + return -EINVAL; + if (vchan->channel == voodoo_tv->input && + vchan->norm == voodoo_tv->norm) + return 0; + if ((vchan->norm != VIDEO_MODE_PAL) && + (vchan->norm != VIDEO_MODE_NTSC) && + (vchan->norm != VIDEO_MODE_SECAM)) + return -EINVAL; + + voodoo_tv->channel = vchan->channel; + voodoo_tv->norm = vchan->norm; + voodoo_channels[vchan->channel].norm = voodoo_tv->norm; + + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, + DECODER_SET_INPUT, + &vchan->channel); + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, + DECODER_SET_NORM, + &voodoo_tv->norm); + + v3tv_overlay_setup(voodoo_tv); + + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, + VIDIOCSCHAN, &vchan); + + sarg = (voodoo_tv->channel) ? AUDIO_EXTERN_2 : + AUDIO_TUNER; + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, + AUDC_SET_INPUT, &sarg); + + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *pic = arg; + + if (debug) + printk("v3tv: VIDIOCGPICT\n"); + + /* Return picture information here */ + pic->brightness = voodoo_tv->picture_settings.brightness; + pic->hue = voodoo_tv->picture_settings.hue; + pic->colour = voodoo_tv->picture_settings.contrast; + pic->contrast = voodoo_tv->picture_settings.colour; + pic->whiteness = voodoo_tv->picture_settings.whiteness; + pic->depth = voodoo_tv->picture_settings.depth; + pic->palette = voodoo_tv->picture_settings.palette; + + if (debug) + printk(KERN_INFO + "v3tv: VIDIOCGPICT, video_picture-> " + "brightness: 0x%04x hue: 0x%04x " + "contrast: 0x%04x colour: 0x%04x " + "w: 0x%04x\n", + voodoo_tv->picture_settings.brightness, + voodoo_tv->picture_settings.hue, + voodoo_tv->picture_settings.contrast, + voodoo_tv->picture_settings.colour, + voodoo_tv->picture_settings.whiteness); + if (debug) + printk(KERN_INFO + "v3tv: VIDIOCGPICT, depth = %d, " + "palette = %d %s\n", + voodoo_tv->picture_settings.depth, + voodoo_tv->picture_settings.palette, + desc_pict_palette[ + voodoo_tv->picture_settings.palette] ); + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *pic = arg; + + if (debug) + printk(KERN_INFO + "v3tv: VIDIOCSPICT, video_picture-> " + "brightness: 0x%04x hue: 0x%04x " + "contrast: 0x%04x colour: 0x%04x " + "w: 0x%04x\n", + pic->brightness, pic->hue, pic->contrast, + pic->colour, pic->whiteness); + if (debug) + printk(KERN_INFO + "v3tv: VIDIOCSPICT, depth = %d, " + "palette = %d %s\n", + pic->depth, pic->palette, + desc_pict_palette[pic->palette]); +#if 1 + /* Set picture parameters here */ + if ((pic->palette != VIDEO_PALETTE_RGB565) && + (pic->palette != VIDEO_PALETTE_RGB24) && + (pic->palette != VIDEO_PALETTE_RGB32)) + v3tv_set_palette(pic); +#else + if (pic->palette != voodoo_tv->picture_settings.palette) { + pic->palette = voodoo_tv->picture_settings.palette; + printk(KERN_INFO + "v3tv: VIDIOCSPICT: pallete not supported\n"); + return -EINVAL; + } +#endif + voodoo_tv->picture_settings.brightness = pic->brightness; + voodoo_tv->picture_settings.hue = pic->hue; + voodoo_tv->picture_settings.contrast = pic->contrast; + voodoo_tv->picture_settings.colour = pic->colour; + voodoo_tv->picture_settings.whiteness = pic->whiteness; + voodoo_tv->picture_settings.depth = pic->depth; +// voodoo_tv->picture_settings.palette = pic->palette; + + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, + DECODER_SET_PICTURE, + &voodoo_tv->picture_settings); + return 0; + } + + case VIDIOCGWIN: + { + /* return the current capture window here */ + struct video_window *win = arg; + + memset(win,0,sizeof(*win)); + if (debug) + (printk("v3tv: VIDIOCGWIN: %d, %d, %d, %d\n", + voodoo_tv->capture_win.x, + voodoo_tv->capture_win.y, + voodoo_tv->capture_win.width, + voodoo_tv->capture_win.height)); + + win->x = voodoo_tv->capture_win.x; + win->y = voodoo_tv->capture_win.y; + win->width = voodoo_tv->capture_win.width; + win->height = voodoo_tv->capture_win.height; + return 0; + } + + case VIDIOCSWIN: + { + struct video_window *win = arg; + unsigned long chroma; + unsigned long R, G, B; + + /* set the capture window here */ + + if (win->width > 1280 || win->height > 960) + return -EINVAL; + if (win->width < 16 || win->height < 16) + return -EINVAL; + + /* FIXME: an ugly hack to get chroma working */ + + R = (win->chromakey & 0xFF0000) >> 16; + G = (win->chromakey & 0x00FF00) >> 8; + B = (win->chromakey & 0x0000FF); + + // Stop the seq_fault Divide by zero. + if (voodoo_tv->fb.width == 0) { + printk + ("v3tv: Frame buffer width is 0! divide by zero in VIDIOCSWIN"); + return -EINVAL; + } + switch (voodoo_tv->fb.bytesperline / voodoo_tv->fb.width) { + case 1: + R = R >> 5; + G = G >> 5; + B = B >> 6; + + chroma = 0; + chroma |= R & 0x7; + chroma |= (G << 3) & 0x38; + chroma |= (B << 6) & 0xC0; + + break; + case 2: + R = R >> 3; + G = G >> 2; + B = B >> 3; + + chroma = 0; + chroma |= ((R << 11) & 0xF800); + chroma |= ((G << 5) & 0x7E0); + chroma |= (B & 0x1F); + + break; + default: + chroma = win->chromakey; + break; + } + + voodoo_tv->capture_win.x = win->x; + voodoo_tv->capture_win.y = win->y; + voodoo_tv->capture_win.width = win->width; + voodoo_tv->capture_win.height = win->height; + voodoo_tv->capture_win.chromakey = win->chromakey; + voodoo_tv->chroma = chroma; + + v3tv_set_chromakey(voodoo_tv); + v3tv_set_overlay(voodoo_tv); + + if (debug) + printk + ("v3tv: VIDIOCSWIN: %d, %d, %d, %d\n", + voodoo_tv->capture_win.x, + voodoo_tv->capture_win.y, + voodoo_tv->capture_win.width, + voodoo_tv->capture_win.height); + + return 0; + } + + case VIDIOCCAPTURE: + { + /* turn on/off overlay here */ + int *on = arg; + + if (debug) + printk("v3tv: VIDIOCCAPTURE %s\n", + (*on) ? "Enable" : "Disable"); +/* if (v) + if(capture_fb.width == 0 || capture_w == 0) + return -EINVAL; +*/ + v3tv_overlay(voodoo_tv, *on); + return 0; + } + + case VIDIOCSFBUF: + { + struct video_buffer *fbuf = arg; + + voodoo_tv->fb.base = fbuf->base; + voodoo_tv->fb.height = fbuf->height; + voodoo_tv->fb.width = fbuf->width; + voodoo_tv->fb.depth = fbuf->depth; + voodoo_tv->fb.bytesperline = fbuf->bytesperline; + + if (debug) + printk + ("v3tv: VIDIOCSFBUF: base 0x%p height: %d " + "width %d depth %d bytesperline %d\n", + voodoo_tv->fb.base, voodoo_tv->fb.height, + voodoo_tv->fb.width, voodoo_tv->fb.depth, + voodoo_tv->fb.bytesperline); + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer *fbuf = arg; + + fbuf->base = voodoo_tv->fb.base; + fbuf->height = voodoo_tv->fb.height; + fbuf->width = voodoo_tv->fb.width; + fbuf->depth = voodoo_tv->fb.depth; + fbuf->bytesperline = voodoo_tv->fb.bytesperline; + + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *tuner_info = arg; + if (debug) + printk("v3tv: VIDIOCGTUNER\n"); + + if (tuner_info->tuner != 0) + return -EINVAL; + + strcpy(tuner_info->name, "TV Tuner"); + tuner_info->rangelow = 0; + tuner_info->rangehigh = 0x7FFFFFFF; + tuner_info->flags = + VIDEO_TUNER_PAL | VIDEO_TUNER_NTSC | + VIDEO_TUNER_SECAM; + /* XXX VIDEO_TUNER_STEREO_ON Tuner is seeing stereo, what is RDS and MBS? */ + tuner_info->mode = voodoo_tv->norm; + /* Get signal strength from tuner */ + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, + VIDIOCGTUNER, tuner_info); + + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *tuner_info = arg; + + if (tuner_info->tuner) /* Only tuner 0 */ + return -EINVAL; + voodoo_tv->norm = tuner_info->mode; + /* Get signal strength from tuner */ + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, + VIDIOCGTUNER, tuner_info); + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, + DECODER_SET_NORM, + &voodoo_tv->norm); + return 0; + } + + case VIDIOCGFREQ: + { + if (debug) + printk("v3tv: VIDIOCGFREQ\n"); + + /* get tuner frequency here */ + /* out: int tuner_frequency */ + if (put_user(arg, &voodoo_tv->freq)) { + if (debug) + printk("v3tv: VIDIOCGFREQ put_user failed!!\n"); + return -EFAULT; + } + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + struct video_channel vchan; + + if (debug) + printk("v3tv: VIDIOCSFREQ %ld\n", *freq); + /* set tuner frequency here */ + /* in: int tuner_frequency */ + + voodoo_tv->freq = *freq; + voodoo_tv->channel = 0; /* Force video input to tuner */ + vchan.channel = 0; + + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, + VIDIOCSCHAN, &vchan); + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, + VIDIOCSFREQ, freq); + return 0; + } + + case VIDIOCGAUDIO: + { + if (debug) + printk("v3tv: VIDIOCGAUDIO\n"); + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, + VIDIOCGAUDIO, (int *)arg); + + return 0; + } + case VIDIOCSAUDIO: + { + if (debug) + printk("v3tv: VIDIOCSAUDIO\n"); + i2c_clients_command(voodoo_tv->voodoo_i2c_adapter, + VIDIOCSAUDIO, (int *)arg); + + return 0; + } + + case VIDIOCSYNC: + { + if (debug) + printk("v3tv: VIDIOCSYNC not implemented.\n"); + return -EFAULT; +// int i; +// DECLARE_WAITQUEUE(wait, current); +// +// if(copy_from_user((void *)&i,arg,sizeof(int))) { +// printk("v3tv: VIDIOCSYNC couldn't copy from user.\n"); +// return -EFAULT; +// } +// +// if (i < 0 || i >= voodoo_tv->gbuffers) +// return -EINVAL; +// switch (voodoo_tv->gbuf[i].stat) +// { +// case GBUFFER_UNUSED: +// ret = -EINVAL; +// break; +// case GBUFFER_GRABBING: +// add_wait_queue(&voodoo_tv->capq, &wait); +// current->state = TASK_INTERRUPTIBLE; +// while(voodoo_tv->gbuf[i].stat==GBUFFER_GRABBING) { +// schedule(); +// if(signal_pending(current)) { +// remove_wait_queue(&voodoo_tv->capq, &wait); +// current->state = TASK_RUNNING; +// return -EINTR; +// } +// } +// remove_wait_queue(&voodoo_tv->capq, &wait); +// current->state = TASK_RUNNING; +// /* fall through */ +// case GBUFFER_DONE: +// case GBUFFER_ERROR: +// ret = (voodoo_tv->gbuf[i].stat == GBUFFER_ERROR) ? -EIO : 0; +// voodoo_tv->gbuf[i].stat = GBUFFER_UNUSED; +// } +// return ret; + } + + case VIDIOCMCAPTURE: + { + if (debug) + printk + ("v3tv: VIDIOCMCAPTURE not implemented.\n"); + return -EFAULT; +// struct video_mmap vm; +// int ret; +// if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) { +// printk("v3tv: VIDIOCMCAPTURE couldn't copy from user.\n"); +// return -EFAULT; +// } + +// down(&voodoo_tv->lock); +// ret = vgrab(voodoo_tv, &vm); +// up(&voodoo_tv->lock); +// return ret; + } + + case VIDIOCGMBUF: + { + if (debug) + printk("v3tv: VIDIOCMBUF not implemented.\n"); + return -EFAULT; +// struct video_mbuf vm; +// memset(&vm, 0 , sizeof(vm)); + +// vm.size=gbufsize*gbuffers; +// vm.frames=gbuffers; +// for (i = 0; i < gbuffers; i++) +// vm.offsets[i]=i*gbufsize; +// if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) +// return -EFAULT; +// return 0; + } + + default: + if (debug > 1) { + switch (_IOC_TYPE(cmd)) { + case 'v': + printk("v3tv: unsuported ioctl 0x%x (v4l1, VIDIOC%s)\n", + cmd, (_IOC_NR(cmd) < V4L1_IOCTLS) ? + v4l1_ioctls[_IOC_NR(cmd)] : "???"); + break; + case 'V': + printk("v3tv: unsupported ioctl 0x%x (v4l2, %s)\n", + cmd, v4l2_ioctl_names[_IOC_NR(cmd)]); + break; + default: + printk("v3tv: unknown ioctl 0x%x (\?\?\?)\n", + cmd); + } + } + return -ENOIOCTLCMD; + } + return 0; +} + +static int v3tv_video_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, v3tv_video_do_ioctl); +} + +static struct file_operations v3tv_vdev_radio_fops = { + .owner = THIS_MODULE, + .open = v3tv_radio_open, + .release = v3tv_radio_release, + .ioctl = v3tv_radio_ioctl, + .llseek = no_llseek +}; + +static struct video_device v3tv_vdev_radio = { + .owner = THIS_MODULE, + .name = "Voodoo3 3500 TV radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_V3TV_RADIO, /* FIXME: Define these in a .h file */ + .fops = &v3tv_vdev_radio_fops, + .minor = -1, +}; + +static struct file_operations v3tv_vdev_video_fops = { + .owner = THIS_MODULE, + .open = v3tv_video_open, + .release = v3tv_video_release, + .ioctl = v3tv_video_ioctl, + .llseek = no_llseek, + .read = v3tv_video_read, /* experimental read */ +// .mmap = v4l_voodoo_mmap, +// .poll = v4l_voodoo_poll, +}; + +static struct video_device v3tv_vdev_video = { + .owner = THIS_MODULE, + .name = "Voodoo3 3500 TV", + .type = + VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_OVERLAY | + VID_TYPE_CHROMAKEY | VID_TYPE_SCALES, + .hardware = VID_HARDWARE_V3TV, /* temporary id */ + .fops = &v3tv_vdev_video_fops, + .minor = -1, +}; + +static struct video_device v3tv_vdev_vbi = { + .owner = THIS_MODULE, + .name = "Voodoo3 3500 VBI Teletext", + .type = VID_TYPE_TUNER|VID_TYPE_TELETEXT, + .hardware = VID_HARDWARE_V3TV, /* temporary id */ + .fops = &v3tv_vdev_video_fops, + .minor = -1, +}; + +static void v3tv_unregister_video(struct voodoo_data *voodoo) +{ + if (voodoo->video_dev) { + if (-1 != voodoo->video_dev->minor) { + printk("v3tv: unregistering device video%d (%d)\n", + voodoo->video_dev->minor & 0x1f, + voodoo->video_dev->minor); + video_unregister_device(voodoo->video_dev); + } + voodoo->video_dev = NULL; + } + + if (voodoo->vbi_dev) { + if (-1 != voodoo->vbi_dev->minor) { + printk("v3tv: unregistering device vbi%d (%d)\n", + voodoo->vbi_dev->minor & 0x1f, + voodoo->vbi_dev->minor); + video_unregister_device(voodoo->vbi_dev); + } + voodoo->vbi_dev = NULL; + } + + if (voodoo->radio_dev) { + if (-1 != voodoo->radio_dev->minor) { + printk("v3tv: unregistering device radio%d (%d)\n", + voodoo->radio_dev->minor & 0x1f, + voodoo->radio_dev->minor); + video_unregister_device(voodoo->radio_dev); + } + voodoo->radio_dev = NULL; + } +} + +/* register video4linux devices */ +static int __devinit v3tv_register_video(struct voodoo_data *voodoo) +{ + /* video */ + voodoo->video_dev = &v3tv_vdev_video; + if (video_register_device(voodoo->video_dev,VFL_TYPE_GRABBER,video_nr)<0) + goto err; + printk(KERN_INFO "v3tv: registered device video%d (%d)\n", + voodoo->video_dev->minor & 0x1f, voodoo->video_dev->minor); + + /* vbi */ + voodoo->vbi_dev = &v3tv_vdev_vbi; + if (video_register_device(voodoo->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0) + goto err; + printk(KERN_INFO "v3tv: registered device vbi%d (%d)\n", + voodoo->vbi_dev->minor & 0x1f, voodoo->vbi_dev->minor); + + /* radio */ + voodoo->radio_dev = &v3tv_vdev_radio; + if (video_register_device(voodoo->radio_dev, VFL_TYPE_RADIO,radio_nr)<0) + goto err; + printk(KERN_INFO "v3tv: registered device radio%d (%d)\n", + voodoo->radio_dev->minor & 0x1f, voodoo->radio_dev->minor); + + /* all done */ + return 0; + + err: + v3tv_unregister_video(voodoo); + return -1; +} + +int v3tv_video_init(struct voodoo_data *voodoo) +{ +// int i; + + switch (voodoo->model) { + case VOODOO3_MODEL_NTSC: + printk(KERN_INFO "v3tv: NTSC model.\n"); + snprintf(voodoo->name, sizeof(voodoo->name), + "Voodoo3 TV 3500 %s", "NTSC"); + voodoo->norm = VIDEO_MODE_NTSC; + voodoo->tuner_type = TUNER_PHILIPS_NTSC; /* 2 */ + break; + case VOODOO3_MODEL_PAL: + printk(KERN_INFO "v3tv: PAL model.\n"); + snprintf(voodoo->name, sizeof(voodoo->name), + "Voodoo3 TV 3500 %s", "PAL"); + voodoo->norm = VIDEO_MODE_PAL; + voodoo->tuner_type = TUNER_Samsung_PAL_TCPM9091PD27; + break; + case VOODOO3_MODEL_SECAM: + printk(KERN_INFO "v3tv: SECAM model.\n"); + snprintf(voodoo->name, sizeof(voodoo->name), + "Voodoo3 TV 3500 %s", "SECAM"); + voodoo->norm = VIDEO_MODE_SECAM; + voodoo->tuner_type = TUNER_PHILIPS_SECAM; /* 3 */ + break; + default: + printk(KERN_INFO + "v3tv: Unknown model: 0x%x, defaulting to PAL.\n", + voodoo->model); + snprintf(voodoo->name, sizeof(voodoo->name), + "Voodoo3 TV 3500 model 0x%x", voodoo->model); + voodoo->norm = VIDEO_MODE_PAL; + voodoo->tuner_type = TUNER_Samsung_PAL_TCPM9091PD27; + break; + } + +// for (i=0;i<3;i++) { +// voodoo_channels[i].norm = voodoo->norm; +// } + + if (voodoo->tuner_type != UNSET) { + struct tuner_setup tun_setup; + + tun_setup.mode_mask = T_RADIO | T_ANALOG_TV; + tun_setup.type = voodoo->tuner_type; + tun_setup.addr = ADDR_UNSET; + + i2c_clients_command(voodoo->voodoo_i2c_adapter, + TUNER_SET_TYPE_ADDR, &tun_setup); + } + + voodoo->picture_settings.brightness = 0x8000; + voodoo->picture_settings.hue = 0x8000; + voodoo->picture_settings.colour = 0x8000; + voodoo->picture_settings.contrast = 0x8000; + voodoo->picture_settings.whiteness = 0x8000; + voodoo->picture_settings.depth = 0; + voodoo->picture_settings.palette = 0; + + voodoo->vidOverlayStartX = 0; + voodoo->vidOverlayStartY = 0; + voodoo->vidOverlayEndX = video_standard[voodoo->norm].width; + voodoo->vidOverlayEndY = video_standard[voodoo->norm].height * 2; + voodoo->vidOrigWidth = video_standard[voodoo->norm].width; + voodoo->vidOrigHeight = video_standard[voodoo->norm].height; + + voodoo->capture_win.x = 0; + voodoo->capture_win.y = 0; + voodoo->capture_win.width = video_standard[voodoo->norm].width; + voodoo->capture_win.height = video_standard[voodoo->norm].height; + + v3tv_register_video(voodoo); + + return 0; +} + +void v3tv_video_cleanup(struct voodoo_data *voodoo) +{ + v3tv_unregister_video(voodoo); + + voodoo->initialized &= ~I_v4l2; +} + +/* + * voodoo.c - Part of v4l voodoo3 drivers + * + * $Id: v3tv.c,v 1.1 2005/12/25 16:23:58 mkrufky Exp $ + */ + +/* Detect whether a Voodoo3 can be found, and map its memory. */ +static int v3tv_probe(void) +{ + struct pci_dev *dev = NULL; + u16 subsys_id; + + while ((dev = pci_find_device(PCI_VENDOR_ID_3DFX, + PCI_DEVICE_ID_3DFX_VOODOO3, dev))) { + subsys_id = dev->subsystem_device; + if ((subsys_id == VOODOO3_MODEL_NTSC) || + (subsys_id == VOODOO3_MODEL_PAL) || + (subsys_id == VOODOO3_MODEL_SECAM)) + break; + } + + if (!dev) { + printk(KERN_INFO "v3tv: No Voodoo3 3500 TV found.\n"); + return -ENODEV; + } + + printk(KERN_INFO "v3tv: Voodoo3 found.\n"); + if (!model) + if (debug) + printk(KERN_INFO + "v3tv: Autodetecting Voodoo3 model.\n"); + voodoo->model = (model) ? model : dev->subsystem_device; + + /* map Voodoo3 memory. */ + voodoo->io_base = ioremap_nocache(dev->resource[0].start & + PCI_BASE_ADDRESS_MEM_MASK, 0x200400); + if (!voodoo->io_base) + return (-ENOMEM); + + voodoo->fb_base = ioremap(dev->resource[1].start & + PCI_BASE_ADDRESS_MEM_MASK, 1024 * 1024 * 16); + if (!voodoo->fb_base) { + iounmap(voodoo->io_base); + return -ENOMEM; + } + + if (debug) + printk + ("v3tv: Registers mapped @ 0x%lx, Frame buffer mapped @ 0x%lx\n", + (unsigned long)voodoo->io_base, + (unsigned long)voodoo->fb_base); + + voodoo->dev = dev; + + return 0; +} + +static void v3tv_cleanup_module(void) +{ + if (voodoo->initialized & I_v4l2) + v3tv_video_cleanup(voodoo); + + v3tv_i2c_setup(0); + + if (voodoo->io_base) + iounmap(voodoo->io_base); + + if (voodoo->fb_base) + iounmap(voodoo->fb_base); + + kfree(voodoo); +} + +static int v3tv_init_module(void) +{ + int res; + + printk("v3tv: version %s (%s)\n", V3_VERSION, __DATE__); + + voodoo = kmalloc(sizeof(struct voodoo_data), GFP_KERNEL); + if (!voodoo) + return -ENOMEM; + + memset(voodoo, 0, sizeof(struct voodoo_data)); /* Paranoia: Clear data */ + + if ((res = v3tv_probe())) { + printk("v3tv: Voodoo3 not detected, module not inserted.\n"); + v3tv_cleanup_module(); + return res; + } + +// init_MUTEX(&voodoo->lock); + /* Configure the chip to initial state */ + v3tv_conf(voodoo3_init_reg, CONF_SIZE(voodoo3_init_reg)); + + /* activate i2c */ + if (v3tv_i2c_setup(1)) + return -EINVAL; + + if (v3tv_video_init(voodoo)) + return -EINVAL; + + voodoo->initialized |= I_v4l2; + + /* set up overlay and videobuffers */ + v3tv_overlay_setup(voodoo); + + return 0; +} + +#ifdef MODULE +MODULE_AUTHOR("Juha Valkama <juhisv@surfree.com>"); +MODULE_DESCRIPTION("Voodoo3 3500 TV Video4Linux driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debug flag (0, 1 or 2)"); +MODULE_PARM(model, "i"); +MODULE_PARM_DESC(model, "Voodoo3 submodel ID (0x60, 0x61, or 0x62"); +#ifdef INCLUDE_I2C +MODULE_PARM(ddc, "i"); +MODULE_PARM_DESC(ddc, + "Set to 0 to disable DDC init (if you don't have a DDC monitor)"); +#endif + +MODULE_PARM(video_nr,"i"); +MODULE_PARM(vbi_nr,"i"); +MODULE_PARM(radio_nr,"i"); + +module_init(v3tv_init_module); +module_exit(v3tv_cleanup_module); +#endif /* MODULE */ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ |