diff options
Diffstat (limited to 'v4l_experimental')
-rw-r--r-- | v4l_experimental/v3tv/i2c-voodoo3.c | 20 | ||||
-rw-r--r-- | v4l_experimental/v3tv/v3tv.c | 3354 | ||||
-rw-r--r-- | v4l_experimental/v3tv/vpx3224-proc.c | 823 | ||||
-rw-r--r-- | v4l_experimental/v3tv/vpx3224.c | 1296 |
4 files changed, 5491 insertions, 2 deletions
diff --git a/v4l_experimental/v3tv/i2c-voodoo3.c b/v4l_experimental/v3tv/i2c-voodoo3.c index b675773b0..3ec720f78 100644 --- a/v4l_experimental/v3tv/i2c-voodoo3.c +++ b/v4l_experimental/v3tv/i2c-voodoo3.c @@ -60,6 +60,7 @@ #define TIMEOUT (HZ / 2) +struct pci_dev *voodoo3_dev; static void __iomem *ioaddr; /* The voo GPIO registers don't have individual masks for each bit @@ -151,6 +152,7 @@ static int config_v3(struct pci_dev *dev) dev_info(&dev->dev, "Using Banshee/Voodoo3 I2C device at %p\n", ioaddr); return 0; } + printk(KERN_WARNING "i2c-voodoo3: !!! I2C device not found !!!"); return -ENODEV; } @@ -224,21 +226,35 @@ static void __devexit voodoo3_remove(struct pci_dev *dev) iounmap(ioaddr); } +/* static struct pci_driver voodoo3_driver = { .name = "voodoo3_smbus", .id_table = voodoo3_ids, .probe = voodoo3_probe, .remove = __devexit_p(voodoo3_remove), }; +*/ static int __init i2c_voodoo3_init(void) { - return pci_register_driver(&voodoo3_driver); +/* return pci_register_driver(&voodoo3_driver); */ + const struct pci_device_id *ids = voodoo3_ids; + + while (ids->vendor) { + if ((voodoo3_dev = pci_get_device(ids->vendor, ids->device, NULL))) + printk(KERN_ERR "i2c_voodoo3: dev->dev = %p", &voodoo3_dev->dev); + if (voodoo3_probe(voodoo3_dev, ids) >= 0) + return 0; + ids++; + } + return -ENODEV; } static void __exit i2c_voodoo3_exit(void) { - pci_unregister_driver(&voodoo3_driver); +/* pci_unregister_driver(&voodoo3_driver); */ + voodoo3_remove(voodoo3_dev); + pci_dev_put(voodoo3_dev); } 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: + */ diff --git a/v4l_experimental/v3tv/vpx3224-proc.c b/v4l_experimental/v3tv/vpx3224-proc.c new file mode 100644 index 000000000..eaf9d12e7 --- /dev/null +++ b/v4l_experimental/v3tv/vpx3224-proc.c @@ -0,0 +1,823 @@ +/* + * vpx3224d & vpx3225d video decoder register inspector + * + * Copyright (C) 2004 Perry Gilfillan <perrye@linuxmail.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/i2c-proc.h> +#include <linux/proc_fs.h> + +static char proc_buf[1000]; +static int reg_set = 0; +static int leftovers = 0; +struct proc_dir_entry *proc_entry; + +/* -- SENSORS SYSCTL START -- */ +#define vpx3224_SYSCTL_PARTNUM 3200 +#define vpx3224_SYSCTL_OUTPUT 3201 +#define vpx3224_SYSCTL_SYNC_SLICER 3202 +#define vpx3224_SYSCTL_BIT_SLICER 3203 +#define vpx3224_SYSCTL_BYTE_SLICER 3204 +#define vpx3224_SYSCTL_SDT_SELECT 3205 +#define vpx3224_SYSCTL_COLOR_PROC 3206 + +/* -- SENSORS SYSCTL END -- */ + +static void +vpx3224_partnum(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = vpx3224_read(client, R_JEDEC); + results[1] = (vpx3224_read(client, R_PARTHIGH) << 8) + + vpx3224_read(client, R_PARTLOW); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + DEB(printk(KERN_INFO "%s: Warning: write was requested on " + "read-only proc file: PartNumber\n", + i2c_clientname(client))); + } +} + +static void +vpx3224_output(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = (u8) vpx3224_read(client, R_driver_a); + results[1] = (u8) vpx3224_read(client, R_driver_b); + results[2] = (u8) vpx3224_read(client, R_oena); + results[3] = (u8) vpx3224_read(client, R_llc); + *nrels_mag = 4; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) + vpx3224_write(client, R_driver_a, (u8) results[0]); + if (*nrels_mag >= 2) + vpx3224_write(client, R_driver_b, (u8) results[1]); + if (*nrels_mag >= 3) + vpx3224_write(client, R_oena, (u8) results[2]); + if (*nrels_mag >= 4) + vpx3224_write(client, R_llc, (u8) results[3]); + } +} + +static void +vpx3224_sync_slicer(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = (u8) vpx3224_read(client, R_sync_slicer); + results[1] = (u8) vpx3224_read(client, R_sync_stat) & 0xC0; + results[2] = (u8) vpx3224_read(client, R_sync_count); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) + vpx3224_write(client, R_sync_slicer, + (u8) results[0]); + if (*nrels_mag >= 2) + DEB(printk + (KERN_INFO + "%s: Warning: write was requested on " + "read-only register: " STR_fmt "0x%x: \n", + i2c_clientname(client), + VPX_LABEL(R_sync_stat) R_sync_stat)); + if (*nrels_mag >= 3) + DEB(printk + (KERN_INFO + "%s: Warning: write was requested on " + "read-only register: " STR_fmt "0x%x: \n", + i2c_clientname(client), + VPX_LABEL(R_sync_count) R_sync_count)); + } +} + +static void +vpx3224_bit_slicer(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = + (u8) vpx3224_read(client, R_soft_slicer) & 0x7F; + results[1] = (u8) vpx3224_read(client, R_ttx_freql); + results[2] = (u8) vpx3224_read(client, R_ttx_freqh) & 0x0F; + results[3] = (u8) vpx3224_read(client, R_filter) & 0x3F; + results[4] = (u8) vpx3224_read(client, R_data_slicer); + results[5] = (u8) vpx3224_read(client, R_accu) & 0x3F; + results[6] = (u8) vpx3224_read(client, R_coeff_rd); + results[7] = (u8) vpx3224_read(client, R_level_rd); + *nrels_mag = 8; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) + vpx3224_write(client, R_soft_slicer, + (u8) results[0] & 0x7F); + if (*nrels_mag >= 2) + vpx3224_write(client, R_ttx_freql, + (u8) results[1]); + if (*nrels_mag >= 3) + vpx3224_write(client, R_ttx_freqh, + (u8) results[2] & 0x0F); + if (*nrels_mag >= 4) + vpx3224_write(client, R_filter, + (u8) results[3] & 0x3F); + if (*nrels_mag >= 5) + vpx3224_write(client, R_data_slicer, + (u8) results[4]); + if (*nrels_mag >= 6) + vpx3224_write(client, R_accu, + (u8) results[5] & 0x3F); + if (*nrels_mag >= 7) + DEB(printk + (KERN_INFO + "%s: Warning: write was requested on " + "read-only register: " STR_fmt "0x%x: \n", + i2c_clientname(client), + VPX_LABEL(R_coeff_rd) R_coeff_rd)); + if (*nrels_mag >= 8) + DEB(printk + (KERN_INFO + "%s: Warning: write was requested on " + "read-only register: " STR_fmt "0x%x: \n", + i2c_clientname(client), + VPX_LABEL(R_level_rd) R_level_rd)); + } +} + +static void +vpx3224_byte_slicer(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = vpx3224_read(client, R_soft_cnt); + results[1] = vpx3224_read(client, R_standard); + results[2] = (vpx3224_read(client, R_reference_l) + + (vpx3224_read(client, R_reference_m) << 8) + + (vpx3224_read(client, R_reference_h) << 16)) + & 0x00ffffff; + results[3] = + (vpx3224_read(client, R_mask_l) + + (vpx3224_read(client, R_mask_m) << 8) + + (vpx3224_read(client, R_mask_h) << 16)) & 0x00ffffff; + results[4] = vpx3224_read(client, R_tolerance) & 0x3f; + results[5] = vpx3224_read(client, R_out_mode); + *nrels_mag = 6; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 2) + vpx3224_write(client, R_standard, results[1]); + if (*nrels_mag >= 3) { + vpx3224_write(client, R_reference_l, + results[2] & 0xff); + vpx3224_write(client, R_reference_m, + (results[2] >> 8) & 0xff); + vpx3224_write(client, R_reference_h, + (results[2] >> 16) & 0xff); + } + if (*nrels_mag >= 4) { + vpx3224_write(client, R_mask_l, results[3] & 0xff); + vpx3224_write(client, R_mask_m, + (results[3] >> 8) & 0xff); + vpx3224_write(client, R_mask_h, + (results[3] >> 16) & 0xff); + } + if (*nrels_mag >= 5) + vpx3224_write(client, R_tolerance, + results[4] & 0x3f); + if (*nrels_mag >= 6) + vpx3224_write(client, R_out_mode, results[5]); + } + +} + +static void +vpx3224_sdt_select(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = vpx3224_fp_read(client, RFP_sdt); + results[1] = vpx3224_fp_read(client, RFP_insel); + results[2] = vpx3224_fp_read(client, RFP_sfif); + results[3] = vpx3224_fp_read(client, RFP_ldly); + *nrels_mag = 4; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) + vpx3224_fp_write(client, RFP_sdt, results[0]); + if (*nrels_mag >= 2) + vpx3224_fp_write(client, RFP_insel, results[1]); + if (*nrels_mag >= 3) + vpx3224_fp_write(client, RFP_sfif, results[2]); + if (*nrels_mag >= 4) + vpx3224_fp_write(client, RFP_ldly, results[3]); + } +} + +static void +vpx3224_color_proc(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = vpx3224_fp_read(client, RFP_accref); + results[1] = vpx3224_fp_read(client, RFP_accb); + results[2] = vpx3224_fp_read(client, RFP_accr); + results[3] = vpx3224_fp_read(client, RFP_kilvl); + results[4] = vpx3224_fp_read(client, RFP_kilhy); + results[5] = vpx3224_fp_read(client, RFP_tint); + *nrels_mag = 6; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) + vpx3224_fp_write(client, RFP_accref, results[0]); + if (*nrels_mag >= 2) + vpx3224_fp_write(client, RFP_accb, results[1]); + if (*nrels_mag >= 3) + vpx3224_fp_write(client, RFP_accr, results[2]); + if (*nrels_mag >= 4) + vpx3224_fp_write(client, RFP_kilvl, results[3]); + if (*nrels_mag >= 5) + vpx3224_fp_write(client, RFP_kilhy, results[4]); + if (*nrels_mag >= 6) + vpx3224_fp_write(client, RFP_tint, results[5]); + } +} + +static int vpx3224_dump_register(struct i2c_client *client, int arg) +{ + u16 retval = 0; + + switch (arg) { + case 0: /* Chip Identidfication */ + { + unsigned char jedec; + u16 pn; + + jedec = vpx3224_read(client, R_JEDEC); + pn = (vpx3224_read(client, R_PARTHIGH) << 8) + + vpx3224_read(client, R_PARTLOW); + + retval = sprintf(proc_buf, + "Chip Identification: %s\n" + " Manufacturer ID: 0x%x\n" + " Part Number: 0x%x\n", + i2c_clientname(client), jedec, + pn); + } + break; + case 1: /* Output */ + { + unsigned char driver_a, driver_b, oena, llc; + unsigned char stra1, stra2, stra3; + unsigned char strb1, strb2; + unsigned char aen, ben, clkena, zen, llc2en, + oeqdel, latoeq, oeq_dis; + unsigned char lowpow, iresen, llc2, llc2_pol, + slowpow, oldllc; + + driver_a = vpx3224_read(client, R_driver_a); + stra1 = driver_a & 7; + stra2 = (driver_a & 0x38) >> 3; + stra3 = (driver_a & 0xC0) >> 6; + driver_b = vpx3224_read(client, R_driver_b); + strb1 = driver_b & 7; + strb2 = (driver_b & 0x38) >> 3; + oena = vpx3224_read(client, R_oena); + aen = oena & 1; + ben = oena & 2; + clkena = oena & 4; + zen = oena & 8; + llc2en = oena & 16; + oeqdel = oena & 32; + latoeq = oena & 64; + oeq_dis = oena & 128; + llc = vpx3224_read(client, R_llc); + lowpow = llc & 3; + iresen = llc & 4; + llc2 = llc & 8; + llc2_pol = llc & 16; + slowpow = llc & 32; + oldllc = llc & 64; + + retval = sprintf(proc_buf, + "Output: DRIVER_A: 0x%x\n" + "\tbit [2:0] Driver strength of Port A[7:0]: stra1 0x%x\n" + "\tbit [5:3] Driver strength of PIXCLK, LLC, and VACT: stra2 0x%x\n" + "\tbit [7:6] Additional PIXCLK driver strength: stra3 0x%x\n" + "Output: DRIVER_B: 0x%x\n" + "\tbit [2:0] Driver strength of Port B[7:0]: strb1 0x%x\n" + "\tbit [5:3] Driver strength of HREF, VREF, FIELD, and LLC2: stra2 0x%x\n" + "Output Enable: OENA: 0x%x\n" + "\tbit [0] aen:\t%s\n" + "\tbit [1] ben:\t%s\n" + "\tbit [2] clkena\t%s\n" + "\tbit [3] zen:\t%s\n" + "\tbit [4] llc2en:\t%s\n" + "\tbit [5] oeqdel:\t%s\n" + "\tbit [6] latoeq:\t%s\n" + "\tbit [7] oeq_dis:\t%s\n" + "Low power mode, LLC mode: LLC: 0x%x\n" + "\tbit [1:0] lowpower:\t%s\n" + "\tbit [2] iresen:\t%s\n" + "\tbit [3] llc2:\t%s\n" + "\tbit [4] llc2_pol: %x:\t%s\n" + "\tbit [5] slowpow:\t%s\n" + "\tbit [6] oldllc:\t%s\n", + driver_a, stra1, stra2, stra3, + driver_b, strb1, strb2, + oena, + aen ? "Enable Video Port A" : "Disable Video Port A", + ben ? "Enable Video Port B" : "Disable Video Port B", + clkena ? "Enable Pixclk Output" : "Disable Pixclk Output", + zen ? "Enable HREF, VREF, FIELD, VACT, LLC, LLC2" + : "Disbale HREF, VREF, FIELD, VACT, LLC, LLC2", + llc2en ? "Enable LLC2 to TDO pin" : "Disable LLC2", + oeqdel ? "no delay of OEQ input signal" + : "1 LLC cycle delay of ", + latoeq ? "latch OEQ input signal with rising edge of LLC" + : "don't latch OEQ input signal", + oeq_dis ? "disable OEQ pin function" + : "enable OEQ pin function", + llc, + (lowpow == 0) ? "active mode, outputs enabled" + : (lowpow == 1) ? "outputs tri-stated, clock divided by 2, i2c full speed" + : (lowpow == 2) ? "outputs tri-stated, clock divided by 4, i2c full speed" + : (lowpow == 3) ? "outputs tri-stated, clock divided by 8, i2c < 100 kbit/s" + : "", + iresen ? "i2c reset" : "", + llc2 ? "connect LLC2 to TDO pin" : "connect bit[4] to TDO pin", + llc2_pol >> 4, + !llc2 ? "LLC2 polaroity determined by bit [4]" : "", + slowpow ? "reset all slicer registers" : "", + oldllc ? "use old llc timing with long hold time" + : "use new llc timing with shorter hold time"); + } + break; + case 2: + { /* Sync Slicer - Hsync Counter */ + unsigned char sync_slicer, sync_stat, sync_cnt; + unsigned char sync_level, vsw; + unsigned char vwin, field; + + sync_slicer = vpx3224_read(client, R_sync_slicer); + sync_level = sync_slicer & 0x7F; + vsw = sync_slicer & 0x80; + sync_stat = + vpx3224_read(client, R_sync_stat) & 0xC0; + vwin = sync_stat & 0x40; + field = sync_stat & 0x80; + sync_cnt = vpx3224_read(client, R_sync_count); + + retval = sprintf(proc_buf, + "Sync Slicer: sync_slicer: 0x%x\n" + "\tbit [6:0] binary sync level is compared with binary data: 0x%x\n" + "\tbit [7] vertical sync window: %s\n" + "Sync Status: sync_stat: 0x%x\t\tREADONLY\n" + "\tbit [6] vwin: %s\n" + "\tbit [7] field: %s\n" + "Hsync Counter: sync_cnt:\t\tREADONLY\n" + "\tbit [7:0] number of detected horizontal sync pulses per frame /4: 0x%x\n", + sync_slicer, + sync_level, + vsw ? "disable" : "enable", + sync_stat, + vwin ? "vert. retrace\tset at line 628/528 (PAL/NTSC)" + : "vert. window\treset at line 624/524 (PAL/NTSC)", + field ? "field 1\tset at line 624/524 (PAL/NTSC)" + : "field 2\treset at line 313/263 (PAL/NTSC)", + sync_cnt); + } + break; + case 3: + { /* Bit Slicer */ + u8 soft_slicer, ttx_freql, ttx_freqh, filter, + data_slicer; + u8 accu, coeff_rd, level_rd; + u8 soft_level; + u16 ttx_freq; + u8 ttx_phinc; + u8 coeff; + u8 reset, dcen, acen, soften, acaden, fltaden; + + soft_slicer = vpx3224_read(client, R_soft_slicer); + soft_level = soft_slicer & 0x7f; + ttx_freql = vpx3224_read(client, R_ttx_freql); + ttx_freqh = + vpx3224_read(client, R_ttx_freqh) & 0x0F; + ttx_freq = (ttx_freql + (ttx_freqh << 8)) & 0x07ff; + ttx_phinc = ttx_freqh & 0x08; /* bit 11 */ + filter = vpx3224_read(client, R_filter) & 0x3F; + coeff = filter; + data_slicer = vpx3224_read(client, R_data_slicer); + accu = vpx3224_read(client, R_accu) & 0x3f; + reset = accu & 1; + dcen = accu & 2; + acen = accu & 4; + soften = accu & 8; + acaden = accu & 16; + fltaden = accu & 32; + coeff_rd = vpx3224_read(client, R_coeff_rd); + level_rd = vpx3224_read(client, R_level_rd); + + retval = sprintf(proc_buf, + "Bit Slicer:\n" + "Soft Slicer: soft_slicer: 0x%x\n" + "\tbit [6:0] binary soft slicer level is compared with ABS[data]: 0x%x\n" + "Freq: ttx bitslicer frequency: 0x%x %x\n" + "\tbit [10:0] Freq: ttx_freq: 0x%x\n" + "\tbit [11] phase inc = %s\n" + "Filter Coefficient\n" + "\tbit [5:0] high pass filter coefficient in 2's complement: 0x%x\n" + "\t\t 100000 = not allowed; 100001 = -31; 000000 = 0; 011111 = 31\n" + "Data Slicer:\n" + "\tbit [0:7] data_slicer: 0x%x\n" + "Accumulator Mode: accu: 0x%x\n" + "\tbit[0] %s\n" + "\tbit[1] DC accu %s\n" + "\tbit[2] AC and FLT accu %s\n" + "\tbit[3] soft error correction %s\n" + "\tbit[4] ac adaption %s\n" + "\tbit[5] flt adaption %s\n" + "Read Filter Coefficient: coeff_rd: 0x%x\n" + "Read Data Slicer Level: level_rd: 0x%x\n", + soft_slicer, soft_level, + ttx_freqh, ttx_freql, ttx_freq, + ttx_phinc ? "Freq*(1+1/8) before framing code\n" + "\t\t Freq*(1+1/16) after framing code" + : "Freq", + coeff, data_slicer, accu, + reset ? "reset DC and AC and FLT accu (one shot)" + : "no action", + dcen ? "disable" : "enable", + acen ? "disable\n\t\t\t(only for VPS and CAPTION and WSS line)" + : "enable", + soften ? "enable" : "disable", + acaden ? "enable" : "disable", + fltaden ? "enable" : "disable", + coeff_rd, level_rd); + } + break; + case 4: + { /* Byte Slicer */ + u8 soft_cnt, standard; + u32 reference, mask; + u8 tolerance, out_mode; + + soft_cnt = vpx3224_read(client, R_soft_cnt); + standard = vpx3224_read(client, R_standard); + reference = (vpx3224_read(client, R_reference_l) + + (vpx3224_read(client, R_reference_m) + << 8) + (vpx3224_read(client, + R_reference_h) + << 16)) & 0x00ffffff; + mask = + (vpx3224_read(client, R_mask_l) + + (vpx3224_read(client, R_mask_m) << 8) + + (vpx3224_read(client, R_mask_h) << 16)) & + 0x00ffffff; + tolerance = + vpx3224_read(client, R_tolerance) & 0x3f; + out_mode = vpx3224_read(client, R_out_mode); + + retval = sprintf(proc_buf, + "Byte Slicer:\n" + "Soft Error Counter: soft_cnt: 0x%x\n" + "Standard: 0x%x\n" + "\tbit[0] TTX: %s\n" + "\tbit[1] %s mode\n" + "\tbit[2] full field %s\n" + "\tbit[3] VPS line 16 %s\n" + "\tbit[4] WSS line 23 %s\n" + "\tbit[5] CAPTION line 21 field 1 %s\n" + "\tbit[6] CAPTION line 21 field 2 %s\n" + "\tbit[7] horizontal quit mode %s\n" + "Clock run-in and framing code reference: 0x%06x\n" + "Clock run-in and framing code don't care mask: 0x%06x\n" + "Tolerance: 0x%x\n" + "\tbit[1:0] maximum number of bit errors in low mask: %d\n" + "\tbit[3:2] maximum number of bit errors in mid mask: %d\n" + "\tbit[5:4] maximum number of bit errors in high mask: %d\n" + "Output Mode: out_mode: 0x%x\n" + "\tbit[5:0] number of data bytes per text " + "line including framing code: 0x%x\n" + "\tbit[6] 64 byte mode %s\n" + "\tbit[7] data output %s\n", + soft_cnt, standard, + (standard & 1) ? "enable" : "disable", + (standard & 2) ? "PAL" : "NTSC", + (standard & 4) ? "enable" : "disable", + (standard & 8) ? "enable" : "disable", + (standard & 16) ? "enable" : "disable", + (standard & 32) ? "enable" : "disable", + (standard & 64) ? "enable" : "disable", + (standard & 128) ? "enable" : "disable", + reference, mask, tolerance, + tolerance & 0x03, + (tolerance >> 2) & 0x03, + (tolerance >> 4) & 0x03, out_mode, + out_mode & 0x3f, + (out_mode & 64) ? "enable" : "disable", + (out_mode & 128) ? "only for text lines" + : "for every video line"); + } + break; + case 5: + { /* Standard Selection */ + u16 sdt, insel, sfif, ldly; + char standard; + + sdt = vpx3224_fp_read(client, RFP_sdt); + standard = (sdt & 0x03); + + insel = vpx3224_fp_read(client, RFP_insel); + sfif = vpx3224_fp_read(client, RFP_sfif); + ldly = vpx3224_fp_read(client, RFP_ldly); + + retval = sprintf(proc_buf, + "Standard Selection:\n" + "Standard select: 0x%03x\n" + "\tbit[2:0] sdt: 0x%01x: %s\n" + "\tbit[3] sdtmod: %s\n" + "\tbit[6] svhs: SVHS mode %s\n" + "\tbit[7] Options: %s%s%s\n" + "Input Selection: insel: 0x%03x\n" + "\tbit[1:0] luma selector: %s\n" + "\tbit[2] chroma selector: %s\n" + "\tbit[4:3] IF compensation: %s\n" + "\tbit[6:5] chroma bandwidth selector: %s\n" + "\tbit[7] SECAM notch filter: %s\n" + "\tbit[8] luma lowpass filter: %s\n" + "\tbit[10:9] hpll speed: %s\n" + "Picture Start Position: sfif: 0x%03x\n" + "Luma/Chroma Delay Adjust: ldly: bit[11:6] 0x%02x\n", + sdt, standard, + (standard == VPX_PALBGHI) ? "PAL B,G,H,I" + : (standard == VPX_NTSCM) ? "NTSC M" + : (standard == VPX_SECAM) ? "SECAM" + : (standard == VPX_NTSC44) ? "NTSC44" + : (standard == VPX_PALM) ? "PAL M" + : (standard == VPX_PALN) ? "PAL N" + : (standard == VPX_PAL60) ? "PAL 60" + : (standard == VPX_NTSCCOMB) ? "NTSC COMB" + : "", + !(standard & 0x04) ? "MOD standard modifier" + : (standard == VPX_NTSC44) ? "NTSC modified to compensated NTSC" + : (standard == VPX_NTSCM) ? "NTSC modified to compensated NTSC" + : (standard == VPX_NTSCCOMB) ? "NTSCC modified to monochrome 525" + : (standard == VPX_SECAM) ? "SECAM modified to monochrome 625" + : (standard == VPX_PALBGHI) ? "PAL modified to simple PAL" + : (standard == VPX_PALM) ? "PAL modified to simple PAL" + : (standard == VPX_PALN) ? "PAL modified to simple PAL" + : (standard == VPX_PAL60) ? "PAL modified to simple PAL" + : "", + (standard & 0x40) ? "on" : "off", + (standard & 0x80) ? "no hpll setup; " : "", + (standard & 0x100) ? "no vertical setup; " : "", + (standard & 0x200) ? "no acc setup" : "", insel, + ((insel & 0x03) == 0x00) ? "VIN3" + : ((insel & 0x03) == 0x01) ? "VIN2" + : ((insel & 0x03) == 0x02) ? "VIN1" + : "reserved", + (insel & 0x04) ? "CIN" : "VIN1", + ((insel & 0x18) == 0x08) ? "6 db/0kt" + : ((insel & 0x18) == 0x10) ? "12 db/0kt" + : ((insel & 0x18) == 0x18) ? "10 db/Mhz only for SECAM" + : "off", + ((insel & 0x60) == 0x00) ? "narrow" + : ((insel & 0x60) == 0x20) ? "normal" + : ((insel & 0x60) == 0x40) ? "broad" + : ((insel & 0x60) == 0x60) ? "wide" + : "", + (insel & 0x80) ? "fixed" : "adaptive", + (insel & 0x100) ? "enable" : "disable", + ((insel & 0x600) == 0x00) ? "no change" + : ((insel & 0x600) == 0x200) ? "terrestrial" + : ((insel & 0x600) == 0x400) ? "vcr" + : ((insel & 0x600) == 0x600) ? "mixed" + : "", + sfif, + (ldly >> 6) & 0x3f); + } + break; + case 6: + { /* Color Processing */ + u16 accref, accb, accr, kilvl, kilhy, tint; + + accref = vpx3224_fp_read(client, RFP_accref); + accb = vpx3224_fp_read(client, RFP_accb); + accr = vpx3224_fp_read(client, RFP_accr); + kilvl = vpx3224_fp_read(client, RFP_kilvl); + kilhy = vpx3224_fp_read(client, RFP_kilhy); + tint = vpx3224_fp_read(client, RFP_tint); + + retval = sprintf(proc_buf, + "Color Processing:\n" + "\tACC reference level: 0x%03x\n" + "\tACC multiplier for SECAM Db chroma component: 0x%03x\n" + "\tACC multiplier for SECAM Dr chroma component: 0x%03x\n" + "\tAmplitude Killer Level: 0x%03x\n" + "\tAmplitude Killer Hysteresis: 0x%03x\n" + "\tNTSC tint angle: 0x%03x\n", + accref, accb, accr, kilvl, kilhy, + tint); + } + break; + case 7: + { /* DVCO - Digitally Controled Clock Oscillator */ + u16 xlck, dvco, adjust; + + xlck = vpx3224_fp_read(client, RFP_xlck); + dvco = vpx3224_fp_read(client, RFP_dvco); + adjust = vpx3224_fp_read(client, RFP_adjust); + + retval = sprintf(proc_buf, + "DVCO: Digitally Controled Clock Oscillator\n" + "\tcrystal oscillator line-locked mode: 0x%03x\n" + "\tcrystal oscillator center frequency adjust: 0x%03x\n" + "\tCrystal Oscillator Center Frequency Adjust Value: 0x%03x\n", + xlck, dvco, adjust); + } + break; + } + reg_set++; + + return (retval); +} + +int +vpx3224_read_regs(char *buffer, char **location, + off_t offset, int length, int *eof, void *data) +{ +#define VPX3224_REG_STOP 7 + + int n; + + if (offset == 0) { + leftovers = 0; + reg_set = 0; + } + + if (reg_set >= VPX3224_REG_STOP) { + *eof = 1; + return 0; + } + + if (leftovers) { + n = leftovers; + leftovers = 0; + } else { + n = vpx3224_dump_register(data, reg_set); + } + + if (n > length) { + memcpy(*location = buffer, proc_buf, length); + memmove(proc_buf, proc_buf + length, n - length); + leftovers = n - length; + return length; + } else { + memcpy(*location = buffer, proc_buf, n); + return n; + } +} + +static ctl_table vpx3224_dir_table_template[] = { + {.ctl_name = vpx3224_SYSCTL_PARTNUM, + .procname = "PartNumber", + .data = NULL, + .maxlen = 0, + .mode = 0444, + .child = NULL, + .proc_handler = &i2c_proc_real, + .strategy = &i2c_sysctl_real, + .de = NULL, + .extra1 = &vpx3224_partnum, + .extra2 = NULL}, + {.ctl_name = vpx3224_SYSCTL_OUTPUT, + .procname = "Output", + .data = NULL, + .maxlen = 0, + .mode = 0666, + .child = NULL, + .proc_handler = &i2c_proc_real, + .strategy = &i2c_sysctl_real, + .de = NULL, + .extra1 = &vpx3224_output, + .extra2 = NULL}, + {.ctl_name = vpx3224_SYSCTL_SYNC_SLICER, + .procname = "SyncSlicer", + .data = NULL, + .maxlen = 0, + .mode = 0666, + .child = NULL, + .proc_handler = &i2c_proc_real, + .strategy = &i2c_sysctl_real, + .de = NULL, + .extra1 = &vpx3224_sync_slicer, + .extra2 = NULL}, + {.ctl_name = vpx3224_SYSCTL_BIT_SLICER, + .procname = "BitSlicer", + .data = NULL, + .maxlen = 0, + .mode = 0666, + .child = NULL, + .proc_handler = &i2c_proc_real, + .strategy = &i2c_sysctl_real, + .de = NULL, + .extra1 = &vpx3224_bit_slicer, + .extra2 = NULL}, + {.ctl_name = vpx3224_SYSCTL_BYTE_SLICER, + .procname = "ByteSlicer", + .data = NULL, + .maxlen = 0, + .mode = 0666, + .child = NULL, + .proc_handler = &i2c_proc_real, + .strategy = &i2c_sysctl_real, + .de = NULL, + .extra1 = &vpx3224_byte_slicer, + .extra2 = NULL}, + {.ctl_name = vpx3224_SYSCTL_SDT_SELECT, + .procname = "StandardSelection", + .data = NULL, + .maxlen = 0, + .mode = 0666, + .child = NULL, + .proc_handler = &i2c_proc_real, + .strategy = &i2c_sysctl_real, + .de = NULL, + .extra1 = &vpx3224_sdt_select, + .extra2 = NULL}, + {.ctl_name = vpx3224_SYSCTL_COLOR_PROC, + .procname = "ColorProcessing", + .data = NULL, + .maxlen = 0, + .mode = 0666, + .child = NULL, + .proc_handler = &i2c_proc_real, + .strategy = &i2c_sysctl_real, + .de = NULL, + .extra1 = &vpx3224_color_proc, + .extra2 = NULL}, + {0} +}; + +void vpx3224_init_proc(struct i2c_client *client) +{ + int i; + struct vpx3224 *decoder = i2c_get_clientdata(client); + + /* Register a new directory entry with module sensors */ + i = i2c_register_entry + (client, i2c_clientname(client), vpx3224_dir_table_template); + if (i < 0) + printk(KERN_INFO "%s: could not register " + "vpx3224_dir_table_template: err: %d\n", + i2c_clientname(client), i); + else + decoder->sysctl_id = i; + + proc_entry = create_proc_read_entry + (i2c_clientname(client), 0, &proc_root, &vpx3224_read_regs, + client); + if (!proc_entry) + printk(KERN_INFO "%s: could not create " + "/proc/%s\n", i2c_clientname(client), + i2c_clientname(client)); +} + +void vpx3224_del_proc(struct i2c_client *client) +{ + i2c_deregister_entry(((struct vpx3224 *) (client->data))-> + sysctl_id); + + if (proc_entry) + remove_proc_entry(i2c_clientname(client), &proc_root); +} diff --git a/v4l_experimental/v3tv/vpx3224.c b/v4l_experimental/v3tv/vpx3224.c new file mode 100644 index 000000000..26cdcaece --- /dev/null +++ b/v4l_experimental/v3tv/vpx3224.c @@ -0,0 +1,1296 @@ +/* + + * vpx3224d & vpx3225d video decoder driver version 0.0.1 + * + * Copyright (C) 2004 Perry Gilfillan <perrye@linuxmail.org> + * + * Based on work: + * + * Copyright (C) 2001 Laurent Pinchart <lpinchart@freegates.be> + * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1 + * + * vpx322x.h - Part of voodoo3500tv v4l drivers + * + * vpx322x.c - Part of voodoo3500tv v4l drivers + * Copyright (c) 2000 Juha Valkama <juhisv@surfree.com> + * + * vpx322x-i2c.c - Part of voodoo3500tv v4l drivers + * Copyright (c) 2000 Juha Valkama <juhisv@surfree.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/slab.h> + +#include <linux/byteorder/swab.h> + +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#include "i2c-compat.h" +#endif + +#include <linux/videodev.h> +#include <linux/video_decoder.h> + +#if 0 +static inline int vpx3224_write(struct i2c_client *client, + u8 reg, u8 value); +static inline int vpx3224_read(struct i2c_client *client, u8 reg); +static int +vpx3224_write_block(struct i2c_client *client, + const u8 * data, unsigned int len); +static int vpx3224_fp_status(struct i2c_client *client); +static int +vpx3224_fp_write(struct i2c_client *client, u16 fpaddr, u16 data); +static int vpx3224_fp_read(struct i2c_client *client, u16 fpaddr); +static int +vpx3224_write_fp_block(struct i2c_client *client, + const u16 * data, int len); +#endif +static int vpx3224_detect_client(struct i2c_adapter *adapter, int address, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + unsigned short flags, +#endif + int kind); + +/* Each client has this additional data */ + +struct vpx3224 { + int norm; + int input; + int enable; + int bright; + int contrast; + int hue; + int sat; + int sysctl_id; + + int err; +}; + +static int debug = 0; + +/* Many vpx3224 constants specified below */ + +#define I2C_VPX3224 0x43 + +#ifndef I2C_DRIVERID_VPX3224 +#define I2C_DRIVERID_VPX3224 0xFF +#endif + +#define VPX_VERSION "0.1-Multistandard" + +#define VPX_TIMEOUT_COUNT 10 + +#define VPX3224_MAX_INPUT 3 /* 3 CVBS, 5 SVHS */ +#define VPX3224_MAX_OUTPUT 1 /* 1 CVBS */ + +#define MICRONAS_INTERMETALL 0xEC +#define VPX3216B 0x4260 +#define VPX3214C 0x4280 +#define VPX3220A 0x4680 +#define VPX3225D 0x7230 /* Chip Identification */ +#define VPX3224D 0x7231 + +#define VPX_PALBGHI 0x00 /* video modes */ +#define VPX_NTSCM 0x01 +#define VPX_SECAM 0x02 +#define VPX_NTSC44 0x03 +#define VPX_PALM 0x04 +#define VPX_PALN 0x05 +#define VPX_PAL60 0x06 +#define VPX_NTSCCOMB 0x07 + +#define FP 0x1000 /* Registers greater than 0x1000 are considerd fp-ram registers */ + /* Subtract 0x1000 from register and use fp-ram access routines */ +/* The vpx322x registers */ + +#define R_JEDEC 0x00 /* r Manufacture ID Chip Ident. */ +#define R_PARTLOW 0x01 /* r low-byte of 16-bit part number Chip Ident. */ +#define R_PARTHIGH 0x02 /* r high-byte of 16-bit part number Chip Ident. */ +#define R_JEDEC2 0x03 + +#define R_FPSTA 0x35 /* r FP status FP Interface */ +#define R_FPRD 0x36 /* w FP read FP Interface */ +#define R_FPWR 0x37 /* w FP write FP Interface */ +#define R_FPDAT 0x38 /* w/r FP data FP Interface */ + +/* Output */ +#define R_oena 0xF2 /* w Output Enable Output */ +#define R_driver_a 0xF8 /* w Pad Driver Strength - TTL Out pads type A Output */ +#define R_driver_b 0xF9 /* w Pad Driver Strength - TTL Out pads type B Output */ +#define R_llc 0xAA /* w Low power mode, LLC mode Output */ + +/* Data Slicer - vpx3225D only */ +/* Sync Slicer */ +#define R_sync_slicer 0xC8 +#define R_sync_stat 0xB4 +#define R_sync_count 0xB5 + +/* Bit Slicer */ +#define R_soft_slicer 0xC0 +#define R_ttx_freql 0xC1 +#define R_ttx_freqh 0xC2 +#define R_filter 0xC5 +#define R_data_slicer 0xC6 +#define R_accu 0xC7 +#define R_coeff_rd 0xB6 +#define R_level_rd 0xB7 + +/* Byte Slicer */ +#define R_soft_cnt 0xB3 +#define R_standard 0xC9 +#define R_reference_l 0xBD +#define R_reference_m 0xBC +#define R_reference_h 0xBB +#define R_mask_l 0xBA +#define R_mask_m 0xB9 +#define R_mask_h 0xB8 +#define R_tolerance 0xCE +#define R_out_mode 0xCF + +/* the vpx322x FP-RAM registers */ +/* Standard Selection */ +#define RFP_sdt 0x20 /* w Standard select Stand. Sel. */ +#define RFP_insel 0x21 /* w Input select Stand. Sel. */ +#define RFP_sfif 0x22 /* w start point of active video Stand. Sel. */ +#define RFP_ldly 0x23 /* w luma/chroma delay adjust Stand. Sel. */ + +/* Color Processing */ +#define RFP_accref 0x30 /* w ACC reference level (Cr, Cb lev. pic. bus Color Proc. */ +#define RFP_accb 0x32 /* w ACC multiplier for SECAM Db chroma comp Color Proc. */ +#define RFP_accr 0x33 /* w ACC multiplier for SECAM Dr chroma comp Color Proc. */ +#define RFP_kilvl 0x39 /* w amplitude killer level Color Proc. */ +#define RFP_kilhy 0x3A /* w amplitude killer hysteresis Color Proc. */ +#define RFP_tint 0xDC /* w NTSC tint angle, +-512 = +-p/4 Color Proc. */ + +/* DVCO */ +#define RFP_xlck 0xF7 /* w/r crystal oscillator line-locked mode DVCO */ +#define RFP_dvco 0xF8 /* w crystal oscillator center frequency adjust DVCO */ +#define RFP_adjust 0xF9 /* r crystal oscillator ctr freq adj val DVCO */ + +/* FP Status Register */ +#define RFP_gp_ctrl 0x12 /* r/w general purpose control Status */ +#define RFP_asr 0x13 /* r standard recognition status Status */ +#define RFP_nlpf 0xCB /* r number of lines per field, P/S:312, N:262 Status */ +#define RFP_vcnt 0x15 /* r vertical field counter Status */ +#define RFP_sampl 0x74 /* r measured sync amplitude value Status */ +#define RFP_bampl 0x31 /* r measured burst amplitude Status */ +#define RFP_version 0xF0 /* r software version number Status */ + +/* Macrovision Detection ( version D4 only ) */ +#define RFP_mcv_status 0x170 +#define RFP_mcv_start 0x171 +#define RFP_mcv_stop 0x172 + +/* Read Table for Window 1 */ +#define RFP_vact_delay1 0x10F /* r Delay of VACT rltv. to HREF during wndw 1 ReadTab1 */ + +/* Write Table for Window 1 */ +#define RFP_vbegin1 0x120 /* w Vertical Begin WinLoadTab1 */ +#define RFP_vlinesin1 0x121 /* w Vertical LnsIn / Temp.Decim. / FldSel WinLoadTab1 */ +#define RFP_vlinesout1 0x122 /* w Vertical Lines Out WinLoadTab1 */ +#define RFP_hbeg1 0x123 /* w Horizontal Begin WinLoadTab1 */ +#define RFP_hlen1 0x124 /* w Horizontal Length WinLoadTab1 */ +#define RFP_npix1 0x125 /* w Number of Pixels WinLoadTab1 */ +#define RFP_peaking1 0x126 /* w Selection for peaking / coring WinLoadTab1 */ +#define RFP_brightness1 0x127 /* w Brightness WinLoadTab1 */ +#define RFP_contrast1 0x128 /* w Contrast / Noise shaping / Clamping WinLoadTab1 */ + +/* Read Table for Window 2 */ +#define RFP_vact_delay2 0x11F /* r Delay of VACT rltv. to HREF during wndw 2 ReadTab2 */ + +/* Write Table for Window 2 */ +#define RFP_vbegin2 0x12A /* w Vertical Begin WinLoadTab2 */ +#define RFP_vlinesin2 0x12B /* w Vertical Lines In WinLoadTab2 */ +#define RFP_vlinesout2 0x12C /* w Vertical Lines Out WinLoadTab2 */ +#define RFP_hbeg2 0x12D /* w Horizontal Begin WinLoadTab2 */ +#define RFP_hlen2 0x12E /* w Horizontal Length WinLoadTab2 */ +#define RFP_npix2 0x12F /* w Number of Pixels WinLoadTab2 */ +#define RFP_peaking2 0x130 /* w Selection for peaking / coring WinLoadTab2 */ +#define RFP_brightness2 0x131 /* w Brightness WinLoadTab2 */ +#define RFP_contrast2 0x132 /* w Contrast WinLoadTab2 */ + +/* Load Table for VBI Window */ +#define RFP_start_even 0x134 /* w Start line even field VBI-window */ +#define RFP_end_even 0x135 /* w End line even field VBI-window */ +#define RFP_start_odd 0x136 /* w Start line odd field VBI-window */ +#define RFP_end_odd 0x137 /* w End line odd field VBI-window */ +#define RFP_vbicontrol 0x138 /* w Control VBI-Window VBI-window */ +#define RFP_slsize 0x139 /* w Slicer Data Size VBI-window */ + +/* Control Word */ +#define RFP_ControlWord 0x140 /* w/r Register for control and latching */ + +/* Info Word */ +#define RFP_InfoWord 0x141 /* r Internal status register, do not overwrite */ + +/* Formatter */ +#define RFP_format_sel 0x150 /* w Format Selection / Shuffler / PIXCLK-mode Formatter */ + +/* HVREF */ +#define RFP_pval_start 0x151 /* w Strt pos of the prgrammble "video active" HVREF */ +#define RFP_pval_stop 0x152 /* w End pos of the prgrammble "video active" HVREF */ +#define RFP_refsig 0x153 /* w Length and polarity of HREF, VREF, FIELD HVREF */ + +/* Output Multiplexer */ +#define RFP_outmux 0x154 /* w Output Multiplexer / Multi-purpose output Output Mux. */ + +/* Temporal Decimation */ +#define RFP_tdecframes 0x157 /* w Num of frams to output within 3000 frames Temp. Decim. */ + +#if defined (CONFIG_V3TV_VERBOSEDEBUG) + +#define STR_fmt "%s " +#define VPX_LABEL(reg) (reg < vpx_size) ? vpx_registers[reg] : "INVALID", +#define VPXFP_LABEL(reg) (reg < vpxFP_size) ? vpxFP_registers[reg] : "INVALID", + +#define vpxFP_size 0x173 +#define vpx_size 0xFA + +const char *vpx_registers[vpx_size] = { +#if __GNUC__ >= 3 + [0 ... vpx_size - 1] = "unknown", +#endif + + [R_JEDEC] = "JEDEC", + [R_PARTLOW] = "PARTLOW", + [R_PARTHIGH] = "PARTHIGH", + [R_JEDEC2] = "JEDEC2", + [R_FPSTA] = "FPSTA", + [R_FPRD] = "FPRD", + [R_FPWR] = "FPWR", + [R_FPDAT] = "FPDAT", + [R_llc] = "llc", + [R_soft_cnt] = "soft_cnt", + [R_sync_stat] = "sync_stat", + [R_sync_count] = "sync_count", + [R_coeff_rd] = "coeff_rd", + [R_level_rd] = "level_rd", + [R_soft_slicer] = "soft_slicer", + [R_ttx_freql] = "ttx_freql", + [R_ttx_freqh] = "ttx_freqh", + [R_filter] = "filter", + [R_data_slicer] = "data_slicer", + [R_accu] = "accu", + [R_sync_slicer] = "sync_slicer", + [R_standard] = "standard", + [R_reference_l] = "reference_l", + [R_reference_m] = "reference_m", + [R_reference_h] = "reference_h", + [R_mask_l] = "mask_l", + [R_mask_m] = "mask_m", + [R_mask_h] = "mask_h", + [R_tolerance] = "tolerance", + [R_out_mode] = "out_mode", + [R_oena] = "oena", + [R_driver_a] = "driver_a", + [R_driver_b] = "driver_b" +}; + +const char *vpxFP_registers[vpxFP_size] = { +#if __GNUC__ >= 3 + [0 ... vpxFP_size - 1] = "unknown", +#endif + + [RFP_gp_ctrl] = "gp_ctrl", + [RFP_asr] = "asr", + [RFP_vcnt] = "vcnt", + [RFP_sdt] = "sdt", + [RFP_insel] = "insel", + [RFP_sfif] = "sfif", + [RFP_ldly] = "ldly", + [RFP_accref] = "accref", + [RFP_bampl] = "bampl", + [RFP_accb] = "accb", + [RFP_accr] = "accr", + [RFP_kilvl] = "kilvl", + [RFP_kilhy] = "kilhy", + [RFP_sampl] = "sampl", + [RFP_nlpf] = "nlpf", + [RFP_tint] = "tint", + [RFP_version] = "version", + [RFP_xlck] = "xlck", + [RFP_dvco] = "dvco", + [RFP_adjust] = "adjust", + [RFP_vact_delay1] = "vact_delay1", + [RFP_vact_delay2] = "vact_delay2", + [RFP_vbegin1] = "vbegin1", + [RFP_vlinesin1] = "vlinesin1", + [RFP_vlinesout1] = "vlinesout1", + [RFP_hbeg1] = "hbeg1", + [RFP_hlen1] = "hlen1", + [RFP_npix1] = "npix1", + [RFP_peaking1] = "peaking1", + [RFP_brightness1] = "brightness1", + [RFP_contrast1] = "contrast1", + [RFP_vbegin2] = "vbegin2", + [RFP_vlinesin2] = "vlinesin2", + [RFP_vlinesout2] = "vlinesout2", + [RFP_hbeg2] = "hbeg2", + [RFP_hlen2] = "hlen2", + [RFP_npix2] = "npix2", + [RFP_peaking2] = "peaking2", + [RFP_brightness2] = "brightness2", + [RFP_contrast2] = "contrast2", + [RFP_start_even] = "start_even", + [RFP_end_even] = "end_even", + [RFP_start_odd] = "start_odd", + [RFP_end_odd] = "end_odd", + [RFP_vbicontrol] = "vbicontrol", + [RFP_slsize] = "slsize", + [RFP_ControlWord] = "ControlWord", + [RFP_InfoWord] = "InfoWord", + [RFP_format_sel] = "format_sel", + [RFP_pval_start] = "pval_start", + [RFP_pval_stop] = "pval_stop", + [RFP_refsig] = "refsig", + [RFP_outmux] = "outmux", + [RFP_tdecframes] = "tdecframes", + [RFP_mcv_status] = "mcv_status", + [RFP_mcv_start] = "mcv_start", + [RFP_mcv_stop] = "mcv_stop" +}; + +#else /* CONFIG_V3TV_VERBOSEDEBUG not defined */ +#define STR_fmt +#define VPX_LABEL(reg) +#define VPXFP_LABEL(reg) +#endif + +#define DEB(x) if (debug>=1) x + +#if defined (CONFIG_V3TV_VERBOSEDEBUG) +#define DEB2(x) if (debug>=2) x +#define DEB3(x) if (debug>=3) x + +#else +#define DEB2(x) +#define DEB3(x) +#endif + +/* ---------------------------------------------------------------------- */ +/* register intialization values */ + +static const u16 init_ntsc[] = { + RFP_tint, 0x00, /* NTSC tint angle */ +// RFP_vbegin1, 30, /* Window 1 vertical - 17 */ + RFP_vbegin1, 23, /* Window 1 vertical */ + RFP_vlinesin1, 240, /* Vertical lines in */ + RFP_vlinesout1, 240, /* Vertical lines out */ + RFP_hbeg1, 0, /* Horizontal begin */ + RFP_hlen1, 640, /* Horizontal length */ + RFP_npix1, 640, /* Number of pixels */ + RFP_sdt, 1, /* NTSC M, */ +}; + +static const u16 init_pal[] = { + RFP_vbegin1, 0, /* Window 1 vertical begin */ +// RFP_vbegin1, 23, /* Window 1 vertical begin */ + RFP_vlinesin1, 288 + 16, /* Vertical lines in (16 lines + * skipped by the VFE) */ + RFP_vlinesout1, 288 + 16, /* Vertical lines out (16 lines + * skipped by the VFE) */ + RFP_hbeg1, 16, /* Horizontal begin */ + RFP_hlen1, 768, /* Horizontal length */ + RFP_npix1, 784, /* Number of pixels + * Must be >= Horizontal begin + Horizontal length */ + RFP_sdt, 0, /* PAL B,G,H,I, */ +}; + +static const u16 init_secam[] = { + RFP_vbegin1, 23 - 16, /* Window 1 vertical begin */ + RFP_vlinesin1, 288 + 16, /* Vertical lines in (16 lines + * skipped by the VFE) */ + RFP_vlinesout1, 288 + 16, /* Vertical lines out (16 lines + * skipped by the VFE) */ + RFP_hbeg1, 16, /* Horizontal begin */ + RFP_hlen1, 768, /* Horizontal length */ + RFP_npix1, 784, /* Number of pixels + * Must be >= Horizontal begin + Horizontal length */ + RFP_sdt, 2, /* SECAM, */ +}; + +static const u16 init_fp[] = { /* 16 reg-value pairs */ + RFP_format_sel, 1, + RFP_accref, 0x800, /* 0x75C, *//* ACC reference */ + RFP_outmux, 0x200, /* set up output multiplexed 8bit data stream */ + RFP_sfif, 0, /* picture start position */ + RFP_vbicontrol, 0, /* disable VBI window */ + RFP_peaking1, 0x100, /* swap chroma values, Cr Pixels first */ + RFP_vlinesin2, 0xc00, /* Disable window 2 */ + RFP_brightness1, 0, + RFP_brightness2, 0, + RFP_contrast1, 32, + RFP_contrast2, 32 +}; + +static const unsigned char init_common[] = { + R_llc, 0, /* active mode, outputs enabled */ + R_driver_a, 0x24, /* Port A, PIXCLK, HF# & FE# + * stra1 = 100; stra2 = 100 */ + R_driver_b, 0x20, /* Port B, HREF, VREF, PREF & + * strb2 = 100 */ + R_sync_slicer, 0x40, + R_oena, 0x00, /* Disable all outputs */ +}; + +/* ---------------------------------------------------------------------- */ +/* I2C access functions */ + +static int vpx3224_write(struct i2c_client *client, u8 reg, u8 value) +{ + int res = 0; + + res = i2c_smbus_write_byte_data(client, reg, value); + DEB2(printk + (KERN_INFO "%s: Write %sreg: " STR_fmt "0x%x; value: 0x%x\n", + i2c_clientname(client), (res < 0) ? "failed!! " : "", + VPX_LABEL(reg) reg, value)); + return res; +} + +static int vpx3224_read(struct i2c_client *client, u8 reg) +{ + int res; + + res = i2c_smbus_read_byte_data(client, reg); + if (reg != R_FPSTA) + DEB2(printk + (KERN_INFO "%s: Read %sreg: " STR_fmt + "0x%x; value: 0x%x\n", i2c_clientname(client), + (res < 0) ? "failed!! " : "", VPX_LABEL(reg) reg, + res)); + + return res; +} + +static int +vpx3224_write_block(struct i2c_client *client, + const u8 * data, unsigned int len) +{ + u8 reg; + int res = 0; + + while (len >= 2) { + reg = *data++; + res = vpx3224_write(client, reg, *data++); + if (res < 0) + break; + len -= 2; + } + + return res; +} + +/* ---------------------------------------------------------------------- */ +/* Fast Processor functions */ + +static int vpx3224_fp_status(struct i2c_client *client) +{ + int status, i; + + for (i = 0; i < VPX_TIMEOUT_COUNT; i++) { + status = vpx3224_read(client, R_FPSTA); + + if (status < 4) { + DEB3(printk(KERN_INFO "%s: FPSTA %s request: %x\n", + i2c_clientname(client), + (status == 1) ? "write" : (status == + 2) ? "read" + : "invalid", status)); + return status; + } + udelay(10); + + if (need_resched()) + cond_resched(); + } + printk(KERN_INFO "%s: FPSTA failed: status: %d\n", + i2c_clientname(client), status); + + return -1; +} + +static int +vpx3224_fp_write(struct i2c_client *client, u16 fpaddr, u16 data) +{ + int res; + /* Write the 16-bit address to the FPWR register */ + if (i2c_smbus_write_word_data(client, R_FPWR, swab16(fpaddr)) == + -1) { + printk(KERN_INFO "%s: write to R_FPWR failed!! fpaddr: " + STR_fmt "0x%x; data: 0x%x\n", + i2c_clientname(client), VPXFP_LABEL(fpaddr) fpaddr, + data); + return -1; + } + + if (vpx3224_fp_status(client) == -1) + return -1; + + /* Write the 16-bit data to the FPDAT register */ + res = i2c_smbus_write_word_data(client, R_FPDAT, swab16(data)); + DEB2(printk + (KERN_INFO "%s: Write %sfpaddr: " STR_fmt + "0x%x; data: 0x%x\n", i2c_clientname(client), + (res < 0) ? "write to R_FPDAT failed!! " : "", + VPXFP_LABEL(fpaddr) fpaddr, data)); + + return res; +} + +static int vpx3224_fp_read(struct i2c_client *client, u16 fpaddr) +{ + int data; + + /* Write the 16-bit address to the FPRD register */ + if (i2c_smbus_write_word_data(client, R_FPRD, swab16(fpaddr)) == + -1) { + printk(KERN_INFO "%s: write to R_FPRD failed!! fpaddr: " + STR_fmt "0x%d\n", i2c_clientname(client), + VPXFP_LABEL(fpaddr) fpaddr); + return -1; + } + + if (vpx3224_fp_status(client) == -1) + return -1; + + /* Read the 16-bit data from the FPDAT register */ + data = swab16(i2c_smbus_read_word_data(client, R_FPDAT)); + DEB2(printk + (KERN_INFO "%s: Read %sfpaddr: " STR_fmt "0x%x data: 0x%x\n", + i2c_clientname(client), + (data < 0) ? "read R_FPDAT failed!! " : "", + VPXFP_LABEL(fpaddr) fpaddr, data)); + + return data; +} + +static int +vpx3224_write_fp_block(struct i2c_client *client, + const u16 * data, int len) +{ + u16 reg; + int res = 0; + + while (len > 1) { + reg = *data++; + res = vpx3224_fp_write(client, reg, *data++); + if (res < 0) + return res; + + len -= 2; + } + + return res; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#if defined (CONFIG_V3TV_VERBOSEDEBUG) & defined (CONFIG_PROC_FS) +#include "vpx3224-proc.c" +#endif +#endif + +/* ---------------------------------------------------------------------- + * Client procedures */ + +static int vpx3224_get_status(struct i2c_client *client) +{ + int res = 0, status, sdt; + + status = vpx3224_fp_read(client, RFP_asr); + if (status < 0) + return status; + + sdt = vpx3224_fp_read(client, RFP_sdt); + if (sdt < 0) + return sdt; + + DEB(printk(KERN_INFO "%s: status: 0x%x; standard: 0x%x\n", + i2c_clientname(client), status, sdt)); + + /* bit 2 = 1 == no signal detected */ + /* bit 3 = 1 == color amplitude killer active */ + /* bit 5 = 1 == color ident killer active */ + if ((status & 0x04) == 0) { + res |= DECODER_STATUS_GOOD; + + DEB(printk(KERN_INFO "%s: Video signal detected.\n", + i2c_clientname(client))); + + if ((status & 0x08) == 0 || (status & 0x20) == 0) + res |= DECODER_STATUS_COLOR; + else + DEB(printk(KERN_INFO "%s: Color killer active.\n", + i2c_clientname(client))); + + switch (sdt & 7) { + + case VPX_PALBGHI: + case VPX_PALM: + case VPX_PALN: + case VPX_PAL60: + res |= DECODER_STATUS_PAL; + break; + + case VPX_SECAM: + res |= DECODER_STATUS_SECAM; + break; + + case VPX_NTSCM: + case VPX_NTSC44: + case VPX_NTSCCOMB: + res |= DECODER_STATUS_NTSC; + break; + } + } + return res; +} + +#if 0 +static int vpx3224_auto_norm(struct i2c_client *client) +{ +/* * Auto is not supported by the vpx3224/5d. + * Scan the valid modes, read RFP_asr for score, pick the highest score. + * With an NTSC M signal, there is no clear winner... */ + DEB(printk + (KERN_INFO "%s: Auto video mode detection not supported.\n", + i2c_clientname(client))); + + int i, res = 0; + char *sdt_desc[8] = { "PAL B,G,H,I", "NTSC M", "SECAM", "NTSC44", + "PAL M", "PAL N", "PAL 60", "NTSC COMB" + }; + + for (i = 0; i < 8; i++) { + vpx3224_fp_write(client, RFP_sdt, i); + res = vpx3224_fp_read(client, RFP_asr) & 0x3ff; + DEB(printk(KERN_INFO "\tRFP_ASR: %s 0x%03x, %s; %s; %s\n", + sdt_desc[i], res, + (res & 1) ? "vetical lock" : "no v-lock", + (res & 2) ? "horizontal lock" : "no h-lock", + (res & 4) ? "NO signal detected" : + "Signal Detected")); + DEB(printk + (KERN_INFO "\t\t%s; %s\n", + (res & 8) ? "color amplitude killer active" : + "color signal", + (res & 16) ? "amplitude killer disabled" : "")); + DEB(printk + (KERN_INFO "\t\t%s; %s\n", + (res & 32) ? "color ident killer active" : + "color ident signal", + (res & 64) ? "disable ident killer" : "")); + DEB(printk + (KERN_INFO "\t\t%s; %s; %s\n", + (res & 128) ? "interlace detected" : "no interlace", + (res & 256) ? "no vertical sync" : "v-sync", + (res & 512) ? "spurious v-sync" : "")); + } + return -EINVAL; +} +#endif + +static int vpx3224_set_norm(struct i2c_client *client, int norm) +{ + int res = 0; + struct vpx3224 *decoder = i2c_get_clientdata(client); + char *norms[4] = {"PAL", "NTSC", "SECAM", "AUTO"}; + + switch (norm) { + case VIDEO_MODE_NTSC: + res = vpx3224_write_fp_block(client, init_ntsc, + sizeof (init_ntsc) >> 1); + break; + + case VIDEO_MODE_PAL: + res = vpx3224_write_fp_block(client, init_pal, + sizeof (init_pal) >> 1); + break; + + case VIDEO_MODE_SECAM: + res = vpx3224_write_fp_block(client, init_secam, + sizeof (init_secam) >> 1); + break; +/* case VIDEO_MODE_AUTO: + norm = vpx3224_auto_norm(client); + break; +*/ + default: + return -EINVAL; + } + if (!res) { + decoder->norm = norm; + vpx3224_fp_write(client, RFP_accref, decoder->sat >> 4); + + DEB(printk(KERN_INFO "%s: norm switched to %s\n", + i2c_clientname(client), norms[norm])); + } + else + DEB(printk(KERN_INFO "%s: Unable to set video norm.", + i2c_clientname(client))); + return res; +} + +static int vpx3224_set_input(struct i2c_client *client, int chan) +{ + int sdt; + struct vpx3224 *decoder = i2c_get_clientdata(client); + + static char *inputs[] = { "Composite", "Tuner", "S-Video" }; + + static const unsigned int input[3][2] = { + /* insel, sdt RFP_insel: bit 10 = vcr */ + {0x400, 0}, /* VIN3: composite, sdt.bit[6]=0 */ + {0x001, 0}, /* VIN2: tuner, sdt.bit[6]=0 */ + {0x406, 1}, /* VIN1: SVHS: Chroma in, sdt.bit[6]=1 */ + }; + + if (chan < 0 || chan > 2) + return -EINVAL; + + decoder->input = chan; + + vpx3224_fp_write(client, RFP_insel, input[decoder->input][0]); + + sdt = vpx3224_fp_read(client, RFP_sdt); + if (input[decoder->input][1]) + sdt |= 0x40; /* S-VHS mode */ + + vpx3224_fp_write(client, RFP_sdt, sdt & 0x3CF); + DEB(printk(KERN_INFO "%s: input switched to %s\n", + i2c_clientname(client), inputs[decoder->input])); + + return 0; +} + +static int vpx3224_set_picture(struct i2c_client *client, void *arg) +{ + struct vpx3224 *decoder = i2c_get_clientdata(client); + + int latch = 0; + struct video_picture *pic = arg; + + DEB(printk(KERN_INFO "%s:vpx reg-> brightness: 0x%03x " + "hue: 0x%03x contrast: 0x%03x colour: 0x%03x\n", + i2c_clientname(client), + ((s16) pic->brightness - (s16) 0x8000) >> 8, + ((s16) pic->hue - (s16) 0x8000) >> 4, + pic->contrast >> 10, pic->colour >> 4)); + + /* bright, contrast, sat, hue = 0,... ,65535 */ + if (decoder->bright != pic->brightness) { + latch = 1; + /* We want -127 to 128 (binary offset: 0 to 255) we get 0-65535 */ + decoder->bright = pic->brightness; + vpx3224_fp_write(client, RFP_brightness1, + ((s16) decoder->bright - + (s16) 0x8000) >> 8); + vpx3224_fp_write(client, RFP_brightness2, + ((s16) decoder->bright - + (s16) 0x8000) >> 8); + } + + if (decoder->contrast != pic->contrast) { + latch = 1; + /* We want 0 to 64 we get 0-65535 */ + /* Bit 7 and 8 is for noise shaping (decoder->contrast >> 10) + 192) */ + decoder->contrast = pic->contrast; + vpx3224_fp_write(client, RFP_contrast1, + decoder->contrast >> 10); + vpx3224_fp_write(client, RFP_contrast2, + decoder->contrast >> 10); + } + + if (decoder->sat != pic->colour) { + latch = 1; + /* We want 0 to 4096 we get 0-65535 */ + decoder->sat = pic->colour; + vpx3224_fp_write(client, RFP_accref, decoder->sat >> 4); + } + + if ((decoder->hue != pic->hue) + && (decoder->norm == VIDEO_MODE_NTSC)) { + latch = 1; + /* We want -512 to 512 we get 0-65535 */ + decoder->hue = pic->hue; + vpx3224_fp_write(client, RFP_tint, + ((s16) decoder->hue - (s16) 0x8000) >> 4); + } + + if (latch) { /* latch WinTable1+2 */ + vpx3224_fp_write(client, RFP_ControlWord, + 32 + 64 + + (vpx3224_fp_read(client, RFP_ControlWord) + & 7)); + } + + return 0; +} + +#if 0 +static int vpx3224_set_window(struct i2c_client *client, void *arg) +{ + struct video_capture *window = arg; + + vpx3224_fp_write(client, RFP_vbegin1, window->y); + vpx3224_fp_write(client, RFP_vlinesin1, window->height); + vpx3224_fp_write(client, RFP_vlinesout1, window->height); + vpx3224_fp_write(client, RFP_hbeg1, window->x); + vpx3224_fp_write(client, RFP_hlen1, window->width); + vpx3224_fp_write(client, RFP_npix1, window->width + window->x); + + vpx3224_fp_write(client, RFP_ControlWord, + 32 + 64 + + (vpx3224_fp_read(client, RFP_ControlWord) & 7)); + + return 0; +} +#endif + +static int vpx3224_init_client(struct i2c_client *client) +{ + int res; + res = + vpx3224_write_block(client, init_common, sizeof (init_common)); + if (res < 0) + return res; + res = + vpx3224_write_fp_block(client, init_fp, sizeof (init_fp) >> 1); + if (res < 0) + return res; + + res = vpx3224_set_input(client, 0); + if (res < 0) + return res; + +/* * We cycle through the supported modes + * and read RFP_asr to see which is valid. + * restore this line to see output when module loads */ +/* vpx3224_auto_norm(client); */ + res = vpx3224_set_norm(client, VIDEO_MODE_NTSC); + if (res < 0) + return res; + + return 0; +} + +static int vpx3224_check_partnum(struct i2c_client *client, int kind) +{ + u8 id; + u16 pn; + + if (kind < 0) { + /* Check for manufacture ID and part number */ + id = vpx3224_read(client, R_JEDEC); + if (id != MICRONAS_INTERMETALL) { + printk(KERN_INFO + "%s: Wrong manufacturer ID (0x%02x)\n", + THIS_MODULE->name, id); + return -1; + } + + pn = (vpx3224_read(client, R_PARTHIGH) << 8) + + vpx3224_read(client, R_PARTLOW); + switch (pn) { + case VPX3220A: + printk(KERN_INFO + "%s: vpx3220a not supported (0x%x) Try the vpx3220 driver.\n", + THIS_MODULE->name, pn); + return -1; + break; + case VPX3216B: + printk(KERN_INFO + "%s: vpx3216b not supported (0x%x) Try the vpx3220 driver.\n", + THIS_MODULE->name, pn); + return -1; + break; + case VPX3214C: + printk(KERN_INFO + "%s: vpx3214c not supported (0x%x) Try the vpx3220 driver.\n", + THIS_MODULE->name, pn); + return -1; + break; + case VPX3225D: + snprintf(i2c_clientname(client), + sizeof (client->name) - 1, "vpx3225d"); + break; + case VPX3224D: + snprintf(i2c_clientname(client), + sizeof (client->name) - 1, "vpx3224d"); + break; + default: + printk(KERN_INFO + "%s: Wrong part number (0x%04x)\n", + i2c_clientname(client), pn); + return -1; + } + } else { + snprintf(i2c_clientname(client), sizeof (client->name) - 1, + "forced vpx32xx"); + } + return 0; +} + +static int +vpx3224_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct vpx3224 *decoder = i2c_get_clientdata(client); + int res = 0; + + switch (cmd) { + case 0:{ + return vpx3224_init_client(client); + break; + } + + case DECODER_GET_CAPABILITIES:{ + struct video_decoder_capability *cap = arg; + + DEB(printk + (KERN_INFO "%s: DECODER_GET_CAPABILITIES\n", + i2c_clientname(client))); + + cap->flags = VIDEO_DECODER_PAL + | VIDEO_DECODER_NTSC | VIDEO_DECODER_SECAM; +/* | VIDEO_DECODER_AUTO ; // see vpx3224_auto_norm */ + cap->inputs = VPX3224_MAX_INPUT; + cap->outputs = VPX3224_MAX_OUTPUT; + break; + } + + case DECODER_GET_STATUS:{ + DEB(printk(KERN_INFO "%s: DECODER_GET_STATUS\n", + i2c_clientname(client))); + res = vpx3224_get_status(client); + *(int *) arg = res; + break; + } + + case DECODER_SET_NORM:{ + DEB(printk(KERN_INFO "%s: DECODER_SET_NORM %d\n", + i2c_clientname(client), *(int *) arg)); + res = vpx3224_set_norm(client, *(int *) arg); + break; + } + + case DECODER_SET_INPUT:{ + DEB(printk(KERN_INFO "%s: DECODER_SET_INPUT\n", + i2c_clientname(client))); + res = vpx3224_set_input(client, *(int *) arg); + break; + } + + case DECODER_SET_OUTPUT:{ + int *iarg = arg; + + DEB(printk(KERN_INFO "%s: DECODER_SET_OUTPUT\n", + i2c_clientname(client))); + /* not much choice of outputs */ + if (iarg != 0) + return -EINVAL; + break; + } + + case DECODER_ENABLE_OUTPUT:{ + if (decoder->enable != *(int *) arg) { + decoder->enable = *(int *) arg; + DEB(printk + (KERN_INFO + "%s: DECODER_ENABLE_OUTPUT: %s\n", + i2c_clientname(client), + (decoder->enable) ? + "Enable" : "Disable")); + res = vpx3224_write(client, R_oena, + (decoder->enable) ? 0x0F : 0); + } + break; + } + + case DECODER_SET_PICTURE:{ + DEB(printk(KERN_INFO "%s: DECODER_SET_PICTURE\n", + i2c_clientname(client))); + res = vpx3224_set_picture(client, arg); + break; + } +#if 0 + case DECODER_SET_WINDOW:{ + DEB(printk( KERN_INFO "%s: DECODER_SET_WINDOW\n", + i2c_clientname(client))); + res = vpx3224_set_window(client, arg); + break; + } +#endif + default: + return -EINVAL; + } + return res; +} + +/* ----------------------------------------------------------------------- + * Client managment code */ + +/* + * Generic i2c probe data + */ +static unsigned short normal_i2c[] = { I2C_VPX3224, I2C_CLIENT_END }; +// static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +// static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +// static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_i2c, +// .normal_i2c_range = normal_i2c_range, + .probe = probe, +// .probe_range = probe_range, + .ignore = ignore, +// .ignore_range = ignore_range, + .force = force +}; + +/* Maintain a count of registered clients */ +static int vpx3224_i2c_id = 0; + +static int vpx3224_attach_adapter(struct i2c_adapter *adapter) +{ + int res = 0; + +#ifdef I2C_ADAP_CLASS_TV_ANALOG + if (adapter->class & I2C_ADAP_CLASS_TV_ANALOG) + res = i2c_probe(adapter, &addr_data, vpx3224_detect_client); +#else + if (adapter->id & (I2C_ALGO_BIT | I2C_HW_SMBUS_VOODOO3)) + res = i2c_probe(adapter, &addr_data, vpx3224_detect_client); +#endif + DEB2(printk(KERN_INFO "%s: i2c_probe of adapter: %s returned %d\n", + THIS_MODULE->name, adapter->name, res)); + + return res; /* == vpx3224_i2c_id */ +} + +/* call i2c_detach_client to remove client from adapter->clients[] */ +static int vpx3224_detach_client(struct i2c_client *client) +{ + struct vpx3224 *decoder = i2c_get_clientdata(client); + + int res; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#if defined (CONFIG_PROC_FS) && defined (CONFIG_V3TV_VERBOSEDEBUG) + vpx3224_del_proc(client); +#endif +#endif + + res = i2c_detach_client(client); + DEB2(printk(KERN_INFO "%s: i2c_detach_client returned %d\n", + THIS_MODULE->name, res)); + if (res < 0) { + printk(KERN_ERR "%s: %s: Unable to detach client.\n", + THIS_MODULE->name, i2c_clientname(client)); + return res; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +// MOD_DEC_USE_COUNT; +#else + module_put(client->adapter->owner); +#endif + kfree(decoder); + kfree(client); + vpx3224_i2c_id--; + + return 0; +} + +/* ----------------------------------------------------------------------- + * Driver initialization and cleanup code + * + * ----------------------------------------------------------------------- + * I2C driver definition */ + +static struct i2c_driver vpx3224_i2c_driver = { + .owner = THIS_MODULE, + .name = "vpx3224.o", + + .id = I2C_DRIVERID_VPX3224, + .flags = I2C_DF_NOTIFY, + + .attach_adapter = vpx3224_attach_adapter, + .detach_client = vpx3224_detach_client, + .command = vpx3224_command +}; + +/* vpx3224_detect_client - aka: i2c_client_found_addr_proc *found_proc + * so called by i2c_probe, res passed back to vpx3224_attach_adapter */ +static int vpx3224_detect_client(struct i2c_adapter *adapter, int address, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + unsigned short flags, +#endif + int kind) +{ + int res; + struct i2c_client *client; + struct vpx3224 *decoder; + + DEB2(printk + (KERN_INFO "%s: i2c_probe found adapter: %s; address: %d\n", + THIS_MODULE->name, adapter->name, address)); + /* Check if the adapter supports the needed features */ + res = i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA); + DEB2(printk + (KERN_INFO "%s: i2c_check_functionality returned %d\n", + THIS_MODULE->name, res)); + if (res <= 0) + return -ENOSYS; /* function not implemented */ + + client = kmalloc(sizeof (struct i2c_client), GFP_KERNEL); + DEB2(printk(KERN_INFO "%s: kmalloc(client) returned %p\n", + THIS_MODULE->name, client)); + if (client == NULL) { + return -ENOMEM; + } + + memset(client, 0, sizeof (struct i2c_client)); + + vpx3224_i2c_id++; /* ++ to let vpx3224_init know the client connected */ + client->addr = address; + client->adapter = adapter; + client->driver = &vpx3224_i2c_driver; + client->flags = I2C_CLIENT_ALLOW_USE; + + if (vpx3224_check_partnum(client, kind) < 0) { + kfree(client); + vpx3224_i2c_id--; + return -ENODEV; + } + + decoder = kmalloc(sizeof (struct vpx3224), GFP_KERNEL); + DEB2(printk(KERN_INFO "%s: kmalloc(decoder) returned %p\n", + THIS_MODULE->name, decoder)); + if (decoder == NULL) { + kfree(client); + vpx3224_i2c_id--; /* -- to let vpx3224_init know the client did not connect */ + return -ENOMEM; + } + + memset(decoder, 0, sizeof (struct vpx3224)); + i2c_set_clientdata(client, decoder); + + decoder->norm = VIDEO_MODE_NTSC; + decoder->input = 0; + decoder->enable = 0; + decoder->bright = 32768; + decoder->contrast = 32768; + decoder->hue = 32768; + decoder->sat = 32768; + +/* i2c_attach_client adds client to client->adapter->clients[] + returns: + -EBUSY + -ENOMEM enlarge I2C_CLIENT_MAX */ + res = i2c_attach_client(client); + DEB2(printk(KERN_INFO "%s: i2c_attach_client returned %d\n", + i2c_clientname(client), res)); + if (res < 0) { + vpx3224_detach_client(client); + return res; + } + + res = vpx3224_init_client(client); + if (res < 0) { + printk(KERN_INFO "%s: Initialization error!\n", + i2c_clientname(client)); + vpx3224_detach_client(client); + return res; + } + + printk(KERN_INFO "%s: %s client found at address 0x%x\n", + THIS_MODULE->name, i2c_clientname(client), client->addr); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#if defined (CONFIG_PROC_FS) && defined (CONFIG_V3TV_VERBOSEDEBUG) + vpx3224_init_proc(client); +#endif +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +// MOD_INC_USE_COUNT; +#else + try_module_get(client->adapter->owner); +#endif + return vpx3224_i2c_id; +} + +static void vpx3224_exit(void) +{ + int res; + + printk(KERN_INFO "%s: version " VPX_VERSION + " (" __DATE__ "): Exiting.\n", THIS_MODULE->name); + + res = i2c_del_driver(&vpx3224_i2c_driver); + + DEB2(printk(KERN_INFO "%s: i2c_del_driver returnd 0x%02x.\n", + THIS_MODULE->name, res)); +} + +static int vpx3224_init(void) +{ + int err, res = vpx3224_i2c_id; + + printk(KERN_INFO "%s: version " VPX_VERSION + " (" __DATE__ ")\n", THIS_MODULE->name); + + /* calls vpx3224_i2c_driver.attach_adapter + * ignores attach_adapter return value */ + err = i2c_add_driver(&vpx3224_i2c_driver); + DEB2(printk(KERN_INFO "%s: i2c_add_driver returned %d\n", + THIS_MODULE->name, err)); + if (err < 0) { + vpx3224_exit(); + return err; + } + + if (vpx3224_i2c_id == res) { + printk(KERN_INFO "%s: No device detected.\n", + THIS_MODULE->name); + vpx3224_exit(); + return -ENODEV; + } + + return 0; +} + +module_init(vpx3224_init); +module_exit(vpx3224_exit); + +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debug level (0-3"); + +MODULE_DESCRIPTION("vpx3224d/vpx3225d video encoder driver"); +MODULE_AUTHOR("Perry E. Gilfilan <perrye@linuxmail.org"); +MODULE_LICENSE("GPL"); |