summaryrefslogtreecommitdiff
path: root/v4l_experimental
diff options
context:
space:
mode:
Diffstat (limited to 'v4l_experimental')
-rw-r--r--v4l_experimental/v3tv/i2c-voodoo3.c20
-rw-r--r--v4l_experimental/v3tv/v3tv.c3354
-rw-r--r--v4l_experimental/v3tv/vpx3224-proc.c823
-rw-r--r--v4l_experimental/v3tv/vpx3224.c1296
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");