V4L/DVB (5345): ivtv driver for Conexant cx23416/cx23415 MPEG encoder/decoder
Hans Verkuil [Fri, 27 Apr 2007 15:31:25 +0000 (12:31 -0300)]
It took three core maintainers, over four years of work, eight new i2c
modules, eleven new V4L2 ioctls, three new DVB video ioctls, a Sliced
VBI API, a new MPEG encoder API, an enhanced DVB video MPEG decoding
API, major YUV/OSD contributions from Ian and John, web/wiki/svn/trac
support from Axel Thimm, (hardware) support from Hauppauge, support and
assistance from the v4l-dvb people and the many, many users of ivtv to
finally make it possible to merge this driver into the kernel.
Thank you all!

Signed-off-by: Kevin Thayer <nufan_wfk@yahoo.com>
Signed-off-by: Chris Kennedy <c@groovy.org>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: John P Harvey <john.p.harvey@btinternet.com>
Signed-off-by: Ian Armstrong <ian@iarmst.demon.co.uk>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>

40 files changed:
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/ivtv/Kconfig [new file with mode: 0644]
drivers/media/video/ivtv/Makefile [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-audio.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-audio.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-cards.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-cards.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-controls.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-controls.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-driver.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-driver.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-fileops.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-fileops.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-firmware.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-firmware.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-gpio.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-gpio.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-i2c.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-i2c.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-ioctl.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-ioctl.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-irq.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-irq.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-mailbox.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-mailbox.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-queue.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-queue.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-streams.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-streams.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-udma.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-udma.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-vbi.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-vbi.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-version.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-video.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-video.h [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-yuv.c [new file with mode: 0644]
drivers/media/video/ivtv/ivtv-yuv.h [new file with mode: 0644]
include/media/ivtv.h [new file with mode: 0644]

index fa0a876..639e8b6 100644 (file)
@@ -647,6 +647,8 @@ config VIDEO_HEXIUM_GEMINI
 
 source "drivers/media/video/cx88/Kconfig"
 
+source "drivers/media/video/ivtv/Kconfig"
+
 config VIDEO_M32R_AR
        tristate "AR devices"
        depends on M32R && VIDEO_V4L1
index 384f01c..9c2de50 100644 (file)
@@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
 obj-$(CONFIG_VIDEO_MEYE) += meye.o
 obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/
 obj-$(CONFIG_VIDEO_CX88) += cx88/
+obj-$(CONFIG_VIDEO_IVTV) += ivtv/
 obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
 obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
 obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig
new file mode 100644 (file)
index 0000000..88e5101
--- /dev/null
@@ -0,0 +1,26 @@
+config VIDEO_IVTV
+       tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support"
+       depends on VIDEO_V4L2 && USB && I2C && EXPERIMENTAL
+       select FW_LOADER
+       select VIDEO_TUNER
+       select VIDEO_TVEEPROM
+       select VIDEO_CX2341X
+       select VIDEO_MSP3400
+       select VIDEO_SAA711X
+       select VIDEO_SAA7127
+       select VIDEO_TVAUDIO
+       select VIDEO_CS53L32A
+       select VIDEO_TLV320AIC23B
+       select VIDEO_WM8775
+       select VIDEO_WM8739
+       select VIDEO_UPD64031A
+       select VIDEO_UPD64083
+       ---help---
+         This is a video4linux driver for Conexant cx23416 or cx23416 based
+         PCI personal video recorder devices.
+
+         This is used in devices such as the Hauppauge PVR-150/250/350/500
+         cards.
+
+         To compile this driver as a module, choose M here: the
+         module will be called ivtv.
diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile
new file mode 100644 (file)
index 0000000..7e95148
--- /dev/null
@@ -0,0 +1,7 @@
+ivtv-objs      := ivtv-audio.o ivtv-cards.o ivtv-controls.o \
+                  ivtv-driver.o ivtv-fileops.o ivtv-firmware.o \
+                  ivtv-gpio.o ivtv-i2c.o ivtv-ioctl.o ivtv-irq.o \
+                  ivtv-mailbox.o ivtv-queue.o ivtv-streams.o ivtv-udma.o \
+                  ivtv-vbi.o ivtv-video.o ivtv-yuv.o
+
+obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
diff --git a/drivers/media/video/ivtv/ivtv-audio.c b/drivers/media/video/ivtv/ivtv-audio.c
new file mode 100644 (file)
index 0000000..d702b8b
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+    Audio-related ivtv functions.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-i2c.h"
+#include "ivtv-gpio.h"
+#include "ivtv-cards.h"
+#include "ivtv-audio.h"
+#include <media/msp3400.h>
+#include <linux/videodev.h>
+
+/* Selects the audio input and output according to the current
+   settings. */
+int ivtv_audio_set_io(struct ivtv *itv)
+{
+       struct v4l2_routing route;
+       u32 audio_input;
+       int mux_input;
+
+       /* Determine which input to use */
+       if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+               audio_input = itv->card->radio_input.audio_input;
+               mux_input = itv->card->radio_input.muxer_input;
+       } else {
+               audio_input = itv->card->audio_inputs[itv->audio_input].audio_input;
+               mux_input = itv->card->audio_inputs[itv->audio_input].muxer_input;
+       }
+
+       /* handle muxer chips */
+       route.input = mux_input;
+       route.output = 0;
+       ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+
+       route.input = audio_input;
+       if (itv->card->hw_audio & IVTV_HW_MSP34XX) {
+               route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
+       }
+       return ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+}
+
+void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route)
+{
+       ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route);
+}
+
+void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq)
+{
+       static u32 freqs[3] = { 44100, 48000, 32000 };
+
+       /* The audio clock of the digitizer must match the codec sample
+          rate otherwise you get some very strange effects. */
+       if (freq > 2)
+               return;
+       ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]);
+}
+
diff --git a/drivers/media/video/ivtv/ivtv-audio.h b/drivers/media/video/ivtv/ivtv-audio.h
new file mode 100644 (file)
index 0000000..9c42846
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+    Audio-related ivtv functions.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+int ivtv_audio_set_io(struct ivtv *itv);
+void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route);
+void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq);
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c
new file mode 100644 (file)
index 0000000..8eab020
--- /dev/null
@@ -0,0 +1,964 @@
+/*
+    Functions to query card hardware
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-i2c.h"
+
+#include <media/msp3400.h>
+#include <media/wm8775.h>
+#include <media/cs53l32a.h>
+#include <media/cx25840.h>
+#include <media/upd64031a.h>
+
+#define MSP_TUNER  MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
+                               MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER)
+#define MSP_SCART1 MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
+                               MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_SCART2 MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, \
+                               MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_SCART3 MSP_INPUT(MSP_IN_SCART3, MSP_IN_TUNER1, \
+                               MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_MONO   MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \
+                               MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+
+/********************** card configuration *******************************/
+
+/* Please add new PCI IDs to: http://pci-ids.ucw.cz/iii
+   This keeps the PCI ID database up to date. Note that the entries
+   must be added under vendor 0x4444 (Conexant) as subsystem IDs.
+   New vendor IDs should still be added to the vendor ID list. */
+
+/* Hauppauge PVR-250 cards */
+
+/* Note: for Hauppauge cards the tveeprom information is used instead of PCI IDs */
+static const struct ivtv_card ivtv_card_pvr250 = {
+       .type = IVTV_CARD_PVR_250,
+       .name = "Hauppauge WinTV PVR-250",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA7115,
+       .hw_audio = IVTV_HW_MSP34XX,
+       .hw_audio_ctrl = IVTV_HW_MSP34XX,
+       .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+                 IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+               { IVTV_CARD_INPUT_SVIDEO2,    2, IVTV_SAA71XX_SVIDEO1    },
+               { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+               { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  MSP_TUNER  },
+               { IVTV_CARD_INPUT_LINE_IN1,   MSP_SCART1 },
+               { IVTV_CARD_INPUT_LINE_IN2,   MSP_SCART3 },
+       },
+       .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Hauppauge PVR-350 cards */
+
+/* Outputs for Hauppauge PVR350 cards */
+static struct ivtv_card_output ivtv_pvr350_outputs[] = {
+       {
+               .name = "S-Video + Composite",
+               .video_output = 0,
+       }, {
+               .name = "Composite",
+               .video_output = 1,
+       }, {
+               .name = "S-Video",
+               .video_output = 2,
+       }, {
+               .name = "RGB",
+               .video_output = 3,
+       }, {
+               .name = "YUV C",
+               .video_output = 4,
+       }, {
+               .name = "YUV V",
+               .video_output = 5,
+       }
+};
+
+static const struct ivtv_card ivtv_card_pvr350 = {
+       .type = IVTV_CARD_PVR_350,
+       .name = "Hauppauge WinTV PVR-350",
+       .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER,
+       .video_outputs = ivtv_pvr350_outputs,
+       .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs),
+       .hw_video = IVTV_HW_SAA7115,
+       .hw_audio = IVTV_HW_MSP34XX,
+       .hw_audio_ctrl = IVTV_HW_MSP34XX,
+       .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+                 IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+               { IVTV_CARD_INPUT_SVIDEO2,    2, IVTV_SAA71XX_SVIDEO1    },
+               { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+               { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  MSP_TUNER  },
+               { IVTV_CARD_INPUT_LINE_IN1,   MSP_SCART1 },
+               { IVTV_CARD_INPUT_LINE_IN2,   MSP_SCART3 },
+       },
+       .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+};
+
+/* PVR-350 V1 boards have a different audio tuner input and use a
+   saa7114 instead of a saa7115.
+   Note that the info below comes from a pre-production model so it may
+   not be correct. Especially the audio behaves strangely (mono only it seems) */
+static const struct ivtv_card ivtv_card_pvr350_v1 = {
+       .type = IVTV_CARD_PVR_350_V1,
+       .name = "Hauppauge WinTV PVR-350 (V1)",
+       .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER,
+       .video_outputs = ivtv_pvr350_outputs,
+       .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs),
+       .hw_video = IVTV_HW_SAA7114,
+       .hw_audio = IVTV_HW_MSP34XX,
+       .hw_audio_ctrl = IVTV_HW_MSP34XX,
+       .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7114 |
+                 IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+               { IVTV_CARD_INPUT_SVIDEO2,    2, IVTV_SAA71XX_SVIDEO1    },
+               { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+               { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  MSP_MONO   },
+               { IVTV_CARD_INPUT_LINE_IN1,   MSP_SCART1 },
+               { IVTV_CARD_INPUT_LINE_IN2,   MSP_SCART3 },
+       },
+       .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Hauppauge PVR-150/PVR-500 cards */
+
+static const struct ivtv_card ivtv_card_pvr150 = {
+       .type = IVTV_CARD_PVR_150,
+       .name = "Hauppauge WinTV PVR-150",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_CX25840,
+       .hw_audio = IVTV_HW_CX25840,
+       .hw_audio_ctrl = IVTV_HW_CX25840,
+       .hw_muxer = IVTV_HW_WM8775,
+       .hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 |
+                 IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE7 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, CX25840_SVIDEO1    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 },
+               { IVTV_CARD_INPUT_SVIDEO2,    2, CX25840_SVIDEO2    },
+               { IVTV_CARD_INPUT_COMPOSITE2, 2, CX25840_COMPOSITE4 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,
+                 CX25840_AUDIO8, WM8775_AIN2 },
+               { IVTV_CARD_INPUT_LINE_IN1,
+                 CX25840_AUDIO_SERIAL, WM8775_AIN2 },
+               { IVTV_CARD_INPUT_LINE_IN2,
+                 CX25840_AUDIO_SERIAL, WM8775_AIN3 },
+       },
+       .radio_input = { IVTV_CARD_INPUT_AUD_TUNER,
+                        CX25840_AUDIO_SERIAL, WM8775_AIN4 },
+       /* apparently needed for the IR blaster */
+       .gpio_init = { .direction = 0x1f01, .initial_value = 0x26f3 },
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia M179 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_m179[] = {
+       { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3cf },
+       { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3ce },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_m179 = {
+       .type = IVTV_CARD_M179,
+       .name = "AVerMedia M179",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA7114,
+       .hw_audio = IVTV_HW_GPIO,
+       .hw_audio_ctrl = IVTV_HW_GPIO,
+       .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+               { IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+       },
+       .gpio_init = { .direction = 0xe380, .initial_value = 0x8290 },
+       .gpio_audio_input  = { .mask = 0x8040, .tuner  = 0x8000, .linein = 0x0000 },
+       .gpio_audio_mute   = { .mask = 0x2000, .mute   = 0x2000 },
+       .gpio_audio_mode   = { .mask = 0x4300, .mono   = 0x4000, .stereo = 0x0200,
+                             .lang1 = 0x0200, .lang2  = 0x0100, .both   = 0x0000 },
+       .gpio_audio_freq   = { .mask = 0x0018, .f32000 = 0x0000,
+                            .f44100 = 0x0008, .f48000 = 0x0010 },
+       .gpio_audio_detect = { .mask = 0x4000, .stereo = 0x0000 },
+       .tuners = {
+               /* As far as we know all M179 cards use this tuner */
+               { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_NTSC },
+       },
+       .pci_list = ivtv_pci_m179,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG600/Kuroutoshikou ITVC16-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_mpg600[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xfff3 },
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xffff },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_mpg600 = {
+       .type = IVTV_CARD_MPG600,
+       .name = "Yuan MPG600, Kuroutoshikou ITVC16-STVLP",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA7115,
+       .hw_audio = IVTV_HW_GPIO,
+       .hw_audio_ctrl = IVTV_HW_GPIO,
+       .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+               { IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+       },
+       .gpio_init = { .direction = 0x3080, .initial_value = 0x0004 },
+       .gpio_audio_input  = { .mask = 0x3000, .tuner  = 0x0000, .linein = 0x2000 },
+       .gpio_audio_mute   = { .mask = 0x0001, .mute   = 0x0001 },
+       .gpio_audio_mode   = { .mask = 0x000e, .mono   = 0x0006, .stereo = 0x0004,
+                             .lang1 = 0x0004, .lang2  = 0x0000, .both   = 0x0008 },
+       .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
+       .tuners = {
+               /* The PAL tuner is confirmed */
+               { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+               { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+       },
+       .pci_list = ivtv_pci_mpg600,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG160/Kuroutoshikou ITVC15-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_mpg160[] = {
+       { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_YUAN1, 0 },
+       { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_IODATA, 0x40a0 },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_mpg160 = {
+       .type = IVTV_CARD_MPG160,
+       .name = "YUAN MPG160, Kuroutoshikou ITVC15-STVLP, I/O Data GV-M2TV/PCI",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA7114,
+       .hw_audio = IVTV_HW_GPIO,
+       .hw_audio_ctrl = IVTV_HW_GPIO,
+       .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+               { IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+       },
+       .gpio_init = { .direction = 0x7080, .initial_value = 0x400c },
+       .gpio_audio_input  = { .mask = 0x3000, .tuner  = 0x0000, .linein = 0x2000 },
+       .gpio_audio_mute   = { .mask = 0x0001, .mute   = 0x0001 },
+       .gpio_audio_mode   = { .mask = 0x000e, .mono   = 0x0006, .stereo = 0x0004,
+                             .lang1 = 0x0004, .lang2  = 0x0000, .both   = 0x0008 },
+       .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
+       .tuners = {
+               { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+               { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+       },
+       .pci_list = ivtv_pci_mpg160,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan PG600/Diamond PVR-550 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_pg600[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_DIAMONDMM, 0x0070 },
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3,     0x0600 },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_pg600 = {
+       .type = IVTV_CARD_PG600,
+       .name = "Yuan PG600, Diamond PVR-550",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_CX25840,
+       .hw_audio = IVTV_HW_CX25840,
+       .hw_audio_ctrl = IVTV_HW_CX25840,
+       .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1,
+                 CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
+               { IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
+       },
+       .tuners = {
+               { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+               { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+       },
+       .pci_list = ivtv_pci_pg600,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Adaptec VideOh! AVC-2410 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_avc2410[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0093 },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_avc2410 = {
+       .type = IVTV_CARD_AVC2410,
+       .name = "Adaptec VideOh! AVC-2410",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA7115,
+       .hw_audio = IVTV_HW_MSP34XX,
+       .hw_audio_ctrl = IVTV_HW_MSP34XX,
+       .hw_muxer = IVTV_HW_CS53L32A,
+       .hw_all = IVTV_HW_MSP34XX | IVTV_HW_CS53L32A |
+                 IVTV_HW_SAA7115 | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,
+                 MSP_TUNER, CS53L32A_IN0 },
+               { IVTV_CARD_INPUT_LINE_IN1,
+                 MSP_SCART1, CS53L32A_IN2 },
+       },
+       /* This card has no eeprom and in fact the Windows driver relies
+          on the country/region setting of the user to decide which tuner
+          is available. */
+       .tuners = {
+               /* This tuner has been verified for the AVC2410 */
+               { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+               /* This is a good guess, but I'm not totally sure this is
+                  the correct tuner for NTSC. */
+               { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+       },
+       .pci_list = ivtv_pci_avc2410,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Adaptec VideOh! AVC-2010 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_avc2010[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0092 },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_avc2010 = {
+       .type = IVTV_CARD_AVC2010,
+       .name = "Adaptec VideOh! AVC-2010",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA7115,
+       .hw_audio = IVTV_HW_CS53L32A,
+       .hw_audio_ctrl = IVTV_HW_CS53L32A,
+       .hw_all = IVTV_HW_CS53L32A | IVTV_HW_SAA7115,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_SVIDEO1,    0, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 0, IVTV_SAA71XX_COMPOSITE3 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_LINE_IN1,   CS53L32A_IN2 },
+       },
+       /* Does not have a tuner */
+       .pci_list = ivtv_pci_avc2010,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Nagase Transgear 5000TV card */
+
+static const struct ivtv_card_pci_info ivtv_pci_tg5000tv[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_tg5000tv = {
+       .type = IVTV_CARD_TG5000TV,
+       .name = "Nagase Transgear 5000TV",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA7114 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X |
+       IVTV_HW_GPIO,
+       .hw_audio = IVTV_HW_GPIO,
+       .hw_audio_ctrl = IVTV_HW_GPIO,
+       .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER |
+                 IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_SVIDEO0 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO2 },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+               { IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+       },
+       .gr_config = UPD64031A_VERTICAL_EXTERNAL,
+       .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 },
+       .gpio_audio_input  = { .mask = 0x8080, .tuner  = 0x8000, .linein = 0x0080 },
+       .gpio_audio_mute   = { .mask = 0x6000, .mute   = 0x6000 },
+       .gpio_audio_mode   = { .mask = 0x4300, .mono   = 0x4000, .stereo = 0x0200,
+                             .lang1 = 0x0300, .lang2  = 0x0000, .both   = 0x0200 },
+       .gpio_video_input  = { .mask = 0x0030, .tuner  = 0x0000,
+                         .composite = 0x0010, .svideo = 0x0020 },
+       .tuners = {
+               { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+       },
+       .pci_list = ivtv_pci_tg5000tv,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AOpen VA2000MAX-SNT6 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_va2000[] = {
+       { PCI_DEVICE_ID_IVTV16, 0, 0xff5f },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_va2000 = {
+       .type = IVTV_CARD_VA2000MAX_SNT6,
+       .name = "AOpen VA2000MAX-SNT6",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD6408X,
+       .hw_audio = IVTV_HW_MSP34XX,
+       .hw_audio_ctrl = IVTV_HW_MSP34XX,
+       .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+                 IVTV_HW_UPD6408X | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
+       },
+       .tuners = {
+               { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+       },
+       .pci_list = ivtv_pci_va2000,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG600GR/Kuroutoshikou CX23416GYC-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_cx23416gyc[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 },
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN4, 0x0600 },
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x0523 },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc = {
+       .type = IVTV_CARD_CX23416GYC,
+       .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO |
+               IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+       .hw_audio = IVTV_HW_SAA717X,
+       .hw_audio_ctrl = IVTV_HW_SAA717X,
+       .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER |
+                 IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_SVIDEO3 |
+                                                IVTV_SAA717X_TUNER_FLAG },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0 },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO3 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  IVTV_SAA717X_IN2 },
+               { IVTV_CARD_INPUT_LINE_IN1,   IVTV_SAA717X_IN0 },
+       },
+       .gr_config = UPD64031A_VERTICAL_EXTERNAL,
+       .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+       .gpio_video_input  = { .mask = 0x0020, .tuner  = 0x0000,
+                              .composite = 0x0020, .svideo = 0x0020 },
+       .gpio_audio_freq   = { .mask = 0xc000, .f32000 = 0x0000,
+                            .f44100 = 0x4000, .f48000 = 0x8000 },
+       .tuners = {
+               { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+               { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+       },
+       .pci_list = ivtv_pci_cx23416gyc,
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc_nogr = {
+       .type = IVTV_CARD_CX23416GYC_NOGR,
+       .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR)",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | IVTV_HW_UPD6408X,
+       .hw_audio = IVTV_HW_SAA717X,
+       .hw_audio_ctrl = IVTV_HW_SAA717X,
+       .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER |
+                 IVTV_HW_UPD6408X,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 |
+                                                IVTV_SAA717X_TUNER_FLAG },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  IVTV_SAA717X_IN2 },
+               { IVTV_CARD_INPUT_LINE_IN1,   IVTV_SAA717X_IN0 },
+       },
+       .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+       .gpio_video_input  = { .mask = 0x0020, .tuner  = 0x0000,
+                              .composite = 0x0020, .svideo = 0x0020 },
+       .gpio_audio_freq   = { .mask = 0xc000, .f32000 = 0x0000,
+                            .f44100 = 0x4000, .f48000 = 0x8000 },
+       .tuners = {
+               { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+               { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+       },
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = {
+       .type = IVTV_CARD_CX23416GYC_NOGRYCS,
+       .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR/YCS)",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO,
+       .hw_audio = IVTV_HW_SAA717X,
+       .hw_audio_ctrl = IVTV_HW_SAA717X,
+       .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 |
+                                                IVTV_SAA717X_TUNER_FLAG },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  IVTV_SAA717X_IN2 },
+               { IVTV_CARD_INPUT_LINE_IN1,   IVTV_SAA717X_IN0 },
+       },
+       .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+       .gpio_video_input  = { .mask = 0x0020, .tuner  = 0x0000,
+                              .composite = 0x0020, .svideo = 0x0020 },
+       .gpio_audio_freq   = { .mask = 0xc000, .f32000 = 0x0000,
+                            .f44100 = 0x4000, .f48000 = 0x8000 },
+       .tuners = {
+               { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+               { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+       },
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* I/O Data GV-MVP/RX & GV-MVP/RX2W (dual tuner) cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd01e },
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd038 }, /* 2W unit #1 */
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd039 }, /* 2W unit #2 */
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gv_mvprx = {
+       .type = IVTV_CARD_GV_MVPRX,
+       .name = "I/O Data GV-MVP/RX, GV-MVP/RX2W (dual tuner)",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+       .hw_audio = IVTV_HW_GPIO,
+       .hw_audio_ctrl = IVTV_HW_WM8739,
+       .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TVAUDIO |
+                 IVTV_HW_TUNER | IVTV_HW_WM8739 |
+                 IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO1    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2    },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+               { IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+       },
+       .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 },
+       .gpio_audio_input  = { .mask = 0xffff, .tuner  = 0x0200, .linein = 0x0300 },
+       .tuners = {
+               /* This card has the Panasonic VP27 tuner */
+               { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 },
+       },
+       .pci_list = ivtv_pci_gv_mvprx,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* I/O Data GV-MVP/RX2E card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx2e[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd025 },
+       {0, 0, 0}
+};
+
+static const struct ivtv_card ivtv_card_gv_mvprx2e = {
+       .type = IVTV_CARD_GV_MVPRX2E,
+       .name = "I/O Data GV-MVP/RX2E",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA7115,
+       .hw_audio = IVTV_HW_GPIO,
+       .hw_audio_ctrl = IVTV_HW_WM8739,
+       .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER |
+                 IVTV_HW_TVAUDIO | IVTV_HW_WM8739,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+               { IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+       },
+       .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 },
+       .gpio_audio_input  = { .mask = 0xffff, .tuner  = 0x0200, .linein = 0x0300 },
+       .tuners = {
+               /* This card has the Panasonic VP27 tuner */
+               { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 },
+       },
+       .pci_list = ivtv_pci_gv_mvprx2e,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* GotVIEW PCI DVD card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gotview_pci_dvd = {
+       .type = IVTV_CARD_GOTVIEW_PCI_DVD,
+       .name = "GotView PCI DVD",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA717X,
+       .hw_audio = IVTV_HW_SAA717X,
+       .hw_audio_ctrl = IVTV_HW_SAA717X,
+       .hw_all = IVTV_HW_SAA717X | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE1 },  /* pin 116 */
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0 },     /* pin 114/109 */
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },  /* pin 118 */
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  IVTV_SAA717X_IN0 },
+               { IVTV_CARD_INPUT_LINE_IN1,   IVTV_SAA717X_IN2 },
+       },
+       .gpio_init = { .direction = 0xf000, .initial_value = 0xA000 },
+       .tuners = {
+               /* This card has a Philips FQ1216ME MK3 tuner */
+               { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+       },
+       .pci_list = ivtv_pci_gotview_pci_dvd,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* GotVIEW PCI DVD2 Deluxe card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd2[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW1, 0x0600 },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = {
+       .type = IVTV_CARD_GOTVIEW_PCI_DVD2,
+       .name = "GotView PCI DVD2 Deluxe",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_CX25840,
+       .hw_audio = IVTV_HW_CX25840,
+       .hw_audio_ctrl = IVTV_HW_CX25840,
+       .hw_muxer = IVTV_HW_GPIO,
+       .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1,
+                 CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5,       0 },
+               { IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL, 1 },
+       },
+       .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+       .gpio_init = { .direction = 0x0800, .initial_value = 0 },
+       .gpio_audio_input  = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 },
+       .tuners = {
+               /* This card has a Philips FQ1216ME MK5 tuner */
+               { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+       },
+       .pci_list = ivtv_pci_gotview_pci_dvd2,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPC622 miniPCI card */
+
+static const struct ivtv_card_pci_info ivtv_pci_yuan_mpc622[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN2, 0xd998 },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_yuan_mpc622 = {
+       .type = IVTV_CARD_YUAN_MPC622,
+       .name = "Yuan MPC622",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_CX25840,
+       .hw_audio = IVTV_HW_CX25840,
+       .hw_audio_ctrl = IVTV_HW_CX25840,
+       .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1,
+                 CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
+               { IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
+       },
+       .gpio_init = { .direction = 0x00ff, .initial_value = 0x0002 },
+       .tuners = {
+               /* This card has the TDA8290/TDA8275 tuner chips */
+               { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_TDA8290 },
+       },
+       .pci_list = ivtv_pci_yuan_mpc622,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* DIGITAL COWBOY DCT-MTVP1 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_dctmvtvp1[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_dctmvtvp1 = {
+       .type = IVTV_CARD_DCTMTVP1,
+       .name = "Digital Cowboy DCT-MTVP1",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X |
+               IVTV_HW_GPIO,
+       .hw_audio = IVTV_HW_GPIO,
+       .hw_audio_ctrl = IVTV_HW_GPIO,
+       .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER |
+               IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_SVIDEO0    },
+               { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO2    },
+               { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  IVTV_GPIO_TUNER   },
+               { IVTV_CARD_INPUT_LINE_IN1,   IVTV_GPIO_LINE_IN },
+       },
+       .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 },
+       .gpio_audio_input  = { .mask = 0x8080, .tuner  = 0x8000, .linein = 0x0080 },
+       .gpio_audio_mute   = { .mask = 0x6000, .mute   = 0x6000 },
+       .gpio_audio_mode   = { .mask = 0x4300, .mono   = 0x4000, .stereo = 0x0200,
+                             .lang1 = 0x0300, .lang2  = 0x0000, .both   = 0x0200 },
+       .gpio_video_input  = { .mask = 0x0030, .tuner  = 0x0000,
+                              .composite = 0x0010, .svideo = 0x0020},
+       .tuners = {
+               { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+       },
+       .pci_list = ivtv_pci_dctmvtvp1,
+};
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef HAVE_XC3028
+
+/* Yuan PG600-2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = {
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3,     0x0600 },
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW2,  0x0600 },
+       { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_pg600v2 = {
+       .type = IVTV_CARD_PG600V2,
+       .name = "Yuan PG600-2, GotView PCI DVD Lite, Club3D ZAP-TV1x01",
+       .v4l2_capabilities = IVTV_CAP_ENCODER,
+       .hw_video = IVTV_HW_CX25840,
+       .hw_audio = IVTV_HW_CX25840,
+       .hw_audio_ctrl = IVTV_HW_CX25840,
+       .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+       .video_inputs = {
+               { IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+               { IVTV_CARD_INPUT_SVIDEO1,    1,
+                 CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+       },
+       .audio_inputs = {
+               { IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
+               { IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
+       },
+       .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+       .tuners = {
+               { .std = V4L2_STD_ALL, .tuner = TUNER_XCEIVE_XC3028 },
+       },
+       .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */
+       .pci_list = ivtv_pci_pg600v2,
+};
+#endif
+
+static const struct ivtv_card *ivtv_card_list[] = {
+       &ivtv_card_pvr250,
+       &ivtv_card_pvr350,
+       &ivtv_card_pvr150,
+       &ivtv_card_m179,
+       &ivtv_card_mpg600,
+       &ivtv_card_mpg160,
+       &ivtv_card_pg600,
+       &ivtv_card_avc2410,
+       &ivtv_card_avc2010,
+       &ivtv_card_tg5000tv,
+       &ivtv_card_va2000,
+       &ivtv_card_cx23416gyc,
+       &ivtv_card_gv_mvprx,
+       &ivtv_card_gv_mvprx2e,
+       &ivtv_card_gotview_pci_dvd,
+       &ivtv_card_gotview_pci_dvd2,
+       &ivtv_card_yuan_mpc622,
+       &ivtv_card_dctmvtvp1,
+#ifdef HAVE_XC3028
+       &ivtv_card_pg600v2,
+#endif
+
+       /* Variations of standard cards but with the same PCI IDs.
+          These cards must come last in this list. */
+       &ivtv_card_pvr350_v1,
+       &ivtv_card_cx23416gyc_nogr,
+       &ivtv_card_cx23416gyc_nogrycs,
+};
+
+const struct ivtv_card *ivtv_get_card(u16 index)
+{
+       if (index >= ARRAY_SIZE(ivtv_card_list))
+               return NULL;
+       return ivtv_card_list[index];
+}
+
+int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input)
+{
+       const struct ivtv_card_video_input *card_input = itv->card->video_inputs + index;
+       static const char * const input_strs[] = {
+               "Tuner 1",
+               "S-Video 1",
+               "S-Video 2",
+               "Composite 1",
+               "Composite 2",
+               "Composite 3"
+       };
+
+       memset(input, 0, sizeof(*input));
+       if (index >= itv->nof_inputs)
+               return -EINVAL;
+       input->index = index;
+       strcpy(input->name, input_strs[card_input->video_type - 1]);
+       input->type = (card_input->video_type == IVTV_CARD_INPUT_VID_TUNER ?
+                       V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
+       input->audioset = (1 << itv->nof_audio_inputs) - 1;
+       input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ?
+                               itv->tuner_std : V4L2_STD_ALL;
+       return 0;
+}
+
+int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output)
+{
+       const struct ivtv_card_output *card_output = itv->card->video_outputs + index;
+
+       memset(output, 0, sizeof(*output));
+       if (index >= itv->card->nof_outputs)
+               return -EINVAL;
+       output->index = index;
+       strcpy(output->name, card_output->name);
+       output->type = V4L2_OUTPUT_TYPE_ANALOG;
+       output->audioset = 1;
+       output->std = V4L2_STD_ALL;
+       return 0;
+}
+
+int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *audio)
+{
+       const struct ivtv_card_audio_input *aud_input = itv->card->audio_inputs + index;
+       static const char * const input_strs[] = {
+               "Tuner 1",
+               "Line In 1",
+               "Line In 2"
+       };
+
+       memset(audio, 0, sizeof(*audio));
+       if (index >= itv->nof_audio_inputs)
+               return -EINVAL;
+       strcpy(audio->name, input_strs[aud_input->audio_type - 1]);
+       audio->index = index;
+       audio->capability = V4L2_AUDCAP_STEREO;
+       return 0;
+}
+
+int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *aud_output)
+{
+       memset(aud_output, 0, sizeof(*aud_output));
+       if (itv->card->video_outputs == NULL || index != 0)
+               return -EINVAL;
+       strcpy(aud_output->name, "A/V Audio Out");
+       return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h
new file mode 100644 (file)
index 0000000..15012f8
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+    Functions to query card hardware
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* hardware flags */
+#define IVTV_HW_CX25840   (1 << 0)
+#define IVTV_HW_SAA7115   (1 << 1)
+#define IVTV_HW_SAA7127   (1 << 2)
+#define IVTV_HW_MSP34XX   (1 << 3)
+#define IVTV_HW_TUNER     (1 << 4)
+#define IVTV_HW_WM8775    (1 << 5)
+#define IVTV_HW_CS53L32A  (1 << 6)
+#define IVTV_HW_TVEEPROM  (1 << 7)
+#define IVTV_HW_SAA7114   (1 << 8)
+#define IVTV_HW_TVAUDIO   (1 << 9)
+#define IVTV_HW_UPD64031A (1 << 10)
+#define IVTV_HW_UPD6408X  (1 << 11)
+#define IVTV_HW_SAA717X   (1 << 12)
+#define IVTV_HW_WM8739    (1 << 13)
+#define IVTV_HW_GPIO      (1 << 14)
+
+#define IVTV_HW_SAA711X   (IVTV_HW_SAA7115 | IVTV_HW_SAA7114)
+
+/* video inputs */
+#define        IVTV_CARD_INPUT_VID_TUNER       1
+#define        IVTV_CARD_INPUT_SVIDEO1         2
+#define        IVTV_CARD_INPUT_SVIDEO2         3
+#define        IVTV_CARD_INPUT_COMPOSITE1      4
+#define        IVTV_CARD_INPUT_COMPOSITE2      5
+#define        IVTV_CARD_INPUT_COMPOSITE3      6
+
+/* audio inputs */
+#define        IVTV_CARD_INPUT_AUD_TUNER       1
+#define        IVTV_CARD_INPUT_LINE_IN1        2
+#define        IVTV_CARD_INPUT_LINE_IN2        3
+
+#define IVTV_CARD_MAX_VIDEO_INPUTS 6
+#define IVTV_CARD_MAX_AUDIO_INPUTS 3
+#define IVTV_CARD_MAX_TUNERS      2
+
+/* SAA71XX HW inputs */
+#define IVTV_SAA71XX_COMPOSITE0 0
+#define IVTV_SAA71XX_COMPOSITE1 1
+#define IVTV_SAA71XX_COMPOSITE2 2
+#define IVTV_SAA71XX_COMPOSITE3 3
+#define IVTV_SAA71XX_COMPOSITE4 4
+#define IVTV_SAA71XX_COMPOSITE5 5
+#define IVTV_SAA71XX_SVIDEO0    6
+#define IVTV_SAA71XX_SVIDEO1    7
+#define IVTV_SAA71XX_SVIDEO2    8
+#define IVTV_SAA71XX_SVIDEO3    9
+
+/* SAA717X needs to mark the tuner input by ORing with this flag */
+#define IVTV_SAA717X_TUNER_FLAG 0x80
+
+/* Dummy HW input */
+#define IVTV_DUMMY_AUDIO        0
+
+/* GPIO HW inputs */
+#define IVTV_GPIO_TUNER   0
+#define IVTV_GPIO_LINE_IN 1
+
+/* SAA717X HW inputs */
+#define IVTV_SAA717X_IN0 0
+#define IVTV_SAA717X_IN1 1
+#define IVTV_SAA717X_IN2 2
+
+/* V4L2 capability aliases */
+#define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
+                         V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \
+                         V4L2_CAP_SLICED_VBI_CAPTURE)
+#define IVTV_CAP_DECODER (V4L2_CAP_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT | \
+                         V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_VIDEO_OUTPUT_POS)
+
+struct ivtv_card_video_input {
+       u8  video_type;         /* video input type */
+       u8  audio_index;        /* index in ivtv_card_audio_input array */
+       u16 video_input;        /* hardware video input */
+};
+
+struct ivtv_card_audio_input {
+       u8  audio_type;         /* audio input type */
+       u32 audio_input;        /* hardware audio input */
+       u16 muxer_input;        /* hardware muxer input for boards with a
+                                  multiplexer chip */
+};
+
+struct ivtv_card_output {
+       u8  name[32];
+       u16 video_output;  /* hardware video output */
+};
+
+struct ivtv_card_pci_info {
+       u16 device;
+       u16 subsystem_vendor;
+       u16 subsystem_device;
+};
+
+/* GPIO definitions */
+
+/* The mask is the set of bits used by the operation */
+
+struct ivtv_gpio_init {        /* set initial GPIO DIR and OUT values */
+       u16 direction;          /* DIR setting. Leave to 0 if no init is needed */
+       u16 initial_value;
+};
+
+struct ivtv_gpio_video_input {         /* select tuner/line in input */
+       u16 mask;               /* leave to 0 if not supported */
+       u16 tuner;
+       u16 composite;
+       u16 svideo;
+};
+
+struct ivtv_gpio_audio_input {         /* select tuner/line in input */
+       u16 mask;               /* leave to 0 if not supported */
+       u16 tuner;
+       u16 linein;
+       u16 radio;
+};
+
+struct ivtv_gpio_audio_mute {
+       u16 mask;               /* leave to 0 if not supported */
+       u16 mute;               /* set this value to mute, 0 to unmute */
+};
+
+struct ivtv_gpio_audio_mode {
+       u16 mask;               /* leave to 0 if not supported */
+       u16 mono;               /* set audio to mono */
+       u16 stereo;             /* set audio to stereo */
+       u16 lang1;              /* set audio to the first language */
+       u16 lang2;              /* set audio to the second language */
+       u16 both;               /* both languages are output */
+};
+
+struct ivtv_gpio_audio_freq {
+       u16 mask;               /* leave to 0 if not supported */
+       u16 f32000;
+       u16 f44100;
+       u16 f48000;
+};
+
+struct ivtv_gpio_audio_detect {
+       u16 mask;               /* leave to 0 if not supported */
+       u16 stereo;             /* if the input matches this value then
+                                  stereo is detected */
+};
+
+struct ivtv_card_tuner {
+       v4l2_std_id std;        /* standard for which the tuner is suitable */
+       int         tuner;      /* tuner ID (from tuner.h) */
+};
+
+/* for card information/parameters */
+struct ivtv_card {
+       int type;
+       char *name;
+       u32 v4l2_capabilities;
+       u32 hw_video;           /* hardware used to process video */
+       u32 hw_audio;           /* hardware used to process audio */
+       u32 hw_audio_ctrl;      /* hardware used for the V4L2 controls (only 1 dev allowed) */
+       u32 hw_muxer;           /* hardware used to multiplex audio input */
+       u32 hw_all;             /* all hardware used by the board */
+       struct ivtv_card_video_input video_inputs[IVTV_CARD_MAX_VIDEO_INPUTS];
+       struct ivtv_card_audio_input audio_inputs[IVTV_CARD_MAX_AUDIO_INPUTS];
+       struct ivtv_card_audio_input radio_input;
+       int nof_outputs;
+       const struct ivtv_card_output *video_outputs;
+       u8 gr_config;           /* config byte for the ghost reduction device */
+
+       /* GPIO card-specific settings */
+       struct ivtv_gpio_init           gpio_init;
+       struct ivtv_gpio_video_input    gpio_video_input;
+       struct ivtv_gpio_audio_input    gpio_audio_input;
+       struct ivtv_gpio_audio_mute     gpio_audio_mute;
+       struct ivtv_gpio_audio_mode     gpio_audio_mode;
+       struct ivtv_gpio_audio_freq     gpio_audio_freq;
+       struct ivtv_gpio_audio_detect   gpio_audio_detect;
+
+       struct ivtv_card_tuner tuners[IVTV_CARD_MAX_TUNERS];
+
+       /* list of device and subsystem vendor/devices that
+          correspond to this card type. */
+       const struct ivtv_card_pci_info *pci_list;
+};
+
+int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input);
+int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output);
+int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *input);
+int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *output);
+const struct ivtv_card *ivtv_get_card(u16 index);
diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c
new file mode 100644 (file)
index 0000000..7a876c3
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+    ioctl control functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-audio.h"
+#include "ivtv-i2c.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-controls.h"
+
+static const u32 user_ctrls[] = {
+       V4L2_CID_USER_CLASS,
+       V4L2_CID_BRIGHTNESS,
+       V4L2_CID_CONTRAST,
+       V4L2_CID_SATURATION,
+       V4L2_CID_HUE,
+       V4L2_CID_AUDIO_VOLUME,
+       V4L2_CID_AUDIO_BALANCE,
+       V4L2_CID_AUDIO_BASS,
+       V4L2_CID_AUDIO_TREBLE,
+       V4L2_CID_AUDIO_MUTE,
+       V4L2_CID_AUDIO_LOUDNESS,
+       0
+};
+
+static const u32 *ctrl_classes[] = {
+       user_ctrls,
+       cx2341x_mpeg_ctrls,
+       NULL
+};
+
+static int ivtv_queryctrl(struct ivtv *itv, struct v4l2_queryctrl *qctrl)
+{
+       const char *name;
+
+       IVTV_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id);
+
+       qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+       if (qctrl->id == 0)
+               return -EINVAL;
+
+       switch (qctrl->id) {
+       /* Standard V4L2 controls */
+       case V4L2_CID_BRIGHTNESS:
+       case V4L2_CID_HUE:
+       case V4L2_CID_SATURATION:
+       case V4L2_CID_CONTRAST:
+               if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl))
+                       qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+               return 0;
+
+       case V4L2_CID_AUDIO_VOLUME:
+       case V4L2_CID_AUDIO_MUTE:
+       case V4L2_CID_AUDIO_BALANCE:
+       case V4L2_CID_AUDIO_BASS:
+       case V4L2_CID_AUDIO_TREBLE:
+       case V4L2_CID_AUDIO_LOUDNESS:
+               if (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
+                       qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+               return 0;
+
+       default:
+               if (cx2341x_ctrl_query(&itv->params, qctrl))
+                       qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+               return 0;
+       }
+       strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
+       qctrl->name[sizeof(qctrl->name) - 1] = 0;
+       return 0;
+}
+
+static int ivtv_querymenu(struct ivtv *itv, struct v4l2_querymenu *qmenu)
+{
+       struct v4l2_queryctrl qctrl;
+
+       qctrl.id = qmenu->id;
+       ivtv_queryctrl(itv, &qctrl);
+       return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
+}
+
+static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
+{
+       s32 v = vctrl->value;
+
+       IVTV_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v);
+
+       switch (vctrl->id) {
+               /* Standard V4L2 controls */
+       case V4L2_CID_BRIGHTNESS:
+       case V4L2_CID_HUE:
+       case V4L2_CID_SATURATION:
+       case V4L2_CID_CONTRAST:
+               return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl);
+
+       case V4L2_CID_AUDIO_VOLUME:
+       case V4L2_CID_AUDIO_MUTE:
+       case V4L2_CID_AUDIO_BALANCE:
+       case V4L2_CID_AUDIO_BASS:
+       case V4L2_CID_AUDIO_TREBLE:
+       case V4L2_CID_AUDIO_LOUDNESS:
+               return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
+
+       default:
+               IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
+{
+       IVTV_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id);
+
+       switch (vctrl->id) {
+               /* Standard V4L2 controls */
+       case V4L2_CID_BRIGHTNESS:
+       case V4L2_CID_HUE:
+       case V4L2_CID_SATURATION:
+       case V4L2_CID_CONTRAST:
+               return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl);
+
+       case V4L2_CID_AUDIO_VOLUME:
+       case V4L2_CID_AUDIO_MUTE:
+       case V4L2_CID_AUDIO_BALANCE:
+       case V4L2_CID_AUDIO_BASS:
+       case V4L2_CID_AUDIO_TREBLE:
+       case V4L2_CID_AUDIO_LOUDNESS:
+               return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
+       default:
+               IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt)
+{
+       if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
+               return -EINVAL;
+       if (atomic_read(&itv->capturing) > 0)
+               return -EBUSY;
+
+       /* First try to allocate sliced VBI buffers if needed. */
+       if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) {
+               int i;
+
+               for (i = 0; i < IVTV_VBI_FRAMES; i++) {
+                       /* Yuck, hardcoded. Needs to be a define */
+                       itv->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL);
+                       if (itv->vbi.sliced_mpeg_data[i] == NULL) {
+                               while (--i >= 0) {
+                                       kfree(itv->vbi.sliced_mpeg_data[i]);
+                                       itv->vbi.sliced_mpeg_data[i] = NULL;
+                               }
+                               return -ENOMEM;
+                       }
+               }
+       }
+
+       itv->vbi.insert_mpeg = fmt;
+
+       if (itv->vbi.insert_mpeg == 0) {
+               return 0;
+       }
+       /* Need sliced data for mpeg insertion */
+       if (get_service_set(itv->vbi.sliced_in) == 0) {
+               if (itv->is_60hz)
+                       itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
+               else
+                       itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
+               expand_service_set(itv->vbi.sliced_in, itv->is_50hz);
+       }
+       return 0;
+}
+
+int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+       struct v4l2_control ctrl;
+
+       switch (cmd) {
+       case VIDIOC_QUERYMENU:
+               IVTV_DEBUG_IOCTL("VIDIOC_QUERYMENU\n");
+               return ivtv_querymenu(itv, arg);
+
+       case VIDIOC_QUERYCTRL:
+               return ivtv_queryctrl(itv, arg);
+
+       case VIDIOC_S_CTRL:
+               return ivtv_s_ctrl(itv, arg);
+
+       case VIDIOC_G_CTRL:
+               return ivtv_g_ctrl(itv, arg);
+
+       case VIDIOC_S_EXT_CTRLS:
+       {
+               struct v4l2_ext_controls *c = arg;
+
+               if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+                       int i;
+                       int err = 0;
+
+                       for (i = 0; i < c->count; i++) {
+                               ctrl.id = c->controls[i].id;
+                               ctrl.value = c->controls[i].value;
+                               err = ivtv_s_ctrl(itv, &ctrl);
+                               c->controls[i].value = ctrl.value;
+                               if (err) {
+                                       c->error_idx = i;
+                                       break;
+                               }
+                       }
+                       return err;
+               }
+               IVTV_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n");
+               if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+                       struct cx2341x_mpeg_params p = itv->params;
+                       int err = cx2341x_ext_ctrls(&p, arg, cmd);
+
+                       if (err)
+                               return err;
+
+                       if (p.video_encoding != itv->params.video_encoding) {
+                               int is_mpeg1 = p.video_encoding ==
+                                               V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+                               struct v4l2_format fmt;
+
+                               /* fix videodecoder resolution */
+                               fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+                               fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1);
+                               fmt.fmt.pix.height = itv->params.height;
+                               itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt);
+                       }
+                       err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
+                       if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) {
+                               err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt);
+                       }
+                       itv->params = p;
+                       itv->dualwatch_stereo_mode = p.audio_properties & 0x0300;
+                       ivtv_audio_set_audio_clock_freq(itv, p.audio_properties & 0x03);
+                       return err;
+               }
+               return -EINVAL;
+       }
+
+       case VIDIOC_G_EXT_CTRLS:
+       {
+               struct v4l2_ext_controls *c = arg;
+
+               if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+                       int i;
+                       int err = 0;
+
+                       for (i = 0; i < c->count; i++) {
+                               ctrl.id = c->controls[i].id;
+                               ctrl.value = c->controls[i].value;
+                               err = ivtv_g_ctrl(itv, &ctrl);
+                               c->controls[i].value = ctrl.value;
+                               if (err) {
+                                       c->error_idx = i;
+                                       break;
+                               }
+                       }
+                       return err;
+               }
+               IVTV_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n");
+               if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+                       return cx2341x_ext_ctrls(&itv->params, arg, cmd);
+               return -EINVAL;
+       }
+
+       case VIDIOC_TRY_EXT_CTRLS:
+       {
+               struct v4l2_ext_controls *c = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n");
+               if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+                       return cx2341x_ext_ctrls(&itv->params, arg, cmd);
+               return -EINVAL;
+       }
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h
new file mode 100644 (file)
index 0000000..5a11149
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+    ioctl control functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg);
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
new file mode 100644 (file)
index 0000000..8d38765
--- /dev/null
@@ -0,0 +1,1385 @@
+/*
+    ivtv driver initialization and card probing
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Main Driver file for the ivtv project:
+ * Driver for the Conexant CX23415/CX23416 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.ivtvdriver.org
+ *
+ * -----
+ * MPG600/MPG160 support by  T.Adachi <tadachi@tadachi-net.com>
+ *                      and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ *                using information provided by Jiun-Kuei Jung @ AVerMedia.
+ *
+ * Kurouto Sikou CX23416GYC-STVLP tested by K.Ohta <alpha292@bremen.or.jp>
+ *                using information from T.Adachi,Takeru KOMORIYA and others :-)
+ *
+ * Nagase TRANSGEAR 5000TV, Aopen VA2000MAX-STN6 and I/O data GV-MVP/RX
+ *                version by T.Adachi. Special thanks  Mr.Suzuki
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-firmware.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-streams.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-cards.h"
+#include "ivtv-vbi.h"
+#include "ivtv-audio.h"
+#include "ivtv-gpio.h"
+#include "ivtv-yuv.h"
+
+#include <linux/vermagic.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+
+/* var to keep track of the number of array elements in use */
+int ivtv_cards_active = 0;
+
+/* If you have already X v4l cards, then set this to X. This way
+   the device numbers stay matched. Example: you have a WinTV card
+   without radio and a PVR-350 with. Normally this would give a
+   video1 device together with a radio0 device for the PVR. By
+   setting this to 1 you ensure that radio0 is now also radio1. */
+int ivtv_first_minor = 0;
+
+/* Master variable for all ivtv info */
+struct ivtv *ivtv_cards[IVTV_MAX_CARDS];
+
+/* Protects ivtv_cards_active */
+spinlock_t ivtv_cards_lock = SPIN_LOCK_UNLOCKED;
+
+/* add your revision and whatnot here */
+static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
+       {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0,}
+};
+
+MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl);
+
+const u32 yuv_offset[4] = {
+       IVTV_YUV_BUFFER_OFFSET,
+       IVTV_YUV_BUFFER_OFFSET_1,
+       IVTV_YUV_BUFFER_OFFSET_2,
+       IVTV_YUV_BUFFER_OFFSET_3
+};
+
+/* Parameter declarations */
+static int cardtype[IVTV_MAX_CARDS];
+static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+
+static int cardtype_c = 1;
+static int tuner_c = 1;
+static int radio_c = 1;
+static char pal[] = "--";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+/* Buffers */
+static int enc_mpg_buffers = IVTV_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_yuv_buffers = IVTV_DEFAULT_ENC_YUV_BUFFERS;
+static int enc_vbi_buffers = IVTV_DEFAULT_ENC_VBI_BUFFERS;
+static int enc_pcm_buffers = IVTV_DEFAULT_ENC_PCM_BUFFERS;
+static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS;
+static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS;
+static int dec_vbi_buffers = IVTV_DEFAULT_DEC_VBI_BUFFERS;
+
+static int ivtv_yuv_mode = 0;
+static int ivtv_yuv_threshold=480;
+static int ivtv_pci_latency = 1;
+
+int ivtv_debug = 0;
+
+int newi2c = -1;
+
+module_param_array(tuner, int, &tuner_c, 0644);
+module_param_array(radio, bool, &radio_c, 0644);
+module_param_array(cardtype, int, &cardtype_c, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+module_param_named(debug,ivtv_debug, int, 0644);
+module_param(ivtv_pci_latency, int, 0644);
+module_param(ivtv_yuv_mode, int, 0644);
+module_param(ivtv_yuv_threshold, int, 0644);
+module_param(ivtv_first_minor, int, 0644);
+
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_yuv_buffers, int, 0644);
+module_param(enc_vbi_buffers, int, 0644);
+module_param(enc_pcm_buffers, int, 0644);
+module_param(dec_mpg_buffers, int, 0644);
+module_param(dec_yuv_buffers, int, 0644);
+module_param(dec_vbi_buffers, int, 0644);
+
+module_param(newi2c, int, 0644);
+
+MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
+                       "\t\t\tsee tuner.h for values");
+MODULE_PARM_DESC(radio,
+                "Enable or disable the radio. Use only if autodetection\n"
+                "\t\t\tfails. 0 = disable, 1 = enable");
+MODULE_PARM_DESC(cardtype,
+                "Only use this option if your card is not detected properly.\n"
+                "\t\tSpecify card type:\n"
+                "\t\t\t 1 = WinTV PVR 250\n"
+                "\t\t\t 2 = WinTV PVR 350\n"
+                "\t\t\t 3 = WinTV PVR-150 or PVR-500\n"
+                "\t\t\t 4 = AVerMedia M179\n"
+                "\t\t\t 5 = YUAN MPG600/Kuroutoshikou iTVC16-STVLP\n"
+                "\t\t\t 6 = YUAN MPG160/Kuroutoshikou iTVC15-STVLP\n"
+                "\t\t\t 7 = YUAN PG600/DIAMONDMM PVR-550 (CX Falcon 2)\n"
+                "\t\t\t 8 = Adaptec AVC-2410\n"
+                "\t\t\t 9 = Adaptec AVC-2010\n"
+                "\t\t\t10 = NAGASE TRANSGEAR 5000TV\n"
+                "\t\t\t11 = AOpen VA2000MAX-STN6\n"
+                "\t\t\t12 = YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP\n"
+                "\t\t\t13 = I/O Data GV-MVP/RX\n"
+                "\t\t\t14 = I/O Data GV-MVP/RX2E\n"
+                "\t\t\t15 = GOTVIEW PCI DVD\n"
+                "\t\t\t16 = GOTVIEW PCI DVD2 Deluxe\n"
+                "\t\t\t17 = Yuan MPC622\n"
+                "\t\t\t18 = Digital Cowboy DCT-MTVP1\n"
+#ifdef HAVE_XC3028
+                "\t\t\t19 = Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01\n"
+#endif
+                "\t\t\t 0 = Autodetect (default)\n"
+                "\t\t\t-1 = Ignore this card\n\t\t");
+MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
+MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC");
+MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K");
+MODULE_PARM_DESC(debug,
+                "Debug level (bitmask). Default: errors only\n"
+                "\t\t\t(debug = 511 gives full debugging)");
+MODULE_PARM_DESC(ivtv_pci_latency,
+                "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+                "\t\t\tDefault: Yes");
+MODULE_PARM_DESC(ivtv_yuv_mode,
+                "Specify the yuv playback mode:\n"
+                "\t\t\t0 = interlaced\n\t\t\t1 = progressive\n\t\t\t2 = auto\n"
+                "\t\t\tDefault: 0 (interlaced)");
+MODULE_PARM_DESC(ivtv_yuv_threshold,
+                "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n"
+                "\t\t\tDefault: 480");;
+MODULE_PARM_DESC(enc_mpg_buffers,
+                "Encoder MPG Buffers (in MB)\n"
+                "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_buffers,
+                "Encoder YUV Buffers (in MB)\n"
+                "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_buffers,
+                "Encoder VBI Buffers (in MB)\n"
+                "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_buffers,
+                "Encoder PCM buffers (in MB)\n"
+                "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(dec_mpg_buffers,
+                "Decoder MPG buffers (in MB)\n"
+                "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_MPG_BUFFERS));
+MODULE_PARM_DESC(dec_yuv_buffers,
+                "Decoder YUV buffers (in MB)\n"
+                "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_YUV_BUFFERS));
+MODULE_PARM_DESC(dec_vbi_buffers,
+                "Decoder VBI buffers (in MB)\n"
+                "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_VBI_BUFFERS));
+MODULE_PARM_DESC(newi2c,
+                "Use new I2C implementation\n"
+                "\t\t\t-1 is autodetect, 0 is off, 1 is on\n"
+                "\t\t\tDefault is autodetect");
+
+MODULE_PARM_DESC(ivtv_first_minor, "Set minor assigned to first card");
+
+MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
+MODULE_DESCRIPTION("CX23415/CX23416 driver");
+MODULE_SUPPORTED_DEVICE
+    ("CX23415/CX23416 MPEG2 encoder (WinTV PVR-150/250/350/500,\n"
+               "\t\t\tYuan MPG series and similar)");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(IVTV_VERSION);
+
+void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask)
+{
+       itv->irqmask &= ~mask;
+       write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
+}
+
+void ivtv_set_irq_mask(struct ivtv *itv, u32 mask)
+{
+       itv->irqmask |= mask;
+       write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
+}
+
+int ivtv_set_output_mode(struct ivtv *itv, int mode)
+{
+    int old_mode;
+
+    spin_lock(&itv->lock);
+    old_mode = itv->output_mode;
+    if (old_mode == 0)
+       itv->output_mode = old_mode = mode;
+    spin_unlock(&itv->lock);
+    return old_mode;
+}
+
+struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv)
+{
+       switch (itv->output_mode) {
+       case OUT_MPG:
+               return &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+       case OUT_YUV:
+               return &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+       default:
+               return NULL;
+       }
+}
+
+int ivtv_waitq(wait_queue_head_t *waitq)
+{
+       DEFINE_WAIT(wait);
+
+       prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE);
+       schedule();
+       finish_wait(waitq, &wait);
+       return signal_pending(current) ? -EINTR : 0;
+}
+
+/* Generic utility functions */
+int ivtv_sleep_timeout(int timeout, int intr)
+{
+       int ret;
+
+       do {
+               set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+               timeout = schedule_timeout(timeout);
+               if (intr && (ret = signal_pending(current)))
+                       return ret;
+       } while (timeout);
+       return 0;
+}
+
+/* Release ioremapped memory */
+static void ivtv_iounmap(struct ivtv *itv)
+{
+       if (itv == NULL)
+               return;
+
+       /* Release registers memory */
+       if (itv->reg_mem != NULL) {
+               IVTV_DEBUG_INFO("releasing reg_mem\n");
+               iounmap(itv->reg_mem);
+               itv->reg_mem = NULL;
+       }
+       /* Release io memory */
+       if (itv->has_cx23415 && itv->dec_mem != NULL) {
+               IVTV_DEBUG_INFO("releasing dec_mem\n");
+               iounmap(itv->dec_mem);
+       }
+       itv->dec_mem = NULL;
+
+       /* Release io memory */
+       if (itv->enc_mem != NULL) {
+               IVTV_DEBUG_INFO("releasing enc_mem\n");
+               iounmap(itv->enc_mem);
+               itv->enc_mem = NULL;
+       }
+}
+
+/* Hauppauge card? get values from tveeprom */
+void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv)
+{
+       u8 eedata[256];
+
+       itv->i2c_client.addr = 0xA0 >> 1;
+       tveeprom_read(&itv->i2c_client, eedata, sizeof(eedata));
+       tveeprom_hauppauge_analog(&itv->i2c_client, tv, eedata);
+}
+
+static void ivtv_process_eeprom(struct ivtv *itv)
+{
+       struct tveeprom tv;
+       int pci_slot = PCI_SLOT(itv->dev->devfn);
+
+       ivtv_read_eeprom(itv, &tv);
+
+       /* Many thanks to Steven Toth from Hauppauge for providing the
+          model numbers */
+       switch (tv.model) {
+               /* In a few cases the PCI subsystem IDs do not correctly
+                  identify the card. A better method is to check the
+                  model number from the eeprom instead. */
+               case 32000 ... 32999:
+               case 48000 ... 48099:  /* 48??? range are PVR250s with a cx23415 */
+               case 48400 ... 48599:
+                       itv->card = ivtv_get_card(IVTV_CARD_PVR_250);
+                       break;
+               case 48100 ... 48399:
+               case 48600 ... 48999:
+                       itv->card = ivtv_get_card(IVTV_CARD_PVR_350);
+                       break;
+               case 23000 ... 23999:  /* PVR500 */
+               case 25000 ... 25999:  /* Low profile PVR150 */
+               case 26000 ... 26999:  /* Regular PVR150 */
+                       itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+                       break;
+               case 0:
+                       IVTV_ERR("Invalid EEPROM\n");
+                       return;
+               default:
+                       IVTV_ERR("Unknown model %d, defaulting to PVR-150\n", tv.model);
+                       itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+                       break;
+       }
+
+       switch (tv.model) {
+               /* Old style PVR350 (with an saa7114) uses this input for
+                  the tuner. */
+               case 48254:
+                       itv->card = ivtv_get_card(IVTV_CARD_PVR_350_V1);
+                       break;
+               default:
+                       break;
+       }
+
+       itv->v4l2_cap = itv->card->v4l2_capabilities;
+       itv->card_name = itv->card->name;
+
+       /* If this is a PVR500 then it should be possible to detect whether it is the
+          first or second unit by looking at the subsystem device ID: is bit 4 is
+          set, then it is the second unit (according to info from Hauppauge).
+
+          However, while this works for most cards, I have seen a few PVR500 cards
+          where both units have the same subsystem ID.
+
+          So instead I look at the reported 'PCI slot' (which is the slot on the PVR500
+          PCI bridge) and if it is 8, then it is assumed to be the first unit, otherwise
+          it is the second unit. It is possible that it is a different slot when ivtv is
+          used in Xen, in that case I ignore this card here. The worst that can happen
+          is that the card presents itself with a non-working radio device.
+
+          This detection is needed since the eeprom reports incorrectly that a radio is
+          present on the second unit. */
+       if (tv.model / 1000 == 23) {
+               itv->card_name = "WinTV PVR 500";
+               if (pci_slot == 8 || pci_slot == 9) {
+                       int is_first = (pci_slot & 1) == 0;
+
+                       itv->card_name = is_first ? "WinTV PVR 500 (unit #1)" :
+                                                   "WinTV PVR 500 (unit #2)";
+                       if (!is_first) {
+                               IVTV_INFO("Correcting tveeprom data: no radio present on second unit\n");
+                               tv.has_radio = 0;
+                       }
+               }
+       }
+       IVTV_INFO("Autodetected %s\n", itv->card_name);
+
+       switch (tv.tuner_hauppauge_model) {
+               case 85:
+               case 99:
+               case 112:
+                       itv->pvr150_workaround = 1;
+                       break;
+               default:
+                       break;
+       }
+       if (tv.tuner_type == TUNER_ABSENT)
+               IVTV_ERR("tveeprom cannot autodetect tuner!");
+
+       if (itv->options.tuner == -1)
+               itv->options.tuner = tv.tuner_type;
+       if (itv->options.radio == -1)
+               itv->options.radio = (tv.has_radio != 0);
+       /* only enable newi2c if an IR blaster is present */
+       /* FIXME: for 2.6.20 the test against 2 should be removed */
+       if (itv->options.newi2c == -1 && tv.has_ir != -1 && tv.has_ir != 2) {
+               itv->options.newi2c = (tv.has_ir & 2) ? 1 : 0;
+               if (itv->options.newi2c) {
+                   IVTV_INFO("reopen i2c bus for IR-blaster support\n");
+                   exit_ivtv_i2c(itv);
+                   init_ivtv_i2c(itv);
+               }
+       }
+
+       if (itv->std != 0)
+               /* user specified tuner standard */
+               return;
+
+       /* autodetect tuner standard */
+       if (tv.tuner_formats & V4L2_STD_PAL) {
+               IVTV_DEBUG_INFO("PAL tuner detected\n");
+               itv->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+       } else if (tv.tuner_formats & V4L2_STD_NTSC) {
+               IVTV_DEBUG_INFO("NTSC tuner detected\n");
+               itv->std |= V4L2_STD_NTSC_M;
+       } else if (tv.tuner_formats & V4L2_STD_SECAM) {
+               IVTV_DEBUG_INFO("SECAM tuner detected\n");
+               itv->std |= V4L2_STD_SECAM_L;
+       } else {
+               IVTV_INFO("No tuner detected, default to NTSC-M\n");
+               itv->std |= V4L2_STD_NTSC_M;
+       }
+}
+
+static v4l2_std_id ivtv_parse_std(struct ivtv *itv)
+{
+       switch (pal[0]) {
+               case '6':
+                       return V4L2_STD_PAL_60;
+               case 'b':
+               case 'B':
+               case 'g':
+               case 'G':
+                       return V4L2_STD_PAL_BG;
+               case 'h':
+               case 'H':
+                       return V4L2_STD_PAL_H;
+               case 'n':
+               case 'N':
+                       if (pal[1] == 'c' || pal[1] == 'C')
+                               return V4L2_STD_PAL_Nc;
+                       return V4L2_STD_PAL_N;
+               case 'i':
+               case 'I':
+                       return V4L2_STD_PAL_I;
+               case 'd':
+               case 'D':
+               case 'k':
+               case 'K':
+                       return V4L2_STD_PAL_DK;
+               case 'M':
+               case 'm':
+                       return V4L2_STD_PAL_M;
+               case '-':
+                       break;
+               default:
+                       IVTV_WARN("pal= argument not recognised\n");
+                       return 0;
+       }
+
+       switch (secam[0]) {
+               case 'b':
+               case 'B':
+               case 'g':
+               case 'G':
+               case 'h':
+               case 'H':
+                       return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+               case 'd':
+               case 'D':
+               case 'k':
+               case 'K':
+                       return V4L2_STD_SECAM_DK;
+               case 'l':
+               case 'L':
+                       if (secam[1] == 'C' || secam[1] == 'c')
+                               return V4L2_STD_SECAM_LC;
+                       return V4L2_STD_SECAM_L;
+               case '-':
+                       break;
+               default:
+                       IVTV_WARN("secam= argument not recognised\n");
+                       return 0;
+       }
+
+       switch (ntsc[0]) {
+               case 'm':
+               case 'M':
+                       return V4L2_STD_NTSC_M;
+               case 'j':
+               case 'J':
+                       return V4L2_STD_NTSC_M_JP;
+               case 'k':
+               case 'K':
+                       return V4L2_STD_NTSC_M_KR;
+               case '-':
+                       break;
+               default:
+                       IVTV_WARN("ntsc= argument not recognised\n");
+                       return 0;
+       }
+
+       /* no match found */
+       return 0;
+}
+
+static void ivtv_process_options(struct ivtv *itv)
+{
+       const char *chipname;
+       int i, j;
+
+       itv->options.megabytes[IVTV_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+       itv->options.megabytes[IVTV_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
+       itv->options.megabytes[IVTV_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
+       itv->options.megabytes[IVTV_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+       itv->options.megabytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers;
+       itv->options.megabytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers;
+       itv->options.megabytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers;
+       itv->options.cardtype = cardtype[itv->num];
+       itv->options.tuner = tuner[itv->num];
+       itv->options.radio = radio[itv->num];
+       itv->options.newi2c = newi2c;
+
+       itv->std = ivtv_parse_std(itv);
+       itv->has_cx23415 = (itv->dev->device == PCI_DEVICE_ID_IVTV15);
+       chipname = itv->has_cx23415 ? "cx23415" : "cx23416";
+       if (itv->options.cardtype == -1) {
+               IVTV_INFO("Ignore card (detected %s based chip)\n", chipname);
+               return;
+       }
+       if ((itv->card = ivtv_get_card(itv->options.cardtype - 1))) {
+               IVTV_INFO("User specified %s card (detected %s based chip)\n",
+                               itv->card->name, chipname);
+       } else if (itv->options.cardtype != 0) {
+               IVTV_ERR("Unknown user specified type, trying to autodetect card\n");
+       }
+       if (itv->card == NULL) {
+               if (itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE ||
+                   itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 ||
+                   itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) {
+                       itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150);
+                       IVTV_INFO("Autodetected Hauppauge card (%s based)\n",
+                                       chipname);
+               }
+       }
+       if (itv->card == NULL) {
+               for (i = 0; (itv->card = ivtv_get_card(i)); i++) {
+                       if (itv->card->pci_list == NULL)
+                               continue;
+                       for (j = 0; itv->card->pci_list[j].device; j++) {
+                               if (itv->dev->device !=
+                                   itv->card->pci_list[j].device)
+                                       continue;
+                               if (itv->dev->subsystem_vendor !=
+                                   itv->card->pci_list[j].subsystem_vendor)
+                                       continue;
+                               if (itv->dev->subsystem_device !=
+                                   itv->card->pci_list[j].subsystem_device)
+                                       continue;
+                               IVTV_INFO("Autodetected %s card (%s based)\n",
+                                               itv->card->name, chipname);
+                               goto done;
+                       }
+               }
+       }
+done:
+
+       if (itv->card == NULL) {
+               itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+               IVTV_ERR("Unknown card: vendor/device: %04x/%04x\n",
+                    itv->dev->vendor, itv->dev->device);
+               IVTV_ERR("              subsystem vendor/device: %04x/%04x\n",
+                    itv->dev->subsystem_vendor, itv->dev->subsystem_device);
+               IVTV_ERR("              %s based\n", chipname);
+               IVTV_ERR("Defaulting to %s card\n", itv->card->name);
+               IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+               IVTV_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+               IVTV_ERR("Prefix your subject line with [UNKNOWN CARD].\n");
+       }
+       itv->v4l2_cap = itv->card->v4l2_capabilities;
+       itv->card_name = itv->card->name;
+}
+
+/* Precondition: the ivtv structure has been memset to 0. Only
+   the dev and num fields have been filled in.
+   No assumptions on the card type may be made here (see ivtv_init_struct2
+   for that).
+ */
+static int __devinit ivtv_init_struct1(struct ivtv *itv)
+{
+       itv->base_addr = pci_resource_start(itv->dev, 0);
+       itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */
+       itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */
+
+       mutex_init(&itv->i2c_bus_lock);
+       mutex_init(&itv->udma.lock);
+
+       itv->lock = SPIN_LOCK_UNLOCKED;
+       itv->dma_reg_lock = SPIN_LOCK_UNLOCKED;
+
+       itv->vbi.work_queues = create_workqueue("ivtv_vbi");
+       if (itv->vbi.work_queues == NULL) {
+               IVTV_ERR("Could not create VBI workqueue\n");
+               return -1;
+       }
+
+       itv->yuv_info.work_queues = create_workqueue("ivtv_yuv");
+       if (itv->yuv_info.work_queues == NULL) {
+               IVTV_ERR("Could not create YUV workqueue\n");
+               destroy_workqueue(itv->vbi.work_queues);
+               return -1;
+       }
+
+       INIT_WORK(&itv->vbi.work_queue, vbi_work_handler);
+       INIT_WORK(&itv->yuv_info.work_queue, ivtv_yuv_work_handler);
+
+       /* start counting open_id at 1 */
+       itv->open_id = 1;
+
+       /* Initial settings */
+       cx2341x_fill_defaults(&itv->params);
+       itv->params.port = CX2341X_PORT_MEMORY;
+       itv->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
+       init_waitqueue_head(&itv->cap_w);
+       init_waitqueue_head(&itv->event_waitq);
+       init_waitqueue_head(&itv->vsync_waitq);
+       init_waitqueue_head(&itv->dma_waitq);
+       init_timer(&itv->dma_timer);
+       itv->dma_timer.function = ivtv_unfinished_dma;
+       itv->dma_timer.data = (unsigned long)itv;
+
+       itv->cur_dma_stream = -1;
+       itv->audio_stereo_mode = AUDIO_STEREO;
+       itv->audio_bilingual_mode = AUDIO_MONO_LEFT;
+
+       /* Ctrls */
+       itv->speed = 1000;
+
+       /* VBI */
+       itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+       itv->vbi.sliced_in = &itv->vbi.in.fmt.sliced;
+
+       /* OSD */
+       itv->osd_global_alpha_state = 1;
+       itv->osd_global_alpha = 255;
+
+       /* YUV */
+       atomic_set(&itv->yuv_info.next_dma_frame, -1);
+       itv->yuv_info.lace_mode = ivtv_yuv_mode;
+       itv->yuv_info.lace_threshold = ivtv_yuv_threshold;
+       return 0;
+}
+
+/* Second initialization part. Here the card type has been
+   autodetected. */
+static void __devinit ivtv_init_struct2(struct ivtv *itv)
+{
+       int i;
+
+       for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++)
+               if (itv->card->video_inputs[i].video_type == 0)
+                       break;
+       itv->nof_inputs = i;
+       for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++)
+               if (itv->card->audio_inputs[i].audio_type == 0)
+                       break;
+       itv->nof_audio_inputs = i;
+
+       /* 0x00EF = saa7114(239) 0x00F0 = saa7115(240) 0x0106 = micro */
+       if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X))
+               itv->digitizer = 0xF1;
+       else if (itv->card->hw_all & IVTV_HW_SAA7114)
+               itv->digitizer = 0xEF;
+       else /* cx25840 */
+               itv->digitizer = 0x140;
+
+       if (itv->card->hw_all & IVTV_HW_CX25840) {
+               itv->vbi.sliced_size = 288;  /* multiple of 16, real size = 284 */
+       } else {
+               itv->vbi.sliced_size = 64;   /* multiple of 16, real size = 52 */
+       }
+
+       /* Find tuner input */
+       for (i = 0; i < itv->nof_inputs; i++) {
+               if (itv->card->video_inputs[i].video_type ==
+                               IVTV_CARD_INPUT_VID_TUNER)
+                       break;
+       }
+       if (i == itv->nof_inputs)
+               i = 0;
+       itv->active_input = i;
+       itv->audio_input = itv->card->video_inputs[i].audio_index;
+       if (itv->card->hw_all & IVTV_HW_CX25840)
+               itv->video_dec_func = ivtv_cx25840;
+       else if (itv->card->hw_all & IVTV_HW_SAA717X)
+               itv->video_dec_func = ivtv_saa717x;
+       else
+               itv->video_dec_func = ivtv_saa7115;
+}
+
+static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
+                         const struct pci_device_id *pci_id)
+{
+       u16 cmd;
+       unsigned char pci_latency;
+
+       IVTV_DEBUG_INFO("Enabling pci device\n");
+
+       if (pci_enable_device(dev)) {
+               IVTV_ERR("Can't enable device %d!\n", itv->num);
+               return -EIO;
+       }
+       if (pci_set_dma_mask(dev, 0xffffffff)) {
+               IVTV_ERR("No suitable DMA available on card %d.\n", itv->num);
+               return -EIO;
+       }
+       if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) {
+               IVTV_ERR("Cannot request encoder memory region on card %d.\n", itv->num);
+               return -EIO;
+       }
+
+       if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET,
+                               IVTV_REG_SIZE, "ivtv registers")) {
+               IVTV_ERR("Cannot request register memory region on card %d.\n", itv->num);
+               release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+               return -EIO;
+       }
+
+       if (itv->has_cx23415 &&
+           !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET,
+                               IVTV_DECODER_SIZE, "ivtv decoder")) {
+               IVTV_ERR("Cannot request decoder memory region on card %d.\n", itv->num);
+               release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+               release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+               return -EIO;
+       }
+
+       /* Check for bus mastering */
+       pci_read_config_word(dev, PCI_COMMAND, &cmd);
+       if (!(cmd & PCI_COMMAND_MASTER)) {
+               IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n");
+               pci_set_master(dev);
+               pci_read_config_word(dev, PCI_COMMAND, &cmd);
+               if (!(cmd & PCI_COMMAND_MASTER)) {
+                       IVTV_ERR("Bus Mastering is not enabled\n");
+                       return -ENXIO;
+               }
+       }
+       IVTV_DEBUG_INFO("Bus Mastering Enabled.\n");
+
+       pci_read_config_byte(dev, PCI_CLASS_REVISION, &itv->card_rev);
+       pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+
+       if (pci_latency < 64 && ivtv_pci_latency) {
+               IVTV_INFO("Unreasonably low latency timer, "
+                              "setting to 64 (was %d)\n", pci_latency);
+               pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+               pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+       }
+       /* This config space value relates to DMA latencies. The
+          default value 0x8080 is too low however and will lead
+          to DMA errors. 0xffff is the max value which solves
+          these problems. */
+       pci_write_config_dword(dev, 0x40, 0xffff);
+
+       IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, "
+                  "irq: %d, latency: %d, memory: 0x%lx\n",
+                  itv->dev->device, itv->card_rev, dev->bus->number,
+                  PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+                  itv->dev->irq, pci_latency, (unsigned long)itv->base_addr);
+
+       return 0;
+}
+
+static void ivtv_request_module(struct ivtv *itv, const char *name)
+{
+       if (request_module(name) != 0) {
+               IVTV_ERR("Failed to load module %s\n", name);
+       } else {
+               IVTV_DEBUG_INFO("Loaded module %s\n", name);
+       }
+}
+
+static void ivtv_load_and_init_modules(struct ivtv *itv)
+{
+       struct v4l2_control ctrl;
+       u32 hw = itv->card->hw_all;
+       int i;
+
+       /* load modules */
+#ifndef CONFIG_VIDEO_TUNER
+       if (hw & IVTV_HW_TUNER) {
+               ivtv_request_module(itv, "tuner");
+#ifdef HAVE_XC3028
+               if (itv->options.tuner == TUNER_XCEIVE_XC3028)
+                       ivtv_request_module(itv, "xc3028-tuner");
+#endif
+       }
+#endif
+#ifndef CONFIG_VIDEO_CX25840
+       if (hw & IVTV_HW_CX25840)
+               ivtv_request_module(itv, "cx25840");
+#endif
+#ifndef CONFIG_VIDEO_SAA711X
+       if (hw & IVTV_HW_SAA711X)
+               ivtv_request_module(itv, "saa7115");
+#endif
+#ifndef CONFIG_VIDEO_SAA7127
+       if (hw & IVTV_HW_SAA7127)
+               ivtv_request_module(itv, "saa7127");
+#endif
+       if (hw & IVTV_HW_SAA717X)
+               ivtv_request_module(itv, "saa717x");
+#ifndef CONFIG_VIDEO_UPD64031A
+       if (hw & IVTV_HW_UPD64031A)
+               ivtv_request_module(itv, "upd64031a");
+#endif
+#ifndef CONFIG_VIDEO_UPD64083
+       if (hw & IVTV_HW_UPD6408X)
+               ivtv_request_module(itv, "upd64083");
+#endif
+#ifndef CONFIG_VIDEO_MSP3400
+       if (hw & IVTV_HW_MSP34XX)
+               ivtv_request_module(itv, "msp3400");
+#endif
+       if (hw & IVTV_HW_TVAUDIO)
+               ivtv_request_module(itv, "tvaudio");
+#ifndef CONFIG_VIDEO_WM8775
+       if (hw & IVTV_HW_WM8775)
+               ivtv_request_module(itv, "wm8775");
+#endif
+#ifndef CONFIG_VIDEO_WM8739
+       if (hw & IVTV_HW_WM8739)
+               ivtv_request_module(itv, "wm8739");
+#endif
+#ifndef CONFIG_VIDEO_CS53L32A
+       if (hw & IVTV_HW_CS53L32A)
+               ivtv_request_module(itv, "cs53l32a");
+#endif
+
+       /* check which i2c devices are actually found */
+       for (i = 0; i < 32; i++) {
+               u32 device = 1 << i;
+
+               if (!(device & hw))
+                       continue;
+               if (device == IVTV_HW_GPIO) {
+                       /* GPIO is always available */
+                       itv->hw_flags |= IVTV_HW_GPIO;
+                       continue;
+               }
+               if (ivtv_i2c_hw_addr(itv, device) > 0)
+                       itv->hw_flags |= device;
+       }
+
+       hw = itv->hw_flags;
+
+       if (itv->card->type == IVTV_CARD_CX23416GYC) {
+               /* Several variations of this card exist, detect which card
+                  type should be used. */
+               if ((hw & (IVTV_HW_UPD64031A | IVTV_HW_UPD6408X)) == 0)
+                       itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGRYCS);
+               else if ((hw & IVTV_HW_UPD64031A) == 0)
+                       itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGR);
+       }
+
+       if (hw & IVTV_HW_CX25840) {
+               /* CX25840_CID_ENABLE_PVR150_WORKAROUND */
+               ctrl.id = V4L2_CID_PRIVATE_BASE;
+               ctrl.value = itv->pvr150_workaround;
+               itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl);
+
+               itv->vbi.raw_decoder_line_size = 1444;
+               itv->vbi.raw_decoder_sav_odd_field = 0x20;
+               itv->vbi.raw_decoder_sav_even_field = 0x60;
+               itv->vbi.sliced_decoder_line_size = 272;
+               itv->vbi.sliced_decoder_sav_odd_field = 0xB0;
+               itv->vbi.sliced_decoder_sav_even_field = 0xF0;
+       }
+
+       if (hw & IVTV_HW_SAA711X) {
+               struct v4l2_chip_ident v = { V4L2_CHIP_MATCH_I2C_DRIVER, I2C_DRIVERID_SAA711X };
+
+               /* determine the exact saa711x model */
+               itv->hw_flags &= ~IVTV_HW_SAA711X;
+
+               ivtv_saa7115(itv, VIDIOC_G_CHIP_IDENT, &v);
+               if (v.ident == V4L2_IDENT_SAA7114) {
+                       itv->hw_flags |= IVTV_HW_SAA7114;
+                       /* VBI is not yet supported by the saa7114 driver. */
+                       itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE);
+               }
+               else {
+                       itv->hw_flags |= IVTV_HW_SAA7115;
+               }
+               itv->vbi.raw_decoder_line_size = 1443;
+               itv->vbi.raw_decoder_sav_odd_field = 0x25;
+               itv->vbi.raw_decoder_sav_even_field = 0x62;
+               itv->vbi.sliced_decoder_line_size = 51;
+               itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
+               itv->vbi.sliced_decoder_sav_even_field = 0xEC;
+       }
+
+       if (hw & IVTV_HW_SAA717X) {
+               itv->vbi.raw_decoder_line_size = 1443;
+               itv->vbi.raw_decoder_sav_odd_field = 0x25;
+               itv->vbi.raw_decoder_sav_even_field = 0x62;
+               itv->vbi.sliced_decoder_line_size = 51;
+               itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
+               itv->vbi.sliced_decoder_sav_even_field = 0xEC;
+       }
+}
+
+static int __devinit ivtv_probe(struct pci_dev *dev,
+                               const struct pci_device_id *pci_id)
+{
+       int retval = 0;
+       int video_input;
+       int yuv_buf_size;
+       int vbi_buf_size;
+       int fw_retry_count = 3;
+       struct ivtv *itv;
+       struct v4l2_frequency vf;
+
+       spin_lock(&ivtv_cards_lock);
+
+       /* Make sure we've got a place for this card */
+       if (ivtv_cards_active == IVTV_MAX_CARDS) {
+               printk(KERN_ERR "ivtv:  Maximum number of cards detected (%d).\n",
+                             ivtv_cards_active);
+               spin_unlock(&ivtv_cards_lock);
+               return -ENOMEM;
+       }
+
+       itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC);
+       if (itv == 0) {
+               spin_unlock(&ivtv_cards_lock);
+               return -ENOMEM;
+       }
+       ivtv_cards[ivtv_cards_active] = itv;
+       itv->dev = dev;
+       itv->num = ivtv_cards_active++;
+       snprintf(itv->name, sizeof(itv->name) - 1, "ivtv%d", itv->num);
+       if (itv->num) {
+               printk(KERN_INFO "ivtv:  ======================  NEXT CARD  ======================\n");
+       }
+
+       spin_unlock(&ivtv_cards_lock);
+
+       ivtv_process_options(itv);
+       if (itv->options.cardtype == -1) {
+               retval = -ENODEV;
+               goto err;
+       }
+       if (ivtv_init_struct1(itv)) {
+               retval = -ENOMEM;
+               goto err;
+       }
+
+       IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr);
+
+       /* PCI Device Setup */
+       if ((retval = ivtv_setup_pci(itv, dev, pci_id)) != 0) {
+               if (retval == -EIO)
+                       goto free_workqueue;
+               else if (retval == -ENXIO)
+                       goto free_mem;
+       }
+       /* save itv in the pci struct for later use */
+       pci_set_drvdata(dev, itv);
+
+       /* map io memory */
+       IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+                  itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE);
+       itv->enc_mem = ioremap_nocache(itv->base_addr + IVTV_ENCODER_OFFSET,
+                                      IVTV_ENCODER_SIZE);
+       if (!itv->enc_mem) {
+               IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n");
+               IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n");
+               retval = -ENOMEM;
+               goto free_mem;
+       }
+
+       if (itv->has_cx23415) {
+               IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+                               itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+               itv->dec_mem = ioremap_nocache(itv->base_addr + IVTV_DECODER_OFFSET,
+                               IVTV_DECODER_SIZE);
+               if (!itv->dec_mem) {
+                       IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n");
+                       IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n");
+                       retval = -ENOMEM;
+                       goto free_mem;
+               }
+       }
+       else {
+               itv->dec_mem = itv->enc_mem;
+       }
+
+       /* map registers memory */
+       IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+                  itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+       itv->reg_mem =
+           ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+       if (!itv->reg_mem) {
+               IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n");
+               IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n");
+               retval = -ENOMEM;
+               goto free_io;
+       }
+
+       while (--fw_retry_count > 0) {
+               /* load firmware */
+               if (ivtv_firmware_init(itv) == 0)
+                       break;
+               if (fw_retry_count > 1)
+                       IVTV_WARN("Retry loading firmware\n");
+       }
+       if (fw_retry_count == 0) {
+               IVTV_ERR("Error initializing firmware\n");
+               goto free_i2c;
+       }
+
+       /* Try and get firmware versions */
+       IVTV_DEBUG_INFO("Getting firmware version..\n");
+       ivtv_firmware_versions(itv);
+
+       /* Check yuv output filter table */
+       if (itv->has_cx23415) ivtv_yuv_filter_check(itv);
+
+       ivtv_gpio_init(itv);
+
+       /* active i2c  */
+       IVTV_DEBUG_INFO("activating i2c...\n");
+       if (init_ivtv_i2c(itv)) {
+               IVTV_ERR("Could not initialize i2c\n");
+               goto free_irq;
+       }
+
+       IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active);
+
+       if (itv->card->hw_all & IVTV_HW_TVEEPROM) {
+#ifdef CONFIG_VIDEO_TVEEPROM_MODULE
+               ivtv_request_module(itv, "tveeprom");
+#endif
+               /* Based on the model number the cardtype may be changed.
+                  The PCI IDs are not always reliable. */
+               ivtv_process_eeprom(itv);
+       }
+
+       if (itv->std == 0) {
+               itv->std = V4L2_STD_NTSC_M;
+       }
+
+       if (itv->options.tuner == -1) {
+               int i;
+
+               for (i = 0; i < IVTV_CARD_MAX_TUNERS; i++) {
+                       if ((itv->std & itv->card->tuners[i].std) == 0)
+                               continue;
+                       itv->options.tuner = itv->card->tuners[i].tuner;
+                       break;
+               }
+       }
+       /* if no tuner was found, then pick the first tuner in the card list */
+       if (itv->options.tuner == -1 && itv->card->tuners[0].std) {
+               itv->std = itv->card->tuners[0].std;
+               itv->options.tuner = itv->card->tuners[0].tuner;
+       }
+       if (itv->options.radio == -1)
+               itv->options.radio = (itv->card->radio_input.audio_type != 0);
+
+       /* The card is now fully identified, continue with card-specific
+          initialization. */
+       ivtv_init_struct2(itv);
+
+       ivtv_load_and_init_modules(itv);
+
+       if (itv->std & V4L2_STD_525_60) {
+               itv->is_60hz = 1;
+               itv->is_out_60hz = 1;
+       } else {
+               itv->is_50hz = 1;
+               itv->is_out_50hz = 1;
+       }
+       itv->params.video_gop_size = itv->is_60hz ? 15 : 12;
+
+       itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
+       itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
+       itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000;
+
+       /* 0x15180 == 720 * 480 / 4, 0x19500 == 720 * 576 / 4 */
+       yuv_buf_size = itv->is_60hz ? 0x15180 : 0x19500;
+       itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = yuv_buf_size / 2;
+       itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = yuv_buf_size / 8;
+
+       /* Setup VBI Raw Size. Should be big enough to hold PAL.
+          It is possible to switch between PAL and NTSC, so we need to
+          take the largest size here. */
+       /* 1456 is multiple of 16, real size = 1444 */
+       itv->vbi.raw_size = 1456;
+       /* We use a buffer size of 1/2 of the total size needed for a
+          frame. This is actually very useful, since we now receive
+          a field at a time and that makes 'compressing' the raw data
+          down to size by stripping off the SAV codes a lot easier.
+          Note: having two different buffer sizes prevents standard
+          switching on the fly. We need to find a better solution... */
+       vbi_buf_size = itv->vbi.raw_size * (itv->is_60hz ? 24 : 36) / 2;
+       itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
+       itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_VBI] = sizeof(struct v4l2_sliced_vbi_data) * 36;
+
+       if (itv->options.radio > 0)
+               itv->v4l2_cap |= V4L2_CAP_RADIO;
+
+       retval = ivtv_streams_setup(itv);
+       if (retval) {
+               IVTV_ERR("Error %d setting up streams\n", retval);
+               goto free_i2c;
+       }
+
+       /* Start Threads */
+       IVTV_DEBUG_INFO("Starting Threads\n");
+
+       /* Decoder Thread */
+       if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) {
+               ivtv_init_mpeg_decoder(itv);
+       }
+
+       IVTV_DEBUG_IRQ("Masking interrupts\n");
+       /* clear interrupt mask, effectively disabling interrupts */
+       ivtv_set_irq_mask(itv, 0xffffffff);
+
+       /* Register IRQ */
+       retval = request_irq(itv->dev->irq, ivtv_irq_handler,
+                            SA_SHIRQ | SA_INTERRUPT, itv->name, (void *)itv);
+       if (retval) {
+               IVTV_ERR("Failed to register irq %d\n", retval);
+               goto free_streams;
+       }
+
+       /* On a cx23416 this seems to be able to enable DMA to the chip? */
+       if (!itv->has_cx23415)
+               write_reg_sync(0x03, IVTV_REG_DMACONTROL);
+
+       /* Default interrupts enabled. For the PVR350 this includes the
+          decoder VSYNC interrupt, which is always on. It is not only used
+          during decoding but also by the OSD.
+          Some old PVR250 cards had a cx23415, so testing for that is too
+          general. Instead test if the card has video output capability. */
+       if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
+               ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC);
+       else
+               ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT);
+
+       if (itv->options.tuner > -1) {
+               struct tuner_setup setup;
+
+               setup.addr = ADDR_UNSET;
+               setup.type = itv->options.tuner;
+               setup.mode_mask = T_ANALOG_TV;  /* matches TV tuners */
+#ifdef HAVE_XC3028
+               setup.initmode = V4L2_TUNER_ANALOG_TV;
+               if (itv->options.tuner == TUNER_XCEIVE_XC3028) {
+                       setup.gpio_write = ivtv_reset_tuner_gpio;
+                       setup.gpio_priv = itv;
+               }
+#endif
+               ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup);
+       }
+
+       vf.tuner = 0;
+       vf.type = V4L2_TUNER_ANALOG_TV;
+       vf.frequency = 6400; /* the tuner 'baseline' frequency */
+       if (itv->std & V4L2_STD_NTSC_M) {
+               /* Why on earth? */
+               vf.frequency = 1076;    /* ch. 4 67250*16/1000 */
+       }
+
+       /* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+          are not. */
+       itv->tuner_std = itv->std;
+
+       video_input = itv->active_input;
+       itv->active_input++;    /* Force update of input */
+       ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_INPUT, &video_input);
+
+       /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+          in one place. */
+       itv->std++;             /* Force full standard initialization */
+       itv->std_out = itv->std;
+       ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_STD, &itv->tuner_std);
+       ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_FREQUENCY, &vf);
+       if (itv->has_cx23415)
+               ivtv_set_osd_alpha(itv);
+
+       IVTV_INFO("Initialized %s, card #%d\n", itv->card_name, itv->num);
+
+       return 0;
+
+      free_irq:
+       free_irq(itv->dev->irq, (void *)itv);
+      free_streams:
+       ivtv_streams_cleanup(itv);
+      free_i2c:
+       exit_ivtv_i2c(itv);
+      free_io:
+       ivtv_iounmap(itv);
+      free_mem:
+       release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+       release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+       if (itv->has_cx23415)
+               release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+      free_workqueue:
+       destroy_workqueue(itv->vbi.work_queues);
+       destroy_workqueue(itv->yuv_info.work_queues);
+      err:
+       if (retval == 0)
+               retval = -ENODEV;
+       IVTV_ERR("Error %d on initialization\n", retval);
+
+       kfree(ivtv_cards[ivtv_cards_active]);
+       ivtv_cards[ivtv_cards_active] = NULL;
+       return retval;
+}
+
+static void ivtv_remove(struct pci_dev *pci_dev)
+{
+       struct ivtv *itv = pci_get_drvdata(pci_dev);
+
+       IVTV_DEBUG_INFO("Removing Card #%d.\n", itv->num);
+
+       /* Stop all captures */
+       IVTV_DEBUG_INFO(" Stopping all streams.\n");
+       if (atomic_read(&itv->capturing) > 0)
+               ivtv_stop_all_captures(itv);
+
+       /* Stop all decoding */
+       IVTV_DEBUG_INFO(" Stopping decoding.\n");
+       if (atomic_read(&itv->decoding) > 0) {
+               int type;
+
+               if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+                       type = IVTV_DEC_STREAM_TYPE_YUV;
+               else
+                       type = IVTV_DEC_STREAM_TYPE_MPG;
+               ivtv_stop_v4l2_decode_stream(&itv->streams[type],
+                       VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0);
+       }
+
+       /* Interrupts */
+       IVTV_DEBUG_INFO(" Disabling interrupts.\n");
+       ivtv_set_irq_mask(itv, 0xffffffff);
+       del_timer_sync(&itv->dma_timer);
+
+       /* Stop all Work Queues */
+       IVTV_DEBUG_INFO(" Stop Work Queues.\n");
+       flush_workqueue(itv->vbi.work_queues);
+       flush_workqueue(itv->yuv_info.work_queues);
+       destroy_workqueue(itv->vbi.work_queues);
+       destroy_workqueue(itv->yuv_info.work_queues);
+
+       IVTV_DEBUG_INFO(" Stopping Firmware.\n");
+       ivtv_halt_firmware(itv);
+
+       IVTV_DEBUG_INFO(" Unregistering v4l devices.\n");
+       ivtv_streams_cleanup(itv);
+       IVTV_DEBUG_INFO(" Freeing dma resources.\n");
+       ivtv_udma_free(itv);
+
+       exit_ivtv_i2c(itv);
+
+       IVTV_DEBUG_INFO(" Releasing irq.\n");
+       free_irq(itv->dev->irq, (void *)itv);
+
+       if (itv->dev) {
+               ivtv_iounmap(itv);
+       }
+
+       IVTV_DEBUG_INFO(" Releasing mem.\n");
+       release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+       release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+       if (itv->has_cx23415)
+               release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+
+       pci_disable_device(itv->dev);
+
+       IVTV_INFO("Removed %s, card #%d\n", itv->card_name, itv->num);
+}
+
+/* define a pci_driver for card detection */
+static struct pci_driver ivtv_pci_driver = {
+      .name =     "ivtv",
+      .id_table = ivtv_pci_tbl,
+      .probe =    ivtv_probe,
+      .remove =   ivtv_remove,
+};
+
+static int module_start(void)
+{
+       printk(KERN_INFO "ivtv:  ==================== START INIT IVTV ====================\n");
+       printk(KERN_INFO "ivtv:  version %s (" VERMAGIC_STRING ") loading\n", IVTV_VERSION);
+
+       memset(ivtv_cards, 0, sizeof(ivtv_cards));
+
+       /* Validate parameters */
+       if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) {
+               printk(KERN_ERR "ivtv:  ivtv_first_minor must be between 0 and %d. Exiting...\n",
+                    IVTV_MAX_CARDS - 1);
+               return -1;
+       }
+
+       if (ivtv_debug < 0 || ivtv_debug > 511) {
+               ivtv_debug = 0;
+               printk(KERN_INFO "ivtv:  debug value must be >= 0 and <= 511!\n");
+       }
+
+       if (pci_module_init(&ivtv_pci_driver)) {
+               printk(KERN_ERR "ivtv:  Error detecting PCI card\n");
+               return -ENODEV;
+       }
+       printk(KERN_INFO "ivtv:  ====================  END INIT IVTV  ====================\n");
+       return 0;
+}
+
+static void module_cleanup(void)
+{
+       int i, j;
+
+       for (i = 0; i < ivtv_cards_active; i++) {
+               if (ivtv_cards[i] == NULL)
+                       continue;
+               for (j = 0; j < IVTV_VBI_FRAMES; j++) {
+                       kfree(ivtv_cards[i]->vbi.sliced_mpeg_data[j]);
+               }
+               kfree(ivtv_cards[i]);
+       }
+       pci_unregister_driver(&ivtv_pci_driver);
+}
+
+EXPORT_SYMBOL(ivtv_set_irq_mask);
+EXPORT_SYMBOL(ivtv_cards_active);
+EXPORT_SYMBOL(ivtv_cards);
+EXPORT_SYMBOL(ivtv_api);
+EXPORT_SYMBOL(ivtv_vapi);
+EXPORT_SYMBOL(ivtv_vapi_result);
+EXPORT_SYMBOL(ivtv_clear_irq_mask);
+EXPORT_SYMBOL(ivtv_debug);
+EXPORT_SYMBOL(ivtv_reset_ir_gpio);
+EXPORT_SYMBOL(ivtv_udma_setup);
+EXPORT_SYMBOL(ivtv_udma_unmap);
+EXPORT_SYMBOL(ivtv_udma_alloc);
+EXPORT_SYMBOL(ivtv_udma_prepare);
+
+module_init(module_start);
+module_exit(module_cleanup);
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
new file mode 100644 (file)
index 0000000..546d7bb
--- /dev/null
@@ -0,0 +1,866 @@
+/*
+    ivtv driver internal defines and structures
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef IVTV_DRIVER_H
+#define IVTV_DRIVER_H
+
+/* Internal header for ivtv project:
+ * Driver for the cx23415/6 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.ivtvdriver.org
+ *
+ * -----
+ * MPG600/MPG160 support by  T.Adachi <tadachi@tadachi-net.com>
+ *                      and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ *                using information provided by Jiun-Kuei Jung @ AVerMedia.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <linux/byteorder/swab.h>
+#include <linux/pagemap.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include <media/cx2341x.h>
+
+/* #define HAVE_XC3028 1 */
+
+#include <media/ivtv.h>
+
+#ifdef CONFIG_LIRC_I2C
+#  error "This driver is not compatible with the LIRC I2C kernel configuration option."
+#endif /* CONFIG_LIRC_I2C */
+
+#ifndef CONFIG_PCI
+#  error "This driver requires kernel PCI support."
+#endif /* CONFIG_PCI */
+
+#define IVTV_ENCODER_OFFSET    0x00000000
+#define IVTV_ENCODER_SIZE      0x00800000      /* Last half isn't needed 0x01000000 */
+
+#define IVTV_DECODER_OFFSET    0x01000000
+#define IVTV_DECODER_SIZE      0x00800000      /* Last half isn't needed 0x01000000 */
+
+#define IVTV_REG_OFFSET        0x02000000
+#define IVTV_REG_SIZE          0x00010000
+
+/* Buffers on hardware offsets */
+#define IVTV_YUV_BUFFER_OFFSET    0x001a8600   /* First YUV Buffer */
+#define IVTV_YUV_BUFFER_OFFSET_1  0x00240400   /* Second YUV Buffer */
+#define IVTV_YUV_BUFFER_OFFSET_2  0x002d8200   /* Third YUV Buffer */
+#define IVTV_YUV_BUFFER_OFFSET_3  0x00370000   /* Fourth YUV Buffer */
+#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400      /* Offset to UV Buffer */
+
+/* Offset to filter table in firmware */
+#define IVTV_YUV_HORIZONTAL_FILTER_OFFSET 0x025d8
+#define IVTV_YUV_VERTICAL_FILTER_OFFSET 0x03358
+
+extern const u32 yuv_offset[4];
+
+/* Maximum ivtv driver instances.
+   Based on 6 PVR500s each with two PVR15s...
+   TODO: make this dynamic. I believe it is only a global in order to support
+    ivtv-fb. There must be a better way to do that. */
+#define IVTV_MAX_CARDS 12
+
+/* Supported cards */
+#define IVTV_CARD_PVR_250            0 /* WinTV PVR 250 */
+#define IVTV_CARD_PVR_350            1 /* encoder, decoder, tv-out */
+#define IVTV_CARD_PVR_150            2 /* WinTV PVR 150 and PVR 500 (really just two
+                                          PVR150s on one PCI board) */
+#define IVTV_CARD_M179               3 /* AVerMedia M179 (encoder only) */
+#define IVTV_CARD_MPG600             4 /* Kuroutoshikou ITVC16-STVLP/YUAN MPG600, encoder only */
+#define IVTV_CARD_MPG160             5 /* Kuroutoshikou ITVC15-STVLP/YUAN MPG160
+                                          cx23415 based, but does not have tv-out */
+#define IVTV_CARD_PG600              6 /* YUAN PG600/DIAMONDMM PVR-550 based on the CX Falcon 2 */
+#define IVTV_CARD_AVC2410            7 /* Adaptec AVC-2410 */
+#define IVTV_CARD_AVC2010            8 /* Adaptec AVD-2010 (No Tuner) */
+#define IVTV_CARD_TG5000TV           9 /* NAGASE TRANSGEAR 5000TV, encoder only */
+#define IVTV_CARD_VA2000MAX_SNT6     10 /* VA2000MAX-STN6 */
+#define IVTV_CARD_CX23416GYC        11 /* Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */
+#define IVTV_CARD_GV_MVPRX          12 /* I/O Data GV-MVP/RX, RX2, RX2W */
+#define IVTV_CARD_GV_MVPRX2E        13 /* I/O Data GV-MVP/RX2E */
+#define IVTV_CARD_GOTVIEW_PCI_DVD    14        /* GotView PCI DVD */
+#define IVTV_CARD_GOTVIEW_PCI_DVD2   15        /* GotView PCI DVD2 */
+#define IVTV_CARD_YUAN_MPC622        16        /* Yuan MPC622 miniPCI */
+#define IVTV_CARD_DCTMTVP1          17 /* DIGITAL COWBOY DCT-MTVP1 */
+#ifdef HAVE_XC3028
+#define IVTV_CARD_PG600V2           18 /* Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 */
+#define IVTV_CARD_LAST                      18
+#else
+#define IVTV_CARD_LAST                      17
+#endif
+
+/* Variants of existing cards but with the same PCI IDs. The driver
+   detects these based on other device information.
+   These cards must always come last.
+   New cards must be inserted above, and the indices of the cards below
+   must be adjusted accordingly. */
+
+/* PVR-350 V1 (uses saa7114) */
+#define IVTV_CARD_PVR_350_V1        (IVTV_CARD_LAST+1)
+/* 2 variants of Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */
+#define IVTV_CARD_CX23416GYC_NOGR    (IVTV_CARD_LAST+2)
+#define IVTV_CARD_CX23416GYC_NOGRYCS (IVTV_CARD_LAST+3)
+
+#define IVTV_ENC_STREAM_TYPE_MPG  0
+#define IVTV_ENC_STREAM_TYPE_YUV  1
+#define IVTV_ENC_STREAM_TYPE_VBI  2
+#define IVTV_ENC_STREAM_TYPE_PCM  3
+#define IVTV_ENC_STREAM_TYPE_RAD  4
+#define IVTV_DEC_STREAM_TYPE_MPG  5
+#define IVTV_DEC_STREAM_TYPE_VBI  6
+#define IVTV_DEC_STREAM_TYPE_VOUT 7
+#define IVTV_DEC_STREAM_TYPE_YUV  8
+#define IVTV_MAX_STREAMS         9
+
+#define IVTV_V4L2_DEC_MPG_OFFSET  16   /* offset from 0 to register decoder mpg v4l2 minors on */
+#define IVTV_V4L2_ENC_PCM_OFFSET  24   /* offset from 0 to register pcm v4l2 minors on */
+#define IVTV_V4L2_ENC_YUV_OFFSET  32   /* offset from 0 to register yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_YUV_OFFSET  48   /* offset from 0 to register decoder yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_VBI_OFFSET   8   /* offset from 0 to register decoder vbi input v4l2 minors on */
+#define IVTV_V4L2_DEC_VOUT_OFFSET 16   /* offset from 0 to register vbi output v4l2 minors on */
+
+#define IVTV_ENC_MEM_START 0x00000000
+#define IVTV_DEC_MEM_START 0x01000000
+
+/* system vendor and device IDs */
+#define PCI_VENDOR_ID_ICOMP  0x4444
+#define PCI_DEVICE_ID_IVTV15 0x0803
+#define PCI_DEVICE_ID_IVTV16 0x0016
+
+/* subsystem vendor ID */
+#define IVTV_PCI_ID_HAUPPAUGE          0x0070
+#define IVTV_PCI_ID_HAUPPAUGE_ALT1     0x0270
+#define IVTV_PCI_ID_HAUPPAUGE_ALT2     0x4070
+#define IVTV_PCI_ID_ADAPTEC            0x9005
+#define IVTV_PCI_ID_AVERMEDIA          0x1461
+#define IVTV_PCI_ID_YUAN1              0x12ab
+#define IVTV_PCI_ID_YUAN2              0xff01
+#define IVTV_PCI_ID_YUAN3              0xffab
+#define IVTV_PCI_ID_YUAN4              0xfbab
+#define IVTV_PCI_ID_DIAMONDMM          0xff92
+#define IVTV_PCI_ID_IODATA             0x10fc
+#define IVTV_PCI_ID_MELCO              0x1154
+#define IVTV_PCI_ID_GOTVIEW1           0xffac
+#define IVTV_PCI_ID_GOTVIEW2           0xffad
+
+/* Decoder Buffer hardware size on Chip */
+#define IVTV_DEC_MAX_BUF        0x00100000     /* max bytes in decoder buffer */
+#define IVTV_DEC_MIN_BUF        0x00010000     /* min bytes in dec buffer */
+
+/* ======================================================================== */
+/* ========================== START USER SETTABLE DMA VARIABLES =========== */
+/* ======================================================================== */
+
+#define IVTV_DMA_SG_OSD_ENT    (2883584/PAGE_SIZE)     /* sg entities */
+
+/* DMA Buffers, Default size in MB allocated */
+#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4
+#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2
+#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1
+#define IVTV_DEFAULT_ENC_PCM_BUFFERS 1
+#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1
+#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1
+#define IVTV_DEFAULT_DEC_VBI_BUFFERS 1
+
+/* ======================================================================== */
+/* ========================== END USER SETTABLE DMA VARIABLES ============= */
+/* ======================================================================== */
+
+/* Decoder Status Register */
+#define IVTV_DMA_ERR_LIST      0x00000010
+#define IVTV_DMA_ERR_WRITE     0x00000008
+#define IVTV_DMA_ERR_READ      0x00000004
+#define IVTV_DMA_SUCCESS_WRITE         0x00000002
+#define IVTV_DMA_SUCCESS_READ  0x00000001
+#define IVTV_DMA_READ_ERR      (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_READ)
+#define IVTV_DMA_WRITE_ERR     (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE)
+#define IVTV_DMA_ERR           (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE | IVTV_DMA_ERR_READ)
+
+/* DMA Registers */
+#define IVTV_REG_DMAXFER       (0x0000)
+#define IVTV_REG_DMASTATUS     (0x0004)
+#define IVTV_REG_DECDMAADDR    (0x0008)
+#define IVTV_REG_ENCDMAADDR    (0x000c)
+#define IVTV_REG_DMACONTROL    (0x0010)
+#define IVTV_REG_IRQSTATUS     (0x0040)
+#define IVTV_REG_IRQMASK       (0x0048)
+
+/* Setup Registers */
+#define IVTV_REG_ENC_SDRAM_REFRESH     (0x07F8)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE   (0x07FC)
+#define IVTV_REG_DEC_SDRAM_REFRESH     (0x08F8)
+#define IVTV_REG_DEC_SDRAM_PRECHARGE   (0x08FC)
+#define IVTV_REG_VDM                   (0x2800)
+#define IVTV_REG_AO                    (0x2D00)
+#define IVTV_REG_BYTEFLUSH             (0x2D24)
+#define IVTV_REG_SPU                   (0x9050)
+#define IVTV_REG_HW_BLOCKS             (0x9054)
+#define IVTV_REG_VPU                   (0x9058)
+#define IVTV_REG_APU                   (0xA064)
+
+#define IVTV_IRQ_ENC_START_CAP         (0x1 << 31)
+#define IVTV_IRQ_ENC_EOS               (0x1 << 30)
+#define IVTV_IRQ_ENC_VBI_CAP           (0x1 << 29)
+#define IVTV_IRQ_ENC_VIM_RST           (0x1 << 28)
+#define IVTV_IRQ_ENC_DMA_COMPLETE      (0x1 << 27)
+#define IVTV_IRQ_DEC_AUD_MODE_CHG      (0x1 << 24)
+#define IVTV_IRQ_DEC_DATA_REQ          (0x1 << 22)
+#define IVTV_IRQ_DEC_DMA_COMPLETE      (0x1 << 20)
+#define IVTV_IRQ_DEC_VBI_RE_INSERT     (0x1 << 19)
+#define IVTV_IRQ_DMA_ERR               (0x1 << 18)
+#define IVTV_IRQ_DMA_WRITE             (0x1 << 17)
+#define IVTV_IRQ_DMA_READ              (0x1 << 16)
+#define IVTV_IRQ_DEC_VSYNC             (0x1 << 10)
+
+/* IRQ Masks */
+#define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|IVTV_IRQ_DMA_READ)
+
+#define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS)
+#define IVTV_IRQ_MASK_DECODE  (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG)
+
+/* i2c stuff */
+#define I2C_CLIENTS_MAX 16
+
+/* debugging */
+
+#define IVTV_DBGFLG_WARN  (1 << 0)
+#define IVTV_DBGFLG_INFO  (1 << 1)
+#define IVTV_DBGFLG_API   (1 << 2)
+#define IVTV_DBGFLG_DMA   (1 << 3)
+#define IVTV_DBGFLG_IOCTL (1 << 4)
+#define IVTV_DBGFLG_I2C   (1 << 5)
+#define IVTV_DBGFLG_IRQ   (1 << 6)
+#define IVTV_DBGFLG_DEC   (1 << 7)
+#define IVTV_DBGFLG_YUV   (1 << 8)
+
+/* NOTE: extra space before comma in 'itv->num , ## args' is required for
+   gcc-2.95, otherwise it won't compile. */
+#define IVTV_DEBUG(x, type, fmt, args...) \
+       do { \
+               if ((x) & ivtv_debug) \
+                       printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \
+       } while (0)
+#define IVTV_DEBUG_WARN(fmt, args...)  IVTV_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args)
+#define IVTV_DEBUG_INFO(fmt, args...)  IVTV_DEBUG(IVTV_DBGFLG_INFO, "info",fmt , ## args)
+#define IVTV_DEBUG_API(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args)
+#define IVTV_DEBUG_DMA(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
+#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define IVTV_DEBUG_I2C(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
+#define IVTV_DEBUG_IRQ(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
+#define IVTV_DEBUG_DEC(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
+#define IVTV_DEBUG_YUV(fmt, args...)   IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
+
+#define IVTV_FB_DEBUG(x, type, fmt, args...) \
+       do { \
+               if ((x) & ivtv_debug) \
+                       printk(KERN_INFO "ivtv%d-fb " type ": " fmt, itv->num , ## args); \
+       } while (0)
+#define IVTV_FB_DEBUG_WARN(fmt, args...)  IVTV_FB_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args)
+#define IVTV_FB_DEBUG_INFO(fmt, args...)  IVTV_FB_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args)
+#define IVTV_FB_DEBUG_API(fmt, args...)   IVTV_FB_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args)
+#define IVTV_FB_DEBUG_DMA(fmt, args...)   IVTV_FB_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
+#define IVTV_FB_DEBUG_IOCTL(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define IVTV_FB_DEBUG_I2C(fmt, args...)   IVTV_FB_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
+#define IVTV_FB_DEBUG_IRQ(fmt, args...)   IVTV_FB_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
+#define IVTV_FB_DEBUG_DEC(fmt, args...)   IVTV_FB_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
+#define IVTV_FB_DEBUG_YUV(fmt, args...)   IVTV_FB_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
+
+/* Standard kernel messages */
+#define IVTV_ERR(fmt, args...)      printk(KERN_ERR  "ivtv%d: " fmt, itv->num , ## args)
+#define IVTV_WARN(fmt, args...)     printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args)
+#define IVTV_INFO(fmt, args...)     printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args)
+#define IVTV_FB_ERR(fmt, args...)  printk(KERN_ERR  "ivtv%d-fb: " fmt, itv->num , ## args)
+#define IVTV_FB_INFO(fmt, args...) printk(KERN_INFO "ivtv%d-fb: " fmt, itv->num , ## args)
+
+/* Values for IVTV_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */
+#define MPEG_FRAME_TYPE_IFRAME 1
+#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3
+#define MPEG_FRAME_TYPE_ALL 7
+
+/* output modes (cx23415 only) */
+#define OUT_NONE        0
+#define OUT_MPG         1
+#define OUT_YUV         2
+#define OUT_UDMA_YUV    3
+#define OUT_PASSTHROUGH 4
+
+#define IVTV_MAX_PGM_INDEX (400)
+
+extern int ivtv_debug;
+
+
+struct ivtv_options {
+       int megabytes[IVTV_MAX_STREAMS]; /* Size in megabytes of each stream */
+       int cardtype;           /* force card type on load */
+       int tuner;              /* set tuner on load */
+       int radio;              /* enable/disable radio */
+       int newi2c;             /* New I2C algorithm */
+};
+
+#define IVTV_MBOX_DMA_START 6
+#define IVTV_MBOX_DMA_END 8
+#define IVTV_MBOX_DMA 9
+#define IVTV_MBOX_FIELD_DISPLAYED 8
+
+/* ivtv-specific mailbox template */
+struct ivtv_mailbox {
+       u32 flags;
+       u32 cmd;
+       u32 retval;
+       u32 timeout;
+       u32 data[CX2341X_MBOX_MAX_DATA];
+};
+
+struct ivtv_api_cache {
+       unsigned long last_jiffies;             /* when last command was issued */
+       u32 data[CX2341X_MBOX_MAX_DATA];        /* last sent api data */
+};
+
+struct ivtv_mailbox_data {
+       volatile struct ivtv_mailbox __iomem *mbox;
+       /* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes.
+          If the bit is set, then the corresponding mailbox is in use by the driver. */
+       unsigned long busy;
+       u8 max_mbox;
+};
+
+/* per-buffer bit flags */
+#define IVTV_F_B_NEED_BUF_SWAP  0      /* this buffer should be byte swapped */
+
+/* per-stream, s_flags */
+#define IVTV_F_S_DMA_PENDING   0       /* this stream has pending DMA */
+#define IVTV_F_S_DMA_HAS_VBI   1       /* the current DMA request also requests VBI data */
+#define IVTV_F_S_NEEDS_DATA    2       /* this decoding stream needs more data */
+
+#define IVTV_F_S_CLAIMED       3       /* this stream is claimed */
+#define IVTV_F_S_STREAMING      4      /* the fw is decoding/encoding this stream */
+#define IVTV_F_S_INTERNAL_USE  5       /* this stream is used internally (sliced VBI processing) */
+#define IVTV_F_S_PASSTHROUGH   6       /* this stream is in passthrough mode */
+#define IVTV_F_S_STREAMOFF     7       /* signal end of stream EOS */
+#define IVTV_F_S_APPL_IO        8      /* this stream is used read/written by an application */
+
+/* per-ivtv, i_flags */
+#define IVTV_F_I_DMA           0       /* DMA in progress */
+#define IVTV_F_I_UDMA          1       /* UDMA in progress */
+#define IVTV_F_I_UDMA_PENDING  2       /* UDMA pending */
+
+#define IVTV_F_I_SPEED_CHANGE  3       /* A speed change is in progress */
+#define IVTV_F_I_EOS           4       /* End of encoder stream reached */
+#define IVTV_F_I_RADIO_USER    5       /* The radio tuner is selected */
+#define IVTV_F_I_DIG_RST       6       /* Reset digitizer */
+#define IVTV_F_I_DEC_YUV       7       /* YUV instead of MPG is being decoded */
+#define IVTV_F_I_ENC_VBI       8       /* VBI DMA */
+#define IVTV_F_I_UPDATE_CC     9       /* CC should be updated */
+#define IVTV_F_I_UPDATE_WSS    10      /* WSS should be updated */
+#define IVTV_F_I_UPDATE_VPS    11      /* VPS should be updated */
+#define IVTV_F_I_DECODING_YUV  12      /* this stream is YUV frame decoding */
+#define IVTV_F_I_ENC_PAUSED    13      /* the encoder is paused */
+#define IVTV_F_I_VALID_DEC_TIMINGS 14  /* last_dec_timing is valid */
+
+/* Event notifications */
+#define IVTV_F_I_EV_DEC_STOPPED        28      /* decoder stopped event */
+#define IVTV_F_I_EV_VSYNC      29      /* VSYNC event */
+#define IVTV_F_I_EV_VSYNC_FIELD 30     /* VSYNC event field (0 = first, 1 = second field) */
+#define IVTV_F_I_EV_VSYNC_ENABLED 31   /* VSYNC event enabled */
+
+/* Scatter-Gather array element, used in DMA transfers */
+struct ivtv_SG_element {
+       u32 src;
+       u32 dst;
+       u32 size;
+};
+
+struct ivtv_user_dma {
+       struct mutex lock;
+       int page_count;
+       struct page *map[IVTV_DMA_SG_OSD_ENT];
+
+       /* Base Dev SG Array for cx23415/6 */
+       struct ivtv_SG_element SGarray[IVTV_DMA_SG_OSD_ENT];
+       dma_addr_t SG_handle;
+       int SG_length;
+
+       /* SG List of Buffers */
+       struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT];
+};
+
+struct ivtv_dma_page_info {
+       unsigned long uaddr;
+       unsigned long first;
+       unsigned long last;
+       unsigned int offset;
+       unsigned int tail;
+       int page_count;
+};
+
+struct ivtv_buffer {
+       struct list_head list;
+       dma_addr_t dma_handle;
+       unsigned long b_flags;
+       char *buf;
+
+       u32 bytesused;
+       u32 readpos;
+};
+
+struct ivtv_queue {
+       struct list_head list;
+       u32 buffers;
+       u32 length;
+       u32 bytesused;
+};
+
+struct ivtv;   /* forward reference */
+
+struct ivtv_stream {
+       /* These first four fields are always set, even if the stream
+          is not actually created. */
+       struct video_device *v4l2dev;   /* NULL when stream not created */
+       struct ivtv *itv;               /* for ease of use */
+       const char *name;               /* name of the stream */
+       int type;                       /* stream type */
+
+       u32 id;
+       spinlock_t qlock;       /* locks access to the queues */
+       unsigned long s_flags;  /* status flags, see above */
+       int dma;                /* can be PCI_DMA_TODEVICE,
+                                  PCI_DMA_FROMDEVICE or
+                                  PCI_DMA_NONE */
+       u32 dma_offset;
+       u32 dma_backup;
+       u64 dma_pts;
+
+       int subtype;
+       wait_queue_head_t waitq;
+       u32 dma_last_offset;
+
+       /* Buffer Stats */
+       u32 buffers;
+       u32 buf_size;
+       u32 buffers_stolen;
+
+       /* Buffer Queues */
+       struct ivtv_queue q_free;       /* free buffers */
+       struct ivtv_queue q_full;       /* full buffers */
+       struct ivtv_queue q_io;         /* waiting for I/O */
+       struct ivtv_queue q_dma;        /* waiting for DMA */
+       struct ivtv_queue q_predma;     /* waiting for DMA */
+
+       /* Base Dev SG Array for cx23415/6 */
+       struct ivtv_SG_element *SGarray;
+       dma_addr_t SG_handle;
+       int SG_length;
+
+       /* SG List of Buffers */
+       struct scatterlist *SGlist;
+};
+
+struct ivtv_open_id {
+       u32 open_id;
+       int type;
+       struct ivtv *itv;
+};
+
+#define IVTV_YUV_UPDATE_HORIZONTAL  0x01
+#define IVTV_YUV_UPDATE_VERTICAL    0x02
+
+struct yuv_frame_info
+{
+       u32 update;
+       int src_x;
+       int src_y;
+       unsigned int src_w;
+       unsigned int src_h;
+       int dst_x;
+       int dst_y;
+       unsigned int dst_w;
+       unsigned int dst_h;
+       int pan_x;
+       int pan_y;
+       u32 vis_w;
+       u32 vis_h;
+       u32 interlaced_y;
+       u32 interlaced_uv;
+       int tru_x;
+       u32 tru_w;
+       u32 tru_h;
+       u32 offset_y;
+};
+
+#define IVTV_YUV_MODE_INTERLACED       0x00
+#define IVTV_YUV_MODE_PROGRESSIVE      0x01
+#define IVTV_YUV_MODE_AUTO             0x02
+#define IVTV_YUV_MODE_MASK             0x03
+
+#define IVTV_YUV_SYNC_EVEN             0x00
+#define IVTV_YUV_SYNC_ODD              0x04
+#define IVTV_YUV_SYNC_MASK             0x04
+
+struct yuv_playback_info
+{
+       u32 reg_2834;
+       u32 reg_2838;
+       u32 reg_283c;
+       u32 reg_2840;
+       u32 reg_2844;
+       u32 reg_2848;
+       u32 reg_2854;
+       u32 reg_285c;
+       u32 reg_2864;
+
+       u32 reg_2870;
+       u32 reg_2874;
+       u32 reg_2890;
+       u32 reg_2898;
+       u32 reg_289c;
+
+       u32 reg_2918;
+       u32 reg_291c;
+       u32 reg_2920;
+       u32 reg_2924;
+       u32 reg_2928;
+       u32 reg_292c;
+       u32 reg_2930;
+
+       u32 reg_2934;
+
+       u32 reg_2938;
+       u32 reg_293c;
+       u32 reg_2940;
+       u32 reg_2944;
+       u32 reg_2948;
+       u32 reg_294c;
+       u32 reg_2950;
+       u32 reg_2954;
+       u32 reg_2958;
+       u32 reg_295c;
+       u32 reg_2960;
+       u32 reg_2964;
+       u32 reg_2968;
+       u32 reg_296c;
+
+       u32 reg_2970;
+
+       int v_filter_1;
+       int v_filter_2;
+       int h_filter;
+
+       u32 osd_x_offset;
+       u32 osd_y_offset;
+
+       u32 osd_x_pan;
+       u32 osd_y_pan;
+
+       u32 osd_vis_w;
+       u32 osd_vis_h;
+
+       int decode_height;
+
+       int frame_interlaced;
+       int frame_interlaced_last;
+
+       int lace_mode;
+       int lace_threshold;
+       int lace_threshold_last;
+       int lace_sync_field;
+
+       atomic_t next_dma_frame;
+       atomic_t next_fill_frame;
+
+       u32 yuv_forced_update;
+       int update_frame;
+       struct workqueue_struct *work_queues;
+       struct work_struct work_queue;
+       struct yuv_frame_info new_frame_info[4];
+       struct yuv_frame_info old_frame_info;
+       struct yuv_frame_info old_frame_info_args;
+
+       void *blanking_ptr;
+       dma_addr_t blanking_dmaptr;
+};
+
+#define IVTV_VBI_FRAMES 32
+
+/* VBI data */
+struct vbi_info {
+       u32 dec_start;
+       u32 enc_start, enc_size;
+       int fpi;
+       u32 frame;
+       u32 dma_offset;
+       u8 cc_data_odd[256];
+       u8 cc_data_even[256];
+       int cc_pos;
+       u8 cc_no_update;
+       u8 vps[5];
+       u8 vps_found;
+       int wss;
+       u8 wss_found;
+       u8 wss_no_update;
+       u32 raw_decoder_line_size;
+       u8 raw_decoder_sav_odd_field;
+       u8 raw_decoder_sav_even_field;
+       u32 sliced_decoder_line_size;
+       u8 sliced_decoder_sav_odd_field;
+       u8 sliced_decoder_sav_even_field;
+       struct v4l2_format in;
+       /* convenience pointer to sliced struct in vbi_in union */
+       struct v4l2_sliced_vbi_format *sliced_in;
+       u32 service_set_in;
+       u32 service_set_out;
+       int insert_mpeg;
+
+       /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines.
+          One for /dev/vbi0 and one for /dev/vbi8 */
+       struct v4l2_sliced_vbi_data sliced_data[36];
+       struct v4l2_sliced_vbi_data sliced_dec_data[36];
+
+       /* Buffer for VBI data inserted into MPEG stream.
+          The first byte is a dummy byte that's never used.
+          The next 16 bytes contain the MPEG header for the VBI data,
+          the remainder is the actual VBI data.
+          The max size accepted by the MPEG VBI reinsertion turns out
+          to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes,
+          where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is
+          a single line header byte and 2 * 18 is the number of VBI lines per frame.
+
+          However, it seems that the data must be 1K aligned, so we have to
+          pad the data until the 1 or 2 K boundary.
+
+          This pointer array will allocate 2049 bytes to store each VBI frame. */
+       u8 *sliced_mpeg_data[IVTV_VBI_FRAMES];
+       u32 sliced_mpeg_size[IVTV_VBI_FRAMES];
+       struct ivtv_buffer sliced_mpeg_buf;
+       u32 inserted_frame;
+
+       struct workqueue_struct *work_queues;
+       struct work_struct work_queue;
+       u32 start[2], count;
+       u32 raw_size;
+       u32 sliced_size;
+};
+
+/* forward declaration of struct defined in ivtv-cards.h */
+struct ivtv_card;
+
+/* Struct to hold info about ivtv cards */
+struct ivtv {
+       int num;                /* board number, -1 during init! */
+       char name[8];           /* board name for printk and interrupts (e.g. 'ivtv0') */
+       struct pci_dev *dev;    /* PCI device */
+       const struct ivtv_card *card;   /* card information */
+       const char *card_name;  /* full name of the card */
+       u8 has_cx23415;         /* 1 if it is a cx23415 based card, 0 for cx23416 */
+       u8 is_50hz;
+       u8 is_60hz;
+       u8 is_out_50hz;
+       u8 is_out_60hz;
+       u8 pvr150_workaround;   /* 1 if the cx25840 needs to workaround a PVR150 bug */
+       u8 nof_inputs;          /* number of video inputs */
+       u8 nof_audio_inputs;    /* number of audio inputs */
+       u32 v4l2_cap;           /* V4L2 capabilities of card */
+       u32 hw_flags;           /* Hardware description of the board */
+
+       /* controlling Video decoder function */
+       int (*video_dec_func)(struct ivtv *, unsigned int, void *);
+
+       struct ivtv_options options;    /* User options */
+       int stream_buf_size[IVTV_MAX_STREAMS]; /* Stream buffer size */
+       struct ivtv_stream streams[IVTV_MAX_STREAMS];   /* Stream data */
+       int speed;
+       u8 speed_mute_audio;
+       unsigned long i_flags;  /* global ivtv flags */
+       atomic_t capturing;     /* count number of active capture streams */
+       atomic_t decoding;      /* count number of active decoding streams */
+       u32 irq_rr_idx; /* Round-robin stream index */
+       int cur_dma_stream;     /* index of stream doing DMA */
+       u32 dma_data_req_offset;
+       u32 dma_data_req_size;
+       int output_mode;        /* NONE, MPG, YUV, UDMA YUV, passthrough */
+       spinlock_t lock;        /* lock access to this struct */
+       int search_pack_header;
+
+       spinlock_t dma_reg_lock; /* lock access to DMA engine registers */
+
+       /* User based DMA for OSD */
+       struct ivtv_user_dma udma;
+
+       int open_id;            /* incremented each time an open occurs, used as unique ID.
+                                  starts at 1, so 0 can be used as uninitialized value
+                                  in the stream->id. */
+
+       u32 base_addr;
+       u32 irqmask;
+       struct timer_list dma_timer; /* Timer used to catch unfinished DMAs */
+
+       struct vbi_info vbi;
+
+       struct ivtv_mailbox_data enc_mbox;
+       struct ivtv_mailbox_data dec_mbox;
+       struct ivtv_api_cache api_cache[256];   /* Cached API Commands */
+
+       u8 card_rev;
+       volatile void __iomem *enc_mem, *dec_mem, *reg_mem;
+
+       u32 pgm_info_offset;
+       u32 pgm_info_num;
+       u32 pgm_info_write_idx;
+       u32 pgm_info_read_idx;
+       struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX];
+
+       u64 mpg_data_received;
+       u64 vbi_data_inserted;
+
+       wait_queue_head_t cap_w;
+       /* when the next decoder event arrives this queue is woken up */
+       wait_queue_head_t event_waitq;
+       /* when the next decoder vsync arrives this queue is woken up */
+       wait_queue_head_t vsync_waitq;
+       /* when the current DMA is finished this queue is woken up */
+       wait_queue_head_t dma_waitq;
+
+       /* OSD support */
+       unsigned long osd_video_pbase;
+       int osd_global_alpha_state; /* 0=off : 1=on */
+       int osd_local_alpha_state;  /* 0=off : 1=on */
+       int osd_color_key_state;    /* 0=off : 1=on */
+       u8  osd_global_alpha;       /* Current global alpha */
+       u32 osd_color_key;          /* Current color key */
+       u32 osd_pixelformat;        /* Current pixel format */
+       struct v4l2_rect osd_rect;  /* Current OSD position and size */
+       struct v4l2_rect main_rect; /* Current Main window position and size */
+
+       u32 last_dec_timing[3];     /* Store last retrieved pts/scr/frame values */
+
+       /* i2c */
+       struct i2c_adapter i2c_adap;
+       struct i2c_algo_bit_data i2c_algo;
+       struct i2c_client i2c_client;
+       struct mutex i2c_bus_lock;
+       int i2c_state;
+       struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
+
+       /* v4l2 and User settings */
+
+       /* codec settings */
+       struct cx2341x_mpeg_params params;
+       u32 audio_input;
+       u32 active_input;
+       u32 active_output;
+       v4l2_std_id std;
+       v4l2_std_id std_out;
+       v4l2_std_id tuner_std;  /* The norm of the tuner (fixed) */
+       u8 audio_stereo_mode;
+       u8 audio_bilingual_mode;
+
+       /* dualwatch */
+       unsigned long dualwatch_jiffies;
+       u16 dualwatch_stereo_mode;
+
+       /* Digitizer type */
+       int digitizer;          /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */
+
+       u32 lastVsyncFrame;
+
+       struct yuv_playback_info yuv_info;
+       struct osd_info *osd_info;
+};
+
+/* Globals */
+extern struct ivtv *ivtv_cards[];
+extern int ivtv_cards_active;
+extern int ivtv_first_minor;
+extern spinlock_t ivtv_cards_lock;
+
+/*==============Prototypes==================*/
+
+/* Hardware/IRQ */
+void ivtv_set_irq_mask(struct ivtv *itv, u32 mask);
+void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask);
+
+/* try to set output mode, return current mode. */
+int ivtv_set_output_mode(struct ivtv *itv, int mode);
+
+/* return current output stream based on current mode */
+struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv);
+
+/* Return non-zero if a signal is pending */
+int ivtv_sleep_timeout(int timeout, int intr);
+
+/* Wait on queue, returns -EINTR if interrupted */
+int ivtv_waitq(wait_queue_head_t *waitq);
+
+/* Read Hauppauge eeprom */
+struct tveeprom; /* forward reference */
+void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv);
+
+/* This is a PCI post thing, where if the pci register is not read, then
+   the write doesn't always take effect right away. By reading back the
+   register any pending PCI writes will be performed (in order), and so
+   you can be sure that the writes are guaranteed to be done.
+
+   Rarely needed, only in some timing sensitive cases.
+   Apparently if this is not done some motherboards seem
+   to kill the firmware and get into the broken state until computer is
+   rebooted. */
+#define write_sync(val, reg) \
+       do { writel(val, reg); readl(reg); } while (0)
+
+#define read_reg(reg) readl(itv->reg_mem + (reg))
+#define write_reg(val, reg) writel(val, itv->reg_mem + (reg))
+#define write_reg_sync(val, reg) \
+       do { write_reg(val, reg); read_reg(reg); } while (0)
+
+#define read_enc(addr) readl(itv->enc_mem + (u32)(addr))
+#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr))
+#define write_enc_sync(val, addr) \
+       do { write_enc(val, addr); read_enc(addr); } while (0)
+
+#define read_dec(addr) readl(itv->dec_mem + (u32)(addr))
+#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr))
+#define write_dec_sync(val, addr) \
+       do { write_dec(val, addr); read_dec(addr); } while (0)
+
+#endif /* IVTV_DRIVER_H */
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
new file mode 100644 (file)
index 0000000..90e0f51
--- /dev/null
@@ -0,0 +1,918 @@
+/*
+    file operation functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-vbi.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-audio.h"
+#include "ivtv-streams.h"
+#include "ivtv-yuv.h"
+#include "ivtv-controls.h"
+#include "ivtv-ioctl.h"
+
+/* This function tries to claim the stream for a specific file descriptor.
+   If no one else is using this stream then the stream is claimed and
+   associated VBI streams are also automatically claimed.
+   Possible error returns: -EBUSY if someone else has claimed
+   the stream or 0 on success. */
+int ivtv_claim_stream(struct ivtv_open_id *id, int type)
+{
+       struct ivtv *itv = id->itv;
+       struct ivtv_stream *s = &itv->streams[type];
+       struct ivtv_stream *s_vbi;
+       int vbi_type;
+
+       if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+               /* someone already claimed this stream */
+               if (s->id == id->open_id) {
+                       /* yes, this file descriptor did. So that's OK. */
+                       return 0;
+               }
+               if (s->id == -1 && (type == IVTV_DEC_STREAM_TYPE_VBI ||
+                                        type == IVTV_ENC_STREAM_TYPE_VBI)) {
+                       /* VBI is handled already internally, now also assign
+                          the file descriptor to this stream for external
+                          reading of the stream. */
+                       s->id = id->open_id;
+                       IVTV_DEBUG_INFO("Start Read VBI\n");
+                       return 0;
+               }
+               /* someone else is using this stream already */
+               IVTV_DEBUG_INFO("Stream %d is busy\n", type);
+               return -EBUSY;
+       }
+       s->id = id->open_id;
+       if (type == IVTV_DEC_STREAM_TYPE_VBI) {
+               /* Enable reinsertion interrupt */
+               ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+       }
+
+       /* IVTV_DEC_STREAM_TYPE_MPG needs to claim IVTV_DEC_STREAM_TYPE_VBI,
+          IVTV_ENC_STREAM_TYPE_MPG needs to claim IVTV_ENC_STREAM_TYPE_VBI
+          (provided VBI insertion is on and sliced VBI is selected), for all
+          other streams we're done */
+       if (type == IVTV_DEC_STREAM_TYPE_MPG) {
+               vbi_type = IVTV_DEC_STREAM_TYPE_VBI;
+       } else if (type == IVTV_ENC_STREAM_TYPE_MPG &&
+                  itv->vbi.insert_mpeg && itv->vbi.sliced_in->service_set) {
+               vbi_type = IVTV_ENC_STREAM_TYPE_VBI;
+       } else {
+               return 0;
+       }
+       s_vbi = &itv->streams[vbi_type];
+
+       if (!test_and_set_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags)) {
+               /* Enable reinsertion interrupt */
+               if (vbi_type == IVTV_DEC_STREAM_TYPE_VBI)
+                       ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+       }
+       /* mark that it is used internally */
+       set_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags);
+       return 0;
+}
+
+/* This function releases a previously claimed stream. It will take into
+   account associated VBI streams. */
+void ivtv_release_stream(struct ivtv_stream *s)
+{
+       struct ivtv *itv = s->itv;
+       struct ivtv_stream *s_vbi;
+
+       s->id = -1;
+       if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) &&
+               test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
+               /* this stream is still in use internally */
+               return;
+       }
+       if (!test_and_clear_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+               IVTV_DEBUG_WARN("Release stream %s not in use!\n", s->name);
+               return;
+       }
+
+       ivtv_flush_queues(s);
+
+       /* disable reinsertion interrupt */
+       if (s->type == IVTV_DEC_STREAM_TYPE_VBI)
+               ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+
+       /* IVTV_DEC_STREAM_TYPE_MPG needs to release IVTV_DEC_STREAM_TYPE_VBI,
+          IVTV_ENC_STREAM_TYPE_MPG needs to release IVTV_ENC_STREAM_TYPE_VBI,
+          for all other streams we're done */
+       if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+               s_vbi = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
+       else if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+               s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+       else
+               return;
+
+       /* clear internal use flag */
+       if (!test_and_clear_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags)) {
+               /* was already cleared */
+               return;
+       }
+       if (s_vbi->id != -1) {
+               /* VBI stream still claimed by a file descriptor */
+               return;
+       }
+       /* disable reinsertion interrupt */
+       if (s_vbi->type == IVTV_DEC_STREAM_TYPE_VBI)
+               ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+       clear_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags);
+       ivtv_flush_queues(s_vbi);
+}
+
+static void ivtv_dualwatch(struct ivtv *itv)
+{
+       struct v4l2_tuner vt;
+       u16 new_bitmap;
+       u16 new_stereo_mode;
+       const u16 stereo_mask = 0x0300;
+       const u16 dual = 0x0200;
+
+       new_stereo_mode = itv->params.audio_properties & stereo_mask;
+       memset(&vt, 0, sizeof(vt));
+       ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt);
+       if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
+               new_stereo_mode = dual;
+
+       if (new_stereo_mode == itv->dualwatch_stereo_mode)
+               return;
+
+       new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask);
+
+       IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
+                          itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
+
+       if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) {
+               itv->dualwatch_stereo_mode = new_stereo_mode;
+               return;
+       }
+       IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+}
+
+static void ivtv_update_pgm_info(struct ivtv *itv)
+{
+       u32 wr_idx = (read_enc(itv->pgm_info_offset) - itv->pgm_info_offset - 4) / 24;
+       int cnt;
+       int i = 0;
+
+       if (wr_idx >= itv->pgm_info_num) {
+               IVTV_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, itv->pgm_info_num);
+               return;
+       }
+       cnt = (wr_idx + itv->pgm_info_num - itv->pgm_info_write_idx) % itv->pgm_info_num;
+       while (i < cnt) {
+               int idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
+               struct v4l2_enc_idx_entry *e = itv->pgm_info + idx;
+               u32 addr = itv->pgm_info_offset + 4 + idx * 24;
+               const int mapping[] = { V4L2_ENC_IDX_FRAME_P, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_B, 0 };
+
+               e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32);
+               if (e->offset > itv->mpg_data_received) {
+                       break;
+               }
+               e->offset += itv->vbi_data_inserted;
+               e->length = read_enc(addr);
+               e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32);
+               e->flags = mapping[read_enc(addr + 12) & 3];
+               i++;
+       }
+       itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
+}
+
+static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err)
+{
+       struct ivtv *itv = s->itv;
+       struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+       struct ivtv_buffer *buf;
+       DEFINE_WAIT(wait);
+
+       *err = 0;
+       while (1) {
+               if (s->type == IVTV_ENC_STREAM_TYPE_MPG) {
+                       /* Process pending program info updates and pending VBI data */
+                       ivtv_update_pgm_info(itv);
+
+                       if (jiffies - itv->dualwatch_jiffies > HZ) {
+                               itv->dualwatch_jiffies = jiffies;
+                               ivtv_dualwatch(itv);
+                       }
+
+                       if (test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+                           !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
+                               while ((buf = ivtv_dequeue(s_vbi, &s_vbi->q_full))) {
+                                       /* byteswap and process VBI data */
+                                       ivtv_process_vbi_data(itv, buf, s_vbi->dma_pts, s_vbi->type);
+                                       ivtv_enqueue(s_vbi, buf, &s_vbi->q_free);
+                               }
+                       }
+                       buf = &itv->vbi.sliced_mpeg_buf;
+                       if (buf->readpos != buf->bytesused) {
+                               return buf;
+                       }
+               }
+
+               /* do we have leftover data? */
+               buf = ivtv_dequeue(s, &s->q_io);
+               if (buf)
+                       return buf;
+
+               /* do we have new data? */
+               buf = ivtv_dequeue(s, &s->q_full);
+               if (buf) {
+                       if (!test_and_clear_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags))
+                               return buf;
+                       if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+                               /* byteswap MPG data */
+                               ivtv_buf_swap(buf);
+                       else if (s->type != IVTV_DEC_STREAM_TYPE_VBI) {
+                               /* byteswap and process VBI data */
+                               ivtv_process_vbi_data(itv, buf, s->dma_pts, s->type);
+                       }
+                       return buf;
+               }
+               /* return if file was opened with O_NONBLOCK */
+               if (non_block) {
+                       *err = -EAGAIN;
+                       return NULL;
+               }
+
+               /* return if end of stream */
+               if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+                       clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+                       IVTV_DEBUG_INFO("EOS %s\n", s->name);
+                       return NULL;
+               }
+
+               /* wait for more data to arrive */
+               prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+               /* New buffers might have become available before we were added to the waitqueue */
+               if (!s->q_full.buffers)
+                       schedule();
+               finish_wait(&s->waitq, &wait);
+               if (signal_pending(current)) {
+                       /* return if a signal was received */
+                       IVTV_DEBUG_INFO("User stopped %s\n", s->name);
+                       *err = -EINTR;
+                       return NULL;
+               }
+       }
+}
+
+static void ivtv_setup_sliced_vbi_buf(struct ivtv *itv)
+{
+       int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
+
+       itv->vbi.sliced_mpeg_buf.buf = itv->vbi.sliced_mpeg_data[idx];
+       itv->vbi.sliced_mpeg_buf.bytesused = itv->vbi.sliced_mpeg_size[idx];
+       itv->vbi.sliced_mpeg_buf.readpos = 0;
+}
+
+static size_t ivtv_copy_buf_to_user(struct ivtv_stream *s, struct ivtv_buffer *buf,
+               char __user *ubuf, size_t ucount)
+{
+       struct ivtv *itv = s->itv;
+       size_t len = buf->bytesused - buf->readpos;
+
+       if (len > ucount) len = ucount;
+       if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+           itv->vbi.sliced_in->service_set && buf != &itv->vbi.sliced_mpeg_buf) {
+               const char *start = buf->buf + buf->readpos;
+               const char *p = start + 1;
+               const u8 *q;
+               u8 ch = itv->search_pack_header ? 0xba : 0xe0;
+               int stuffing, i;
+
+               while (start + len > p && (q = memchr(p, 0, start + len - p))) {
+                       p = q + 1;
+                       if ((char *)q + 15 >= buf->buf + buf->bytesused ||
+                           q[1] != 0 || q[2] != 1 || q[3] != ch) {
+                               continue;
+                       }
+                       if (!itv->search_pack_header) {
+                               if ((q[6] & 0xc0) != 0x80)
+                                       continue;
+                               if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) ||
+                                   ((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) {
+                                       ch = 0xba;
+                                       itv->search_pack_header = 1;
+                                       p = q + 9;
+                               }
+                               continue;
+                       }
+                       stuffing = q[13] & 7;
+                       /* all stuffing bytes must be 0xff */
+                       for (i = 0; i < stuffing; i++)
+                               if (q[14 + i] != 0xff)
+                                       break;
+                       if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 &&
+                                       q[14 + stuffing] == 0 && q[15 + stuffing] == 0 &&
+                                       q[16 + stuffing] == 1) {
+                               itv->search_pack_header = 0;
+                               len = (char *)q - start;
+                               ivtv_setup_sliced_vbi_buf(itv);
+                               break;
+                       }
+               }
+       }
+       if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
+               IVTV_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name);
+               return -EFAULT;
+       }
+       /*IVTV_INFO("copied %lld %d %d %d %d %d vbi %d\n", itv->mpg_data_received, len, ucount,
+                       buf->readpos, buf->bytesused, buf->bytesused - buf->readpos - len,
+                       buf == &itv->vbi.sliced_mpeg_buf); */
+       buf->readpos += len;
+       if (s->type == IVTV_ENC_STREAM_TYPE_MPG && buf != &itv->vbi.sliced_mpeg_buf)
+               itv->mpg_data_received += len;
+       return len;
+}
+
+static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_count, int non_block)
+{
+       struct ivtv *itv = s->itv;
+       size_t tot_written = 0;
+       int single_frame = 0;
+
+       if (atomic_read(&itv->capturing) == 0 && s->id == -1) {
+               /* shouldn't happen */
+               IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name);
+               return -EIO;
+       }
+
+       /* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should
+          arrive one-by-one, so make sure we never output more than one VBI frame at a time */
+       if (s->type == IVTV_DEC_STREAM_TYPE_VBI ||
+                       (s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set))
+               single_frame = 1;
+
+       for (;;) {
+               struct ivtv_buffer *buf;
+               int rc;
+
+               buf = ivtv_get_buffer(s, non_block, &rc);
+               if (buf == NULL && rc == -EAGAIN && tot_written)
+                       break;
+               if (buf == NULL)
+                       return rc;
+               rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written);
+               if (buf != &itv->vbi.sliced_mpeg_buf) {
+                       ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io);
+               }
+               else if (buf->readpos == buf->bytesused) {
+                       int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
+                       itv->vbi.sliced_mpeg_size[idx] = 0;
+                       itv->vbi.inserted_frame++;
+                       itv->vbi_data_inserted += buf->bytesused;
+               }
+               if (rc < 0)
+                       return rc;
+               tot_written += rc;
+
+               if (tot_written == tot_count || single_frame)
+                       break;
+       }
+       return tot_written;
+}
+
+static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t count,
+                       loff_t *pos, int non_block)
+{
+       ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0;
+       struct ivtv *itv = s->itv;
+
+       IVTV_DEBUG_INFO("read %zd from %s, got %zd\n", count, s->name, rc);
+       if (rc > 0)
+               pos += rc;
+       return rc;
+}
+
+int ivtv_start_capture(struct ivtv_open_id *id)
+{
+       struct ivtv *itv = id->itv;
+       struct ivtv_stream *s = &itv->streams[id->type];
+       struct ivtv_stream *s_vbi;
+
+       if (s->type == IVTV_ENC_STREAM_TYPE_RAD ||
+           s->type == IVTV_DEC_STREAM_TYPE_MPG ||
+           s->type == IVTV_DEC_STREAM_TYPE_YUV ||
+           s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+               /* you cannot read from these stream types. */
+               return -EPERM;
+       }
+
+       /* Try to claim this stream. */
+       if (ivtv_claim_stream(id, s->type))
+               return -EBUSY;
+
+       /* This stream does not need to start capturing */
+       if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+               set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+               return 0;
+       }
+
+       /* If capture is already in progress, then we also have to
+          do nothing extra. */
+       if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+               set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+               return 0;
+       }
+
+       /* Start VBI capture if required */
+       s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+       if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+           test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+           !test_and_set_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
+               /* Note: the IVTV_ENC_STREAM_TYPE_VBI is claimed
+                  automatically when the MPG stream is claimed.
+                  We only need to start the VBI capturing. */
+               if (ivtv_start_v4l2_encode_stream(s_vbi)) {
+                       IVTV_DEBUG_WARN("VBI capture start failed\n");
+
+                       /* Failure, clean up and return an error */
+                       clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
+                       clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+                       /* also releases the associated VBI stream */
+                       ivtv_release_stream(s);
+                       return -EIO;
+               }
+               IVTV_DEBUG_INFO("VBI insertion started\n");
+       }
+
+       /* Tell the card to start capturing */
+       if (!ivtv_start_v4l2_encode_stream(s)) {
+               /* We're done */
+               set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+               /* Resume a possibly paused encoder */
+               if (test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+                       ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
+               return 0;
+       }
+
+       /* failure, clean up */
+       IVTV_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
+
+       /* Note: the IVTV_ENC_STREAM_TYPE_VBI is released
+          automatically when the MPG stream is released.
+          We only need to stop the VBI capturing. */
+       if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+           test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
+               ivtv_stop_v4l2_encode_stream(s_vbi, 0);
+               clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
+       }
+       clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+       ivtv_release_stream(s);
+       return -EIO;
+}
+
+ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos)
+{
+       struct ivtv_open_id *id = filp->private_data;
+       struct ivtv *itv = id->itv;
+       struct ivtv_stream *s = &itv->streams[id->type];
+       int rc;
+
+       IVTV_DEBUG_IOCTL("read %zd bytes from %s\n", count, s->name);
+
+       rc = ivtv_start_capture(id);
+       if (rc)
+               return rc;
+       return ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
+}
+
+int ivtv_start_decoding(struct ivtv_open_id *id, int speed)
+{
+       struct ivtv *itv = id->itv;
+       struct ivtv_stream *s = &itv->streams[id->type];
+
+       if (atomic_read(&itv->decoding) == 0) {
+               if (ivtv_claim_stream(id, s->type)) {
+                       /* someone else is using this stream already */
+                       IVTV_DEBUG_WARN("start decode, stream already claimed\n");
+                       return -EBUSY;
+               }
+               ivtv_start_v4l2_decode_stream(s, 0);
+       }
+       if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+               return ivtv_set_speed(itv, speed);
+       return 0;
+}
+
+ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos)
+{
+       struct ivtv_open_id *id = filp->private_data;
+       struct ivtv *itv = id->itv;
+       struct ivtv_stream *s = &itv->streams[id->type];
+       struct ivtv_buffer *buf;
+       struct ivtv_queue q;
+       int bytes_written = 0;
+       int mode;
+       int rc;
+       DEFINE_WAIT(wait);
+
+       IVTV_DEBUG_IOCTL("write %zd bytes to %s\n", count, s->name);
+
+       if (s->type != IVTV_DEC_STREAM_TYPE_MPG &&
+           s->type != IVTV_DEC_STREAM_TYPE_YUV &&
+           s->type != IVTV_DEC_STREAM_TYPE_VOUT)
+               /* not decoder streams */
+               return -EPERM;
+
+       /* Try to claim this stream */
+       if (ivtv_claim_stream(id, s->type))
+               return -EBUSY;
+
+       /* This stream does not need to start any decoding */
+       if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+               set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+               return ivtv_write_vbi(itv, user_buf, count);
+       }
+
+       mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV;
+
+       if (ivtv_set_output_mode(itv, mode) != mode) {
+           ivtv_release_stream(s);
+           return -EBUSY;
+       }
+       ivtv_queue_init(&q);
+       set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+
+retry:
+       for (;;) {
+               /* Gather buffers */
+               while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io)))
+                       ivtv_enqueue(s, buf, &q);
+               while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_free))) {
+                       ivtv_enqueue(s, buf, &q);
+               }
+               if (q.buffers)
+                       break;
+               if (filp->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+               prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+               /* New buffers might have become free before we were added to the waitqueue */
+               if (!s->q_free.buffers)
+                       schedule();
+               finish_wait(&s->waitq, &wait);
+               if (signal_pending(current)) {
+                       IVTV_DEBUG_INFO("User stopped %s\n", s->name);
+                       return -EINTR;
+               }
+       }
+
+       /* copy user data into buffers */
+       while ((buf = ivtv_dequeue(s, &q))) {
+               /* Make sure we really got all the user data */
+               rc = ivtv_buf_copy_from_user(s, buf, user_buf, count);
+
+               if (rc < 0) {
+                       ivtv_queue_move(s, &q, NULL, &s->q_free, 0);
+                       return rc;
+               }
+               user_buf += rc;
+               count -= rc;
+               bytes_written += rc;
+
+               if (buf->bytesused != s->buf_size) {
+                       /* incomplete, leave in q_io for next time */
+                       ivtv_enqueue(s, buf, &s->q_io);
+                       break;
+               }
+               /* Byteswap MPEG buffer */
+               if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+                       ivtv_buf_swap(buf);
+               ivtv_enqueue(s, buf, &s->q_full);
+       }
+
+       /* Start decoder (returns 0 if already started) */
+       rc = ivtv_start_decoding(id, itv->speed);
+       if (rc) {
+               IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name);
+
+               /* failure, clean up */
+               clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+               clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+               return rc;
+       }
+       if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) {
+               if (s->q_full.length >= itv->dma_data_req_size) {
+                       int got_sig;
+
+                       prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+                       while (!(got_sig = signal_pending(current)) &&
+                                       test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) {
+                               schedule();
+                       }
+                       finish_wait(&itv->dma_waitq, &wait);
+                       if (got_sig) {
+                               IVTV_DEBUG_INFO("User interrupted %s\n", s->name);
+                               return -EINTR;
+                       }
+
+                       clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+                       ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
+                       ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 1);
+               }
+       }
+       /* more user data is available, wait until buffers become free
+          to transfer the rest. */
+       if (count && !(filp->f_flags & O_NONBLOCK))
+               goto retry;
+       IVTV_DEBUG_INFO("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
+       return bytes_written;
+}
+
+unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait)
+{
+       struct ivtv_open_id *id = filp->private_data;
+       struct ivtv *itv = id->itv;
+       struct ivtv_stream *s = &itv->streams[id->type];
+       int res = 0;
+
+       /* add stream's waitq to the poll list */
+       poll_wait(filp, &s->waitq, wait);
+
+       set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+       if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) ||
+           test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
+               res = POLLPRI;
+
+       /* Allow write if buffers are available for writing */
+       if (s->q_free.buffers)
+               res |= POLLOUT | POLLWRNORM;
+       return res;
+}
+
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait)
+{
+       struct ivtv_open_id *id = filp->private_data;
+       struct ivtv *itv = id->itv;
+       struct ivtv_stream *s = &itv->streams[id->type];
+       int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+       /* Start a capture if there is none */
+       if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+               int rc = ivtv_start_capture(id);
+
+               if (rc) {
+                       IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n",
+                                       s->name, rc);
+                       return POLLERR;
+               }
+       }
+
+       /* add stream's waitq to the poll list */
+       poll_wait(filp, &s->waitq, wait);
+
+       if (eof || s->q_full.length)
+               return POLLIN | POLLRDNORM;
+       return 0;
+}
+
+void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end)
+{
+       struct ivtv *itv = id->itv;
+       struct ivtv_stream *s = &itv->streams[id->type];
+
+       IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
+
+       /* 'Unclaim' this stream */
+
+       /* Stop capturing */
+       if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+               struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+
+               IVTV_DEBUG_INFO("close stopping capture\n");
+               /* Special case: a running VBI capture for VBI insertion
+                  in the mpeg stream. Need to stop that too. */
+               if (id->type == IVTV_ENC_STREAM_TYPE_MPG &&
+                   test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags) &&
+                   !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
+                       IVTV_DEBUG_INFO("close stopping embedded VBI capture\n");
+                       ivtv_stop_v4l2_encode_stream(s_vbi, 0);
+               }
+               if ((id->type == IVTV_DEC_STREAM_TYPE_VBI ||
+                    id->type == IVTV_ENC_STREAM_TYPE_VBI) &&
+                   test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
+                       /* Also used internally, don't stop capturing */
+                       s->id = -1;
+               }
+               else {
+                       ivtv_stop_v4l2_encode_stream(s, gop_end);
+               }
+       }
+       clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+       clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+       ivtv_release_stream(s);
+}
+
+void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts)
+{
+       struct ivtv *itv = id->itv;
+       struct ivtv_stream *s = &itv->streams[id->type];
+
+       IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
+
+       /* Stop decoding */
+       if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+               IVTV_DEBUG_INFO("close stopping decode\n");
+
+               ivtv_stop_v4l2_decode_stream(s, flags, pts);
+       }
+       clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+       clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+       if (id->type == IVTV_DEC_STREAM_TYPE_YUV && test_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags)) {
+               /* Restore registers we've changed & clean up any mess we've made */
+               ivtv_yuv_close(itv);
+       }
+       if (s->type == IVTV_DEC_STREAM_TYPE_YUV && itv->output_mode == OUT_YUV)
+           itv->output_mode = OUT_NONE;
+       else if (s->type == IVTV_DEC_STREAM_TYPE_MPG && itv->output_mode == OUT_MPG)
+           itv->output_mode = OUT_NONE;
+
+       itv->speed = 0;
+       ivtv_release_stream(s);
+}
+
+int ivtv_v4l2_close(struct inode *inode, struct file *filp)
+{
+       struct ivtv_open_id *id = filp->private_data;
+       struct ivtv *itv = id->itv;
+       struct ivtv_stream *s = &itv->streams[id->type];
+
+       IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
+
+       /* Easy case first: this stream was never claimed by us */
+       if (s->id != id->open_id) {
+               kfree(id);
+               return 0;
+       }
+
+       /* 'Unclaim' this stream */
+
+       /* Stop radio */
+       if (id->type == IVTV_ENC_STREAM_TYPE_RAD) {
+               /* Closing radio device, return to TV mode */
+               ivtv_mute(itv);
+               /* Mark that the radio is no longer in use */
+               clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
+               /* Switch tuner to TV */
+               ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+               /* Select correct audio input (i.e. TV tuner or Line in) */
+               ivtv_audio_set_io(itv);
+               /* Done! Unmute and continue. */
+               ivtv_unmute(itv);
+               ivtv_release_stream(s);
+       } else if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
+               ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0);
+       } else {
+               ivtv_stop_capture(id, 0);
+       }
+       kfree(id);
+       return 0;
+}
+
+int ivtv_v4l2_open(struct inode *inode, struct file *filp)
+{
+       int x, y = 0;
+       struct ivtv_open_id *item;
+       struct ivtv *itv = NULL;
+       struct ivtv_stream *s = NULL;
+       int minor = MINOR(inode->i_rdev);
+
+       /* Find which card this open was on */
+       spin_lock(&ivtv_cards_lock);
+       for (x = 0; itv == NULL && x < ivtv_cards_active; x++) {
+               /* find out which stream this open was on */
+               for (y = 0; y < IVTV_MAX_STREAMS; y++) {
+                       s = &ivtv_cards[x]->streams[y];
+                       if (s->v4l2dev && s->v4l2dev->minor == minor) {
+                               itv = ivtv_cards[x];
+                               break;
+                       }
+               }
+       }
+       spin_unlock(&ivtv_cards_lock);
+
+       if (itv == NULL) {
+               /* Couldn't find a device registered
+                  on that minor, shouldn't happen! */
+               printk(KERN_WARNING "ivtv: no ivtv device found on minor %d\n", minor);
+               return -ENXIO;
+       }
+
+       if (y == IVTV_DEC_STREAM_TYPE_MPG &&
+               test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags))
+               return -EBUSY;
+
+       if (y == IVTV_DEC_STREAM_TYPE_YUV &&
+               test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags))
+               return -EBUSY;
+
+       if (y == IVTV_DEC_STREAM_TYPE_YUV) {
+               if (read_reg(0x82c) == 0) {
+                       IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n");
+                       /* return -ENODEV; */
+               }
+               ivtv_udma_alloc(itv);
+       }
+
+       /* Allocate memory */
+       item = kmalloc(sizeof(struct ivtv_open_id), GFP_KERNEL);
+       if (NULL == item) {
+               IVTV_DEBUG_WARN("nomem on v4l2 open\n");
+               return -ENOMEM;
+       }
+       item->itv = itv;
+       item->type = y;
+
+       item->open_id = itv->open_id++;
+       filp->private_data = item;
+
+       if (item->type == IVTV_ENC_STREAM_TYPE_RAD) {
+               /* Try to claim this stream */
+               if (ivtv_claim_stream(item, item->type)) {
+                       /* No, it's already in use */
+                       kfree(item);
+                       return -EBUSY;
+               }
+
+               /* We have the radio */
+               ivtv_mute(itv);
+               /* Switch tuner to radio */
+               ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL);
+               /* Mark that the radio is being used. */
+               set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
+               /* Select the correct audio input (i.e. radio tuner) */
+               ivtv_audio_set_io(itv);
+               /* Done! Unmute and continue. */
+               ivtv_unmute(itv);
+       }
+
+       /* YUV or MPG Decoding Mode? */
+       if (y == IVTV_DEC_STREAM_TYPE_MPG)
+               clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
+       else if (y == IVTV_DEC_STREAM_TYPE_YUV)
+       {
+               set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
+       }
+
+       return 0;
+}
+
+void ivtv_mute(struct ivtv *itv)
+{
+       struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 1 };
+
+       /* Mute sound to avoid pop */
+       ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl);
+
+       if (atomic_read(&itv->capturing))
+               ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1);
+
+       IVTV_DEBUG_INFO("Mute\n");
+}
+
+void ivtv_unmute(struct ivtv *itv)
+{
+       struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 0 };
+
+       /* initialize or refresh input */
+       if (atomic_read(&itv->capturing) == 0)
+               ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
+
+       ivtv_sleep_timeout(HZ / 10, 0);
+
+       if (atomic_read(&itv->capturing)) {
+               ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
+               ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0);
+       }
+
+       /* Unmute */
+       ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl);
+       IVTV_DEBUG_INFO("Unmute\n");
+}
diff --git a/drivers/media/video/ivtv/ivtv-fileops.h b/drivers/media/video/ivtv/ivtv-fileops.h
new file mode 100644 (file)
index 0000000..1afa950
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+    file operation functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Testing/Debugging */
+int ivtv_v4l2_open(struct inode *inode, struct file *filp);
+ssize_t ivtv_v4l2_read(struct file *filp, char __user *buf, size_t count,
+                     loff_t * pos);
+ssize_t ivtv_v4l2_write(struct file *filp, const char __user *buf, size_t count,
+                      loff_t * pos);
+int ivtv_v4l2_close(struct inode *inode, struct file *filp);
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait);
+unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table * wait);
+int ivtv_start_capture(struct ivtv_open_id *id);
+void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end);
+int ivtv_start_decoding(struct ivtv_open_id *id, int speed);
+void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts);
+void ivtv_mute(struct ivtv *itv);
+void ivtv_unmute(struct ivtv *itv);
+
+/* Utilities */
+
+/* Try to claim a stream for the filehandle. Return 0 on success,
+   -EBUSY if stream already claimed. Once a stream is claimed, it
+   remains claimed until the associated filehandle is closed. */
+int ivtv_claim_stream(struct ivtv_open_id *id, int type);
+
+/* Release a previously claimed stream. */
+void ivtv_release_stream(struct ivtv_stream *s);
diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c
new file mode 100644 (file)
index 0000000..d4c910b
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+    ivtv firmware functions.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-firmware.h"
+#include <linux/firmware.h>
+
+#define IVTV_MASK_SPU_ENABLE           0xFFFFFFFE
+#define IVTV_MASK_VPU_ENABLE15                 0xFFFFFFF6
+#define IVTV_MASK_VPU_ENABLE16                 0xFFFFFFFB
+#define IVTV_CMD_VDM_STOP              0x00000000
+#define IVTV_CMD_AO_STOP               0x00000005
+#define IVTV_CMD_APU_PING              0x00000000
+#define IVTV_CMD_VPU_STOP15            0xFFFFFFFE
+#define IVTV_CMD_VPU_STOP16            0xFFFFFFEE
+#define IVTV_CMD_HW_BLOCKS_RST                 0xFFFFFFFF
+#define IVTV_CMD_SPU_STOP              0x00000001
+#define IVTV_CMD_SDRAM_PRECHARGE_INIT  0x0000001A
+#define IVTV_CMD_SDRAM_REFRESH_INIT    0x80000640
+#define IVTV_SDRAM_SLEEPTIME           (60 * HZ / 100) /* 600 ms */
+
+#define IVTV_DECODE_INIT_MPEG_FILENAME         "v4l-cx2341x-init.mpg"
+#define IVTV_DECODE_INIT_MPEG_SIZE     (152*1024)
+
+/* Encoder/decoder firmware sizes */
+#define IVTV_FW_ENC_SIZE               (376836)
+#define IVTV_FW_DEC_SIZE               (256*1024)
+
+static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size)
+{
+       const struct firmware *fw = NULL;
+       int retries = 3;
+
+retry:
+       if (retries && request_firmware(&fw, fn, &itv->dev->dev) == 0) {
+               int i;
+               volatile u32 __iomem *dst = (volatile u32 __iomem *)mem;
+               const u32 *src = (const u32 *)fw->data;
+
+               /* temporarily allow 256 KB encoding firmwares as well for
+                  compatibility with blackbird cards */
+               if (fw->size != size && fw->size != 256 * 1024) {
+                       /* Due to race conditions in firmware loading (esp. with udev <0.95)
+                          the wrong file was sometimes loaded. So we check filesizes to
+                          see if at least the right-sized file was loaded. If not, then we
+                          retry. */
+                       IVTV_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size);
+                       release_firmware(fw);
+                       retries--;
+                       goto retry;
+               }
+               for (i = 0; i < fw->size; i += 4) {
+                       /* no need for endianness conversion on the ppc */
+                       __raw_writel(*src, dst);
+                       dst++;
+                       src++;
+               }
+               release_firmware(fw);
+               IVTV_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
+               return size;
+       }
+       IVTV_ERR("unable to open firmware %s (must be %ld bytes)\n", fn, size);
+       IVTV_ERR("did you put the firmware in the hotplug firmware directory?\n");
+       return -ENOMEM;
+}
+
+void ivtv_halt_firmware(struct ivtv *itv)
+{
+       IVTV_DEBUG_INFO("Preparing for firmware halt.\n");
+       if (itv->has_cx23415 && itv->dec_mbox.mbox)
+               ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0);
+       if (itv->enc_mbox.mbox)
+               ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0);
+
+       ivtv_sleep_timeout(HZ / 100, 0);
+       itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL;
+
+       IVTV_DEBUG_INFO("Stopping VDM\n");
+       write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM);
+
+       IVTV_DEBUG_INFO("Stopping AO\n");
+       write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO);
+
+       IVTV_DEBUG_INFO("pinging (?) APU\n");
+       write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU);
+
+       IVTV_DEBUG_INFO("Stopping VPU\n");
+       if (!itv->has_cx23415)
+               write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU);
+       else
+               write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU);
+
+       IVTV_DEBUG_INFO("Resetting Hw Blocks\n");
+       write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS);
+
+       IVTV_DEBUG_INFO("Stopping SPU\n");
+       write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU);
+
+       ivtv_sleep_timeout(HZ / 100, 0);
+
+       IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n");
+       write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE);
+
+       IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n");
+       write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH);
+
+       if (itv->has_cx23415) {
+               IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n");
+               write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE);
+
+               IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n");
+               write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH);
+       }
+
+       IVTV_DEBUG_INFO("Sleeping for %dms (600 recommended)\n",
+                  (int)(IVTV_SDRAM_SLEEPTIME * 1000 / HZ));
+       ivtv_sleep_timeout(IVTV_SDRAM_SLEEPTIME, 0);
+}
+
+void ivtv_firmware_versions(struct ivtv *itv)
+{
+       u32 data[CX2341X_MBOX_MAX_DATA];
+
+       /* Encoder */
+       ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0);
+       IVTV_INFO("Encoder revision: 0x%08x\n", data[0]);
+
+       if (data[0] != 0x02060039)
+               IVTV_WARN("Recommended firmware version is 0x02060039.\n");
+
+       if (itv->has_cx23415) {
+               /* Decoder */
+               ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0);
+               IVTV_INFO("Decoder revision: 0x%08x\n", data[0]);
+       }
+}
+
+static int ivtv_firmware_copy(struct ivtv *itv)
+{
+       IVTV_DEBUG_INFO("Loading encoder image\n");
+       if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME,
+                  itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) {
+               IVTV_DEBUG_WARN("failed loading encoder firmware\n");
+               return -3;
+       }
+       if (!itv->has_cx23415)
+               return 0;
+
+       IVTV_DEBUG_INFO("Loading decoder image\n");
+       if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME,
+                  itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) {
+               IVTV_DEBUG_WARN("failed loading decoder firmware\n");
+               return -1;
+       }
+       return 0;
+}
+
+static volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size)
+{
+       int i;
+
+       /* mailbox is preceeded by a 16 byte 'magic cookie' starting at a 256-byte
+          address boundary */
+       for (i = 0; i < size; i += 0x100) {
+               if (readl(mem + i)      == 0x12345678 &&
+                   readl(mem + i + 4)  == 0x34567812 &&
+                   readl(mem + i + 8)  == 0x56781234 &&
+                   readl(mem + i + 12) == 0x78123456) {
+                       return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16);
+               }
+       }
+       return NULL;
+}
+
+int ivtv_firmware_init(struct ivtv *itv)
+{
+       int err;
+
+       ivtv_halt_firmware(itv);
+
+       /* load firmware */
+       err = ivtv_firmware_copy(itv);
+       if (err) {
+               IVTV_DEBUG_WARN("Error %d loading firmware\n", err);
+               return err;
+       }
+
+       /* start firmware */
+       write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU);
+       ivtv_sleep_timeout(HZ / 10, 0);
+       if (itv->has_cx23415)
+               write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU);
+       else
+               write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU);
+       ivtv_sleep_timeout(HZ / 10, 0);
+
+       /* find mailboxes and ping firmware */
+       itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE);
+       if (itv->enc_mbox.mbox == NULL)
+               IVTV_ERR("Encoder mailbox not found\n");
+       else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) {
+               IVTV_ERR("Encoder firmware dead!\n");
+               itv->enc_mbox.mbox = NULL;
+       }
+       if (itv->enc_mbox.mbox == NULL)
+               return -ENODEV;
+
+       if (!itv->has_cx23415)
+               return 0;
+
+       itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE);
+       if (itv->dec_mbox.mbox == NULL)
+               IVTV_ERR("Decoder mailbox not found\n");
+       else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) {
+               IVTV_ERR("Decoder firmware dead!\n");
+               itv->dec_mbox.mbox = NULL;
+       }
+       return itv->dec_mbox.mbox ? 0 : -ENODEV;
+}
+
+void ivtv_init_mpeg_decoder(struct ivtv *itv)
+{
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       long readbytes;
+       volatile u8 __iomem *mem_offset;
+
+       data[0] = 0;
+       data[1] = itv->params.width;    /* YUV source width */
+       data[2] = itv->params.height;
+       data[3] = itv->params.audio_properties; /* Audio settings to use,
+                                                          bitmap. see docs. */
+       if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
+               IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");
+               return;
+       }
+
+       if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) {
+               IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n");
+               return;
+       }
+       ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
+       mem_offset = itv->dec_mem + data[1];
+
+       if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME,
+               mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) {
+               IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n",
+                               IVTV_DECODE_INIT_MPEG_FILENAME);
+       } else {
+               ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0);
+               ivtv_sleep_timeout(HZ / 10, 0);
+       }
+       ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1);
+}
diff --git a/drivers/media/video/ivtv/ivtv-firmware.h b/drivers/media/video/ivtv/ivtv-firmware.h
new file mode 100644 (file)
index 0000000..8b2ffe6
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+    ivtv firmware functions.
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+int ivtv_firmware_init(struct ivtv *itv);
+void ivtv_firmware_versions(struct ivtv *itv);
+void ivtv_halt_firmware(struct ivtv *itv);
+void ivtv_init_mpeg_decoder(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
new file mode 100644 (file)
index 0000000..bc8f8ca
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+    gpio functions.
+    Merging GPIO support into driver:
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-gpio.h"
+#include <media/tuner.h>
+
+/*
+ * GPIO assignment of Yuan MPG600/MPG160
+ *
+ *    bit 15  14  13  12 |  11  10   9   8 |   7   6   5   4 |   3   2   1   0
+ * OUTPUT         IN1 IN0                                       AM3 AM2 AM1 AM0
+ *  INPUT                   DM1         DM0
+ *
+ *   IN* : Input selection
+ *          IN1 IN0
+ *           1   1  N/A
+ *           1   0  Line
+ *           0   1  N/A
+ *           0   0  Tuner
+ *
+ *   AM* : Audio Mode
+ *          AM3  0: Normal        1: Mixed(Sub+Main channel)
+ *          AM2  0: Subchannel    1: Main channel
+ *          AM1  0: Stereo        1: Mono
+ *          AM0  0: Normal        1: Mute
+ *
+ *   DM* : Detected tuner audio Mode
+ *          DM1  0: Stereo        1: Mono
+ *          DM0  0: Multiplex     1: Normal
+ *
+ * GPIO Initial Settings
+ *           MPG600   MPG160
+ *     DIR   0x3080   0x7080
+ *  OUTPUT   0x000C   0x400C
+ *
+ *  Special thanks to Makoto Iguchi <iguchi@tahoo.org> and Mr. Anonymous
+ *  for analyzing GPIO of MPG160.
+ *
+ *****************************************************************************
+ *
+ * GPIO assignment of Avermedia M179 (per information direct from AVerMedia)
+ *
+ *    bit 15  14  13  12 |  11  10   9   8 |   7   6   5   4 |   3   2   1   0
+ * OUTPUT IN0 AM0 IN1               AM1 AM2       IN2     BR0   BR1
+ *  INPUT
+ *
+ *   IN* : Input selection
+ *          IN0 IN1 IN2
+ *           *   1   *  Mute
+ *           0   0   0  Line-In
+ *           1   0   0  TV Tuner Audio
+ *           0   0   1  FM Audio
+ *           1   0   1  Mute
+ *
+ *   AM* : Audio Mode
+ *          AM0 AM1 AM2
+ *           0   0   0  TV Tuner Audio: L_OUT=(L+R)/2, R_OUT=SAP
+ *           0   0   1  TV Tuner Audio: L_OUT=R_OUT=SAP   (SAP)
+ *           0   1   0  TV Tuner Audio: L_OUT=L, R_OUT=R   (stereo)
+ *           0   1   1  TV Tuner Audio: mute
+ *           1   *   *  TV Tuner Audio: L_OUT=R_OUT=(L+R)/2   (mono)
+ *
+ *   BR* : Audio Sample Rate (BR stands for bitrate for some reason)
+ *          BR0 BR1
+ *           0   0   32 kHz
+ *           0   1   44.1 kHz
+ *           1   0   48 kHz
+ *
+ *   DM* : Detected tuner audio Mode
+ *         Unknown currently
+ *
+ * Special thanks to AVerMedia Technologies, Inc. and Jiun-Kuei Jung at
+ * AVerMedia for providing the GPIO information used to add support
+ * for the M179 cards.
+ */
+
+/********************* GPIO stuffs *********************/
+
+/* GPIO registers */
+#define IVTV_REG_GPIO_IN    0x9008
+#define IVTV_REG_GPIO_OUT   0x900c
+#define IVTV_REG_GPIO_DIR   0x9020
+
+void ivtv_reset_ir_gpio(struct ivtv *itv)
+{
+       int curdir, curout;
+
+       if (itv->card->type != IVTV_CARD_PVR_150)
+               return;
+       IVTV_DEBUG_INFO("Resetting PVR150 IR\n");
+       curout = read_reg(IVTV_REG_GPIO_OUT);
+       curdir = read_reg(IVTV_REG_GPIO_DIR);
+       curdir |= 0x80;
+       write_reg(curdir, IVTV_REG_GPIO_DIR);
+       curout = (curout & ~0xF) | 1;
+       write_reg(curout, IVTV_REG_GPIO_OUT);
+       /* We could use something else for smaller time */
+       current->state = TASK_INTERRUPTIBLE;
+       schedule_timeout(1);
+       curout |= 2;
+       write_reg(curout, IVTV_REG_GPIO_OUT);
+       curdir &= ~0x80;
+       write_reg(curdir, IVTV_REG_GPIO_DIR);
+}
+
+#ifdef HAVE_XC3028
+int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr)
+{
+       int curdir, curout;
+       struct ivtv *itv = (struct ivtv *) priv;
+
+       if (itv->card->type != IVTV_CARD_PG600V2 || itv->options.tuner != TUNER_XCEIVE_XC3028)
+               return -EINVAL;
+       IVTV_INFO("Resetting tuner.\n");
+       curout = read_reg(IVTV_REG_GPIO_OUT);
+       curdir = read_reg(IVTV_REG_GPIO_DIR);
+       curdir |= (1 << 12);  /* GPIO bit 12 */
+
+       curout &= ~(1 << 12);
+       write_reg(curout, IVTV_REG_GPIO_OUT);
+       current->state = TASK_INTERRUPTIBLE;
+       schedule_timeout(1);
+
+       curout |= (1 << 12);
+       write_reg(curout, IVTV_REG_GPIO_OUT);
+       current->state = TASK_INTERRUPTIBLE;
+       schedule_timeout(1);
+
+       return 0;
+}
+#endif
+
+void ivtv_gpio_init(struct ivtv *itv)
+{
+       if (itv->card->gpio_init.direction == 0)
+               return;
+
+       IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
+                  read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
+
+       /* init output data then direction */
+       write_reg(itv->card->gpio_init.initial_value, IVTV_REG_GPIO_OUT);
+       write_reg(itv->card->gpio_init.direction, IVTV_REG_GPIO_DIR);
+}
+
+static struct v4l2_queryctrl gpio_ctrl_mute = {
+       .id            = V4L2_CID_AUDIO_MUTE,
+       .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       .name          = "Mute",
+       .minimum       = 0,
+       .maximum       = 1,
+       .step          = 1,
+       .default_value = 1,
+       .flags         = 0,
+};
+
+int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg)
+{
+       struct v4l2_tuner *tuner = arg;
+       struct v4l2_control *ctrl = arg;
+       struct v4l2_routing *route = arg;
+       u16 mask, data;
+
+       switch (command) {
+       case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+               mask = itv->card->gpio_audio_freq.mask;
+               switch (*(u32 *)arg) {
+               case 32000:
+                       data = itv->card->gpio_audio_freq.f32000;
+                       break;
+               case 44100:
+                       data = itv->card->gpio_audio_freq.f44100;
+                       break;
+               case 48000:
+               default:
+                       data = itv->card->gpio_audio_freq.f48000;
+                       break;
+               }
+               break;
+
+       case VIDIOC_G_TUNER:
+               mask = itv->card->gpio_audio_detect.mask;
+               if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
+                       tuner->rxsubchans = V4L2_TUNER_MODE_STEREO |
+                              V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+               else
+                       tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
+               return 0;
+
+       case VIDIOC_S_TUNER:
+               mask = itv->card->gpio_audio_mode.mask;
+               switch (tuner->audmode) {
+               case V4L2_TUNER_MODE_LANG1:
+                       data = itv->card->gpio_audio_mode.lang1;
+                       break;
+               case V4L2_TUNER_MODE_LANG2:
+                       data = itv->card->gpio_audio_mode.lang2;
+                       break;
+               case V4L2_TUNER_MODE_MONO:
+                       data = itv->card->gpio_audio_mode.mono;
+                       break;
+               case V4L2_TUNER_MODE_STEREO:
+               case V4L2_TUNER_MODE_LANG1_LANG2:
+               default:
+                       data = itv->card->gpio_audio_mode.stereo;
+                       break;
+               }
+               break;
+
+       case AUDC_SET_RADIO:
+               mask = itv->card->gpio_audio_input.mask;
+               data = itv->card->gpio_audio_input.radio;
+               break;
+
+       case VIDIOC_S_STD:
+               mask = itv->card->gpio_audio_input.mask;
+               data = itv->card->gpio_audio_input.tuner;
+               break;
+
+       case VIDIOC_INT_S_AUDIO_ROUTING:
+               if (route->input > 2)
+                       return -EINVAL;
+               mask = itv->card->gpio_audio_input.mask;
+               switch (route->input) {
+                       case 0:
+                               data = itv->card->gpio_audio_input.tuner;
+                               break;
+                       case 1:
+                               data = itv->card->gpio_audio_input.linein;
+                               break;
+                       case 2:
+                       default:
+                               data = itv->card->gpio_audio_input.radio;
+                               break;
+               }
+               break;
+
+       case VIDIOC_G_CTRL:
+               if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+                       return -EINVAL;
+               mask = itv->card->gpio_audio_mute.mask;
+               data = itv->card->gpio_audio_mute.mute;
+               ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
+               return 0;
+
+       case VIDIOC_S_CTRL:
+               if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+                       return -EINVAL;
+               mask = itv->card->gpio_audio_mute.mask;
+               data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
+               break;
+
+       case VIDIOC_QUERYCTRL:
+       {
+               struct v4l2_queryctrl *qc = arg;
+
+               if (qc->id != V4L2_CID_AUDIO_MUTE)
+                       return -EINVAL;
+               *qc = gpio_ctrl_mute;
+               return 0;
+       }
+
+       case VIDIOC_LOG_STATUS:
+               IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
+                       read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
+                       read_reg(IVTV_REG_GPIO_IN));
+               return 0;
+
+       case VIDIOC_INT_S_VIDEO_ROUTING:
+               if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */
+                       return -EINVAL;
+               mask = itv->card->gpio_video_input.mask;
+               if  (route->input == 0)
+                       data = itv->card->gpio_video_input.tuner;
+               else if  (route->input == 1)
+                       data = itv->card->gpio_video_input.composite;
+               else
+                       data = itv->card->gpio_video_input.svideo;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       if (mask)
+               write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+       return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-gpio.h b/drivers/media/video/ivtv/ivtv-gpio.h
new file mode 100644 (file)
index 0000000..c301d2a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+    gpio functions.
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* GPIO stuff */
+void ivtv_gpio_init(struct ivtv *itv);
+void ivtv_reset_ir_gpio(struct ivtv *itv);
+int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr);
+int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg);
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
new file mode 100644 (file)
index 0000000..1735341
--- /dev/null
@@ -0,0 +1,750 @@
+/*
+    I2C functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+    This file includes an i2c implementation that was reverse engineered
+    from the Hauppauge windows driver.  Older ivtv versions used i2c-algo-bit,
+    which whilst fine under most circumstances, had trouble with the Zilog
+    CPU on the PVR-150 which handles IR functions (occasional inability to
+    communicate with the chip until it was reset) and also with the i2c
+    bus being completely unreachable when multiple PVR cards were present.
+
+    The implementation is very similar to i2c-algo-bit, but there are enough
+    subtle differences that the two are hard to merge.  The general strategy
+    employed by i2c-algo-bit is to use udelay() to implement the timing
+    when putting out bits on the scl/sda lines.  The general strategy taken
+    here is to poll the lines for state changes (see ivtv_waitscl and
+    ivtv_waitsda).  In addition there are small delays at various locations
+    which poll the SCL line 5 times (ivtv_scldelay).  I would guess that
+    since this is memory mapped I/O that the length of those delays is tied
+    to the PCI bus clock.  There is some extra code to do with recovery
+    and retries.  Since it is not known what causes the actual i2c problems
+    in the first place, the only goal if one was to attempt to use
+    i2c-algo-bit would be to try to make it follow the same code path.
+    This would be a lot of work, and I'm also not convinced that it would
+    provide a generic benefit to i2c-algo-bit.  Therefore consider this
+    an engineering solution -- not pretty, but it works.
+
+    Some more general comments about what we are doing:
+
+    The i2c bus is a 2 wire serial bus, with clock (SCL) and data (SDA)
+    lines.  To communicate on the bus (as a master, we don't act as a slave),
+    we first initiate a start condition (ivtv_start).  We then write the
+    address of the device that we want to communicate with, along with a flag
+    that indicates whether this is a read or a write.  The slave then issues
+    an ACK signal (ivtv_ack), which tells us that it is ready for reading /
+    writing.  We then proceed with reading or writing (ivtv_read/ivtv_write),
+    and finally issue a stop condition (ivtv_stop) to make the bus available
+    to other masters.
+
+    There is an additional form of transaction where a write may be
+    immediately followed by a read.  In this case, there is no intervening
+    stop condition.  (Only the msp3400 chip uses this method of data transfer).
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-gpio.h"
+
+#include <media/ir-kbd-i2c.h>
+
+/* i2c implementation for cx23415/6 chip, ivtv project.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ */
+/* i2c stuff */
+#define IVTV_REG_I2C_SETSCL_OFFSET 0x7000
+#define IVTV_REG_I2C_SETSDA_OFFSET 0x7004
+#define IVTV_REG_I2C_GETSCL_OFFSET 0x7008
+#define IVTV_REG_I2C_GETSDA_OFFSET 0x700c
+
+#ifndef I2C_ADAP_CLASS_TV_ANALOG
+#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG
+#endif /* I2C_ADAP_CLASS_TV_ANALOG */
+
+#define IVTV_CS53L32A_I2C_ADDR         0x11
+#define IVTV_CX25840_I2C_ADDR          0x44
+#define IVTV_SAA7115_I2C_ADDR          0x21
+#define IVTV_SAA7127_I2C_ADDR          0x44
+#define IVTV_SAA717x_I2C_ADDR          0x21
+#define IVTV_MSP3400_I2C_ADDR          0x40
+#define IVTV_HAUPPAUGE_I2C_ADDR        0x50
+#define IVTV_WM8739_I2C_ADDR           0x1a
+#define IVTV_WM8775_I2C_ADDR           0x1b
+#define IVTV_TEA5767_I2C_ADDR          0x60
+#define IVTV_UPD64031A_I2C_ADDR        0x12
+#define IVTV_UPD64083_I2C_ADDR                 0x5c
+#define IVTV_TDA985X_I2C_ADDR          0x5b
+
+/* This array should match the IVTV_HW_ defines */
+static const u8 hw_driverids[] = {
+       I2C_DRIVERID_CX25840,
+       I2C_DRIVERID_SAA711X,
+       I2C_DRIVERID_SAA7127,
+       I2C_DRIVERID_MSP3400,
+       I2C_DRIVERID_TUNER,
+       I2C_DRIVERID_WM8775,
+       I2C_DRIVERID_CS53L32A,
+       I2C_DRIVERID_TVEEPROM,
+       I2C_DRIVERID_SAA711X,
+       I2C_DRIVERID_TVAUDIO,
+       I2C_DRIVERID_UPD64031A,
+       I2C_DRIVERID_UPD64083,
+       I2C_DRIVERID_SAA717X,
+       I2C_DRIVERID_WM8739,
+       0               /* IVTV_HW_GPIO dummy driver ID */
+};
+
+/* This array should match the IVTV_HW_ defines */
+static const char * const hw_drivernames[] = {
+       "cx2584x",
+       "saa7115",
+       "saa7127",
+       "msp3400",
+       "tuner",
+       "wm8775",
+       "cs53l32a",
+       "tveeprom",
+       "saa7114",
+       "tvaudio",
+       "upd64031a",
+       "upd64083",
+       "saa717x",
+       "wm8739",
+       "gpio",
+};
+
+static int attach_inform(struct i2c_client *client)
+{
+       struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
+       int i;
+
+       IVTV_DEBUG_I2C("i2c client attach\n");
+       for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+               if (itv->i2c_clients[i] == NULL) {
+                       itv->i2c_clients[i] = client;
+                       break;
+               }
+       }
+       if (i == I2C_CLIENTS_MAX) {
+               IVTV_ERR("insufficient room for new I2C client!\n");
+       }
+       return 0;
+}
+
+static int detach_inform(struct i2c_client *client)
+{
+       int i;
+       struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
+
+       IVTV_DEBUG_I2C("i2c client detach\n");
+       for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+               if (itv->i2c_clients[i] == client) {
+                       itv->i2c_clients[i] = NULL;
+                       break;
+               }
+       }
+       IVTV_DEBUG_I2C("i2c detach [client=%s,%s]\n",
+                  client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
+
+       return 0;
+}
+
+/* Set the serial clock line to the desired state */
+static void ivtv_setscl(struct ivtv *itv, int state)
+{
+       /* write them out */
+       /* write bits are inverted */
+       write_reg(~state, IVTV_REG_I2C_SETSCL_OFFSET);
+}
+
+/* Set the serial data line to the desired state */
+static void ivtv_setsda(struct ivtv *itv, int state)
+{
+       /* write them out */
+       /* write bits are inverted */
+       write_reg(~state & 1, IVTV_REG_I2C_SETSDA_OFFSET);
+}
+
+/* Read the serial clock line */
+static int ivtv_getscl(struct ivtv *itv)
+{
+       return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1;
+}
+
+/* Read the serial data line */
+static int ivtv_getsda(struct ivtv *itv)
+{
+       return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1;
+}
+
+/* Implement a short delay by polling the serial clock line */
+static void ivtv_scldelay(struct ivtv *itv)
+{
+       int i;
+
+       for (i = 0; i < 5; ++i)
+               ivtv_getscl(itv);
+}
+
+/* Wait for the serial clock line to become set to a specific value */
+static int ivtv_waitscl(struct ivtv *itv, int val)
+{
+       int i;
+
+       ivtv_scldelay(itv);
+       for (i = 0; i < 1000; ++i) {
+               if (ivtv_getscl(itv) == val)
+                       return 1;
+       }
+       return 0;
+}
+
+/* Wait for the serial data line to become set to a specific value */
+static int ivtv_waitsda(struct ivtv *itv, int val)
+{
+       int i;
+
+       ivtv_scldelay(itv);
+       for (i = 0; i < 1000; ++i) {
+               if (ivtv_getsda(itv) == val)
+                       return 1;
+       }
+       return 0;
+}
+
+/* Wait for the slave to issue an ACK */
+static int ivtv_ack(struct ivtv *itv)
+{
+       int ret = 0;
+
+       if (ivtv_getscl(itv) == 1) {
+               IVTV_DEBUG_I2C("SCL was high starting an ack\n");
+               ivtv_setscl(itv, 0);
+               if (!ivtv_waitscl(itv, 0)) {
+                       IVTV_DEBUG_I2C("Could not set SCL low starting an ack\n");
+                       return -EREMOTEIO;
+               }
+       }
+       ivtv_setsda(itv, 1);
+       ivtv_scldelay(itv);
+       ivtv_setscl(itv, 1);
+       if (!ivtv_waitsda(itv, 0)) {
+               IVTV_DEBUG_I2C("Slave did not ack\n");
+               ret = -EREMOTEIO;
+       }
+       ivtv_setscl(itv, 0);
+       if (!ivtv_waitscl(itv, 0)) {
+               IVTV_DEBUG_I2C("Failed to set SCL low after ACK\n");
+               ret = -EREMOTEIO;
+       }
+       return ret;
+}
+
+/* Write a single byte to the i2c bus and wait for the slave to ACK */
+static int ivtv_sendbyte(struct ivtv *itv, unsigned char byte)
+{
+       int i, bit;
+
+       IVTV_DEBUG_I2C("write %x\n",byte);
+       for (i = 0; i < 8; ++i, byte<<=1) {
+               ivtv_setscl(itv, 0);
+               if (!ivtv_waitscl(itv, 0)) {
+                       IVTV_DEBUG_I2C("Error setting SCL low\n");
+                       return -EREMOTEIO;
+               }
+               bit = (byte>>7)&1;
+               ivtv_setsda(itv, bit);
+               if (!ivtv_waitsda(itv, bit)) {
+                       IVTV_DEBUG_I2C("Error setting SDA\n");
+                       return -EREMOTEIO;
+               }
+               ivtv_setscl(itv, 1);
+               if (!ivtv_waitscl(itv, 1)) {
+                       IVTV_DEBUG_I2C("Slave not ready for bit\n");
+                       return -EREMOTEIO;
+               }
+       }
+       ivtv_setscl(itv, 0);
+       if (!ivtv_waitscl(itv, 0)) {
+               IVTV_DEBUG_I2C("Error setting SCL low\n");
+               return -EREMOTEIO;
+       }
+       return ivtv_ack(itv);
+}
+
+/* Read a byte from the i2c bus and send a NACK if applicable (i.e. for the
+   final byte) */
+static int ivtv_readbyte(struct ivtv *itv, unsigned char *byte, int nack)
+{
+       int i;
+
+       *byte = 0;
+
+       ivtv_setsda(itv, 1);
+       ivtv_scldelay(itv);
+       for (i = 0; i < 8; ++i) {
+               ivtv_setscl(itv, 0);
+               ivtv_scldelay(itv);
+               ivtv_setscl(itv, 1);
+               if (!ivtv_waitscl(itv, 1)) {
+                       IVTV_DEBUG_I2C("Error setting SCL high\n");
+                       return -EREMOTEIO;
+               }
+               *byte = ((*byte)<<1)|ivtv_getsda(itv);
+       }
+       ivtv_setscl(itv, 0);
+       ivtv_scldelay(itv);
+       ivtv_setsda(itv, nack);
+       ivtv_scldelay(itv);
+       ivtv_setscl(itv, 1);
+       ivtv_scldelay(itv);
+       ivtv_setscl(itv, 0);
+       ivtv_scldelay(itv);
+       IVTV_DEBUG_I2C("read %x\n",*byte);
+       return 0;
+}
+
+/* Issue a start condition on the i2c bus to alert slaves to prepare for
+   an address write */
+static int ivtv_start(struct ivtv *itv)
+{
+       int sda;
+
+       sda = ivtv_getsda(itv);
+       if (sda != 1) {
+               IVTV_DEBUG_I2C("SDA was low at start\n");
+               ivtv_setsda(itv, 1);
+               if (!ivtv_waitsda(itv, 1)) {
+                       IVTV_DEBUG_I2C("SDA stuck low\n");
+                       return -EREMOTEIO;
+               }
+       }
+       if (ivtv_getscl(itv) != 1) {
+               ivtv_setscl(itv, 1);
+               if (!ivtv_waitscl(itv, 1)) {
+                       IVTV_DEBUG_I2C("SCL stuck low at start\n");
+                       return -EREMOTEIO;
+               }
+       }
+       ivtv_setsda(itv, 0);
+       ivtv_scldelay(itv);
+       return 0;
+}
+
+/* Issue a stop condition on the i2c bus to release it */
+static int ivtv_stop(struct ivtv *itv)
+{
+       int i;
+
+       if (ivtv_getscl(itv) != 0) {
+               IVTV_DEBUG_I2C("SCL not low when stopping\n");
+               ivtv_setscl(itv, 0);
+               if (!ivtv_waitscl(itv, 0)) {
+                       IVTV_DEBUG_I2C("SCL could not be set low\n");
+               }
+       }
+       ivtv_setsda(itv, 0);
+       ivtv_scldelay(itv);
+       ivtv_setscl(itv, 1);
+       if (!ivtv_waitscl(itv, 1)) {
+               IVTV_DEBUG_I2C("SCL could not be set high\n");
+               return -EREMOTEIO;
+       }
+       ivtv_scldelay(itv);
+       ivtv_setsda(itv, 1);
+       if (!ivtv_waitsda(itv, 1)) {
+               IVTV_DEBUG_I2C("resetting I2C\n");
+               for (i = 0; i < 16; ++i) {
+                       ivtv_setscl(itv, 0);
+                       ivtv_scldelay(itv);
+                       ivtv_setscl(itv, 1);
+                       ivtv_scldelay(itv);
+                       ivtv_setsda(itv, 1);
+               }
+               ivtv_waitsda(itv, 1);
+               return -EREMOTEIO;
+       }
+       return 0;
+}
+
+/* Write a message to the given i2c slave.  do_stop may be 0 to prevent
+   issuing the i2c stop condition (when following with a read) */
+static int ivtv_write(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len, int do_stop)
+{
+       int retry, ret = -EREMOTEIO;
+       u32 i;
+
+       for (retry = 0; ret != 0 && retry < 8; ++retry) {
+               ret = ivtv_start(itv);
+
+               if (ret == 0) {
+                       ret = ivtv_sendbyte(itv, addr<<1);
+                       for (i = 0; ret == 0 && i < len; ++i)
+                               ret = ivtv_sendbyte(itv, data[i]);
+               }
+               if (ret != 0 || do_stop) {
+                       ivtv_stop(itv);
+               }
+       }
+       if (ret)
+               IVTV_DEBUG_I2C("i2c write to %x failed\n", addr);
+       return ret;
+}
+
+/* Read data from the given i2c slave.  A stop condition is always issued. */
+static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len)
+{
+       int retry, ret = -EREMOTEIO;
+       u32 i;
+
+       for (retry = 0; ret != 0 && retry < 8; ++retry) {
+               ret = ivtv_start(itv);
+               if (ret == 0)
+                       ret = ivtv_sendbyte(itv, (addr << 1) | 1);
+               for (i = 0; ret == 0 && i < len; ++i) {
+                       ret = ivtv_readbyte(itv, &data[i], i == len - 1);
+               }
+               ivtv_stop(itv);
+       }
+       if (ret)
+               IVTV_DEBUG_I2C("i2c read from %x failed\n", addr);
+       return ret;
+}
+
+/* Kernel i2c transfer implementation.  Takes a number of messages to be read
+   or written.  If a read follows a write, this will occur without an
+   intervening stop condition */
+static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+       struct ivtv *itv = i2c_get_adapdata(i2c_adap);
+       int retval;
+       int i;
+
+       mutex_lock(&itv->i2c_bus_lock);
+       for (i = retval = 0; retval == 0 && i < num; i++) {
+               if (msgs[i].flags & I2C_M_RD)
+                       retval = ivtv_read(itv, msgs[i].addr, msgs[i].buf, msgs[i].len);
+               else {
+                       /* if followed by a read, don't stop */
+                       int stop = !(i + 1 < num && msgs[i + 1].flags == I2C_M_RD);
+
+                       retval = ivtv_write(itv, msgs[i].addr, msgs[i].buf, msgs[i].len, stop);
+               }
+       }
+       mutex_unlock(&itv->i2c_bus_lock);
+       return retval ? retval : num;
+}
+
+/* Kernel i2c capabilities */
+static u32 ivtv_functionality(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm ivtv_algo = {
+       .master_xfer   = ivtv_xfer,
+       .functionality = ivtv_functionality,
+};
+
+/* template for our-bit banger */
+static struct i2c_adapter ivtv_i2c_adap_hw_template = {
+       .name = "ivtv i2c driver",
+       .id = I2C_HW_B_CX2341X,
+       .algo = &ivtv_algo,
+       .algo_data = NULL,                      /* filled from template */
+       .client_register = attach_inform,
+       .client_unregister = detach_inform,
+       .owner = THIS_MODULE,
+#ifdef I2C_ADAP_CLASS_TV_ANALOG
+       .class = I2C_ADAP_CLASS_TV_ANALOG,
+#endif
+};
+
+static void ivtv_setscl_old(void *data, int state)
+{
+       struct ivtv *itv = (struct ivtv *)data;
+
+       if (state)
+               itv->i2c_state |= 0x01;
+       else
+               itv->i2c_state &= ~0x01;
+
+       /* write them out */
+       /* write bits are inverted */
+       write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSCL_OFFSET);
+}
+
+static void ivtv_setsda_old(void *data, int state)
+{
+       struct ivtv *itv = (struct ivtv *)data;
+
+       if (state)
+               itv->i2c_state |= 0x01;
+       else
+               itv->i2c_state &= ~0x01;
+
+       /* write them out */
+       /* write bits are inverted */
+       write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSDA_OFFSET);
+}
+
+static int ivtv_getscl_old(void *data)
+{
+       struct ivtv *itv = (struct ivtv *)data;
+
+       return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1;
+}
+
+static int ivtv_getsda_old(void *data)
+{
+       struct ivtv *itv = (struct ivtv *)data;
+
+       return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1;
+}
+
+/* template for i2c-bit-algo */
+static struct i2c_adapter ivtv_i2c_adap_template = {
+       .name = "ivtv i2c driver",
+       .id = I2C_HW_B_CX2341X,         /* algo-bit is OR'd with this */
+       .algo = NULL,                   /* set by i2c-algo-bit */
+       .algo_data = NULL,              /* filled from template */
+       .client_register = attach_inform,
+       .client_unregister = detach_inform,
+       .owner = THIS_MODULE,
+#ifdef I2C_ADAP_CLASS_TV_ANALOG
+       .class = I2C_ADAP_CLASS_TV_ANALOG,
+#endif
+};
+
+static struct i2c_algo_bit_data ivtv_i2c_algo_template = {
+       NULL,                   /* ?? */
+       ivtv_setsda_old,        /* setsda function */
+       ivtv_setscl_old,        /* " */
+       ivtv_getsda_old,        /* " */
+       ivtv_getscl_old,        /* " */
+       10,                     /* udelay */
+       200                     /* timeout */
+};
+
+static struct i2c_client ivtv_i2c_client_template = {
+       .name = "ivtv internal use only",
+};
+
+int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg)
+{
+       struct i2c_client *client;
+       int retval;
+       int i;
+
+       IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
+       for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+               client = itv->i2c_clients[i];
+               if (client == NULL) {
+                       continue;
+               }
+               if (client->driver->command == NULL) {
+                       continue;
+               }
+               if (addr == client->addr) {
+                       retval = client->driver->command(client, cmd, arg);
+                       return retval;
+               }
+       }
+       IVTV_ERR("i2c addr 0x%02x not found for command 0x%x!\n", addr, cmd);
+       return -ENODEV;
+}
+
+/* Find the i2c device based on the driver ID and return
+   its i2c address or -ENODEV if no matching device was found. */
+int ivtv_i2c_id_addr(struct ivtv *itv, u32 id)
+{
+       struct i2c_client *client;
+       int retval = -ENODEV;
+       int i;
+
+       for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+               client = itv->i2c_clients[i];
+               if (client == NULL)
+                       continue;
+               if (id == client->driver->id) {
+                       retval = client->addr;
+                       break;
+               }
+       }
+       return retval;
+}
+
+/* Find the i2c device name matching the DRIVERID */
+static const char *ivtv_i2c_id_name(u32 id)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+               if (hw_driverids[i] == id)
+                       return hw_drivernames[i];
+       return "unknown device";
+}
+
+/* Find the i2c device name matching the IVTV_HW_ flag */
+static const char *ivtv_i2c_hw_name(u32 hw)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+               if (1 << i == hw)
+                       return hw_drivernames[i];
+       return "unknown device";
+}
+
+/* Find the i2c device matching the IVTV_HW_ flag and return
+   its i2c address or -ENODEV if no matching device was found. */
+int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+               if (1 << i == hw)
+                       return ivtv_i2c_id_addr(itv, hw_driverids[i]);
+       return -ENODEV;
+}
+
+/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing.
+   If hw == IVTV_HW_GPIO then call the gpio handler. */
+int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg)
+{
+       int addr;
+
+       if (hw == IVTV_HW_GPIO)
+               return ivtv_gpio(itv, cmd, arg);
+       if (hw == 0)
+               return 0;
+
+       addr = ivtv_i2c_hw_addr(itv, hw);
+       if (addr < 0) {
+               IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x!\n",
+                              hw, ivtv_i2c_hw_name(hw), cmd);
+               return addr;
+       }
+       return ivtv_call_i2c_client(itv, addr, cmd, arg);
+}
+
+/* Calls i2c device based on I2C driver ID. */
+int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg)
+{
+       int addr;
+
+       addr = ivtv_i2c_id_addr(itv, id);
+       if (addr < 0) {
+               IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x!\n",
+                               id, ivtv_i2c_id_name(id), cmd);
+               return addr;
+       }
+       return ivtv_call_i2c_client(itv, addr, cmd, arg);
+}
+
+int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+       return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+       return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+       return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+       return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_msp34xx(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+       return ivtv_call_i2c_client(itv, IVTV_MSP3400_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+       return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg);
+}
+
+int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+       return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg);
+}
+
+/* broadcast cmd for all I2C clients and for the gpio subsystem */
+void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+       if (itv->i2c_adap.algo == NULL) {
+               IVTV_ERR("adapter is not set");
+               return;
+       }
+       i2c_clients_command(&itv->i2c_adap, cmd, arg);
+       if (itv->hw_flags & IVTV_HW_GPIO)
+               ivtv_gpio(itv, cmd, arg);
+}
+
+/* init + register i2c algo-bit adapter */
+int __devinit init_ivtv_i2c(struct ivtv *itv)
+{
+       IVTV_DEBUG_I2C("i2c init\n");
+
+       if (itv->options.newi2c > 0) {
+               memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template,
+                      sizeof(struct i2c_adapter));
+       } else {
+               memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template,
+                      sizeof(struct i2c_adapter));
+               memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template,
+                      sizeof(struct i2c_algo_bit_data));
+               itv->i2c_algo.data = itv;
+               itv->i2c_adap.algo_data = &itv->i2c_algo;
+       }
+
+       sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d",
+               itv->num);
+       i2c_set_adapdata(&itv->i2c_adap, itv);
+
+       memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
+              sizeof(struct i2c_client));
+       itv->i2c_client.adapter = &itv->i2c_adap;
+       itv->i2c_adap.dev.parent = &itv->dev->dev;
+
+       IVTV_DEBUG_I2C("setting scl and sda to 1\n");
+       ivtv_setscl(itv, 1);
+       ivtv_setsda(itv, 1);
+
+       if (itv->options.newi2c > 0)
+               return i2c_add_adapter(&itv->i2c_adap);
+       else
+               return i2c_bit_add_bus(&itv->i2c_adap);
+}
+
+void __devexit exit_ivtv_i2c(struct ivtv *itv)
+{
+       IVTV_DEBUG_I2C("i2c exit\n");
+
+       i2c_del_adapter(&itv->i2c_adap);
+}
diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h
new file mode 100644 (file)
index 0000000..136dd68
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+    I2C functions
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_msp34xx(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg);
+int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg);
+
+int ivtv_i2c_id_addr(struct ivtv *itv, u32 id);
+int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw);
+int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg);
+int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg);
+int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg);
+void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg);
+
+/* init + register i2c algo-bit adapter */
+int __devinit init_ivtv_i2c(struct ivtv *itv);
+void __devexit exit_ivtv_i2c(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
new file mode 100644 (file)
index 0000000..448e8dd
--- /dev/null
@@ -0,0 +1,1555 @@
+/*
+    ioctl system call
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-fileops.h"
+#include "ivtv-vbi.h"
+#include "ivtv-audio.h"
+#include "ivtv-video.h"
+#include "ivtv-streams.h"
+#include "ivtv-yuv.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-gpio.h"
+#include "ivtv-controls.h"
+#include "ivtv-cards.h"
+#include <media/saa7127.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/dvb/audio.h>
+#include <linux/i2c-id.h>
+
+u16 service2vbi(int type)
+{
+       switch (type) {
+               case V4L2_SLICED_TELETEXT_B:
+                       return IVTV_SLICED_TYPE_TELETEXT_B;
+               case V4L2_SLICED_CAPTION_525:
+                       return IVTV_SLICED_TYPE_CAPTION_525;
+               case V4L2_SLICED_WSS_625:
+                       return IVTV_SLICED_TYPE_WSS_625;
+               case V4L2_SLICED_VPS:
+                       return IVTV_SLICED_TYPE_VPS;
+               default:
+                       return 0;
+       }
+}
+
+static int valid_service_line(int field, int line, int is_pal)
+{
+       return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
+              (!is_pal && line >= 10 && line < 22);
+}
+
+static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
+{
+       u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
+       int i;
+
+       set = set & valid_set;
+       if (set == 0 || !valid_service_line(field, line, is_pal)) {
+               return 0;
+       }
+       if (!is_pal) {
+               if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
+                       return V4L2_SLICED_CAPTION_525;
+       }
+       else {
+               if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
+                       return V4L2_SLICED_VPS;
+               if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
+                       return V4L2_SLICED_WSS_625;
+               if (line == 23)
+                       return 0;
+       }
+       for (i = 0; i < 32; i++) {
+               if ((1 << i) & set)
+                       return 1 << i;
+       }
+       return 0;
+}
+
+void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+       u16 set = fmt->service_set;
+       int f, l;
+
+       fmt->service_set = 0;
+       for (f = 0; f < 2; f++) {
+               for (l = 0; l < 24; l++) {
+                       fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
+               }
+       }
+}
+
+static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+       int f, l;
+       u16 set = 0;
+
+       for (f = 0; f < 2; f++) {
+               for (l = 0; l < 24; l++) {
+                       fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
+                       set |= fmt->service_lines[f][l];
+               }
+       }
+       return set != 0;
+}
+
+u16 get_service_set(struct v4l2_sliced_vbi_format *fmt)
+{
+       int f, l;
+       u16 set = 0;
+
+       for (f = 0; f < 2; f++) {
+               for (l = 0; l < 24; l++) {
+                       set |= fmt->service_lines[f][l];
+               }
+       }
+       return set;
+}
+
+static const struct {
+       v4l2_std_id  std;
+       char        *name;
+} enum_stds[] = {
+       { V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" },
+       { V4L2_STD_PAL_DK,    "PAL-DK"    },
+       { V4L2_STD_PAL_I,     "PAL-I"     },
+       { V4L2_STD_PAL_M,     "PAL-M"     },
+       { V4L2_STD_PAL_N,     "PAL-N"     },
+       { V4L2_STD_PAL_Nc,    "PAL-Nc"    },
+       { V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" },
+       { V4L2_STD_SECAM_DK,  "SECAM-DK"  },
+       { V4L2_STD_SECAM_L,   "SECAM-L"   },
+       { V4L2_STD_SECAM_LC,  "SECAM-L'"  },
+       { V4L2_STD_NTSC_M,    "NTSC-M"    },
+       { V4L2_STD_NTSC_M_JP, "NTSC-J"    },
+       { V4L2_STD_NTSC_M_KR, "NTSC-K"    },
+};
+
+static const struct v4l2_standard ivtv_std_60hz =
+{
+       .frameperiod = {.numerator = 1001, .denominator = 30000},
+       .framelines = 525,
+};
+
+static const struct v4l2_standard ivtv_std_50hz =
+{
+       .frameperiod = {.numerator = 1, .denominator = 25},
+       .framelines = 625,
+};
+
+void ivtv_set_osd_alpha(struct ivtv *itv)
+{
+       ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3,
+               itv->osd_global_alpha_state, itv->osd_global_alpha, !itv->osd_local_alpha_state);
+       ivtv_vapi(itv, CX2341X_OSD_SET_CHROMA_KEY, 2, itv->osd_color_key_state, itv->osd_color_key);
+}
+
+int ivtv_set_speed(struct ivtv *itv, int speed)
+{
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       struct ivtv_stream *s;
+       int single_step = (speed == 1 || speed == -1);
+       DEFINE_WAIT(wait);
+
+       if (speed == 0) speed = 1000;
+
+       /* No change? */
+       if (speed == itv->speed && !single_step)
+               return 0;
+
+       s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+
+       if (single_step && (speed < 0) == (itv->speed < 0)) {
+               /* Single step video and no need to change direction */
+               ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
+               itv->speed = speed;
+               return 0;
+       }
+       if (single_step)
+               /* Need to change direction */
+               speed = speed < 0 ? -1000 : 1000;
+
+       data[0] = (speed > 1000 || speed < -1000) ? 0x80000000 : 0;
+       data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
+       data[1] = (speed < 0);
+       data[2] = speed < 0 ? 3 : 7;
+       data[3] = itv->params.video_b_frames;
+       data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
+       data[5] = 0;
+       data[6] = 0;
+
+       if (speed == 1500 || speed == -1500) data[0] |= 1;
+       else if (speed == 2000 || speed == -2000) data[0] |= 2;
+       else if (speed > -1000 && speed < 0) data[0] |= (-1000 / speed);
+       else if (speed < 1000 && speed > 0) data[0] |= (1000 / speed);
+
+       /* If not decoding, just change speed setting */
+       if (atomic_read(&itv->decoding) > 0) {
+               int got_sig = 0;
+
+               /* Stop all DMA and decoding activity */
+               ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0);
+
+               /* Wait for any DMA to finish */
+               prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+               while (itv->i_flags & IVTV_F_I_DMA) {
+                       got_sig = signal_pending(current);
+                       if (got_sig)
+                               break;
+                       got_sig = 0;
+                       schedule();
+               }
+               finish_wait(&itv->dma_waitq, &wait);
+               if (got_sig)
+                       return -EINTR;
+
+               /* Change Speed safely */
+               ivtv_api(itv, CX2341X_DEC_SET_PLAYBACK_SPEED, 7, data);
+               IVTV_DEBUG_INFO("Setting Speed to 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                               data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
+       }
+       if (single_step) {
+               speed = (speed < 0) ? -1 : 1;
+               ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
+       }
+       itv->speed = speed;
+       return 0;
+}
+
+static int ivtv_validate_speed(int cur_speed, int new_speed)
+{
+       int fact = new_speed < 0 ? -1 : 1;
+       int s;
+
+       if (new_speed < 0) new_speed = -new_speed;
+       if (cur_speed < 0) cur_speed = -cur_speed;
+
+       if (cur_speed <= new_speed) {
+               if (new_speed > 1500) return fact * 2000;
+               if (new_speed > 1000) return fact * 1500;
+       }
+       else {
+               if (new_speed >= 2000) return fact * 2000;
+               if (new_speed >= 1500) return fact * 1500;
+               if (new_speed >= 1000) return fact * 1000;
+       }
+       if (new_speed == 0) return 1000;
+       if (new_speed == 1 || new_speed == 1000) return fact * new_speed;
+
+       s = new_speed;
+       new_speed = 1000 / new_speed;
+       if (1000 / cur_speed == new_speed)
+               new_speed += (cur_speed < s) ? -1 : 1;
+       if (new_speed > 60) return 1000 / (fact * 60);
+       return 1000 / (fact * new_speed);
+}
+
+static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id,
+               struct video_command *vc, int try)
+{
+       struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+
+       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+               return -EINVAL;
+
+       switch (vc->cmd) {
+       case VIDEO_CMD_PLAY: {
+               vc->play.speed = ivtv_validate_speed(itv->speed, vc->play.speed);
+               if (vc->play.speed < 0)
+                       vc->play.format = VIDEO_PLAY_FMT_GOP;
+               if (try) break;
+
+               if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG)
+                       return -EBUSY;
+               return ivtv_start_decoding(id, vc->play.speed);
+       }
+
+       case VIDEO_CMD_STOP:
+               if (vc->flags & VIDEO_CMD_STOP_IMMEDIATELY)
+                       vc->stop.pts = 0;
+               if (try) break;
+               if (atomic_read(&itv->decoding) == 0)
+                       return 0;
+               if (itv->output_mode != OUT_MPG)
+                       return -EBUSY;
+
+               itv->output_mode = OUT_NONE;
+               return ivtv_stop_v4l2_decode_stream(s, vc->flags, vc->stop.pts);
+
+       case VIDEO_CMD_FREEZE:
+               if (try) break;
+               if (itv->output_mode != OUT_MPG)
+                       return -EBUSY;
+               if (atomic_read(&itv->decoding) > 0) {
+                       ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1,
+                               (vc->flags & VIDEO_CMD_FREEZE_TO_BLACK) ? 1 : 0);
+               }
+               break;
+
+       case VIDEO_CMD_CONTINUE:
+               if (try) break;
+               if (itv->output_mode != OUT_MPG)
+                       return -EBUSY;
+               if (atomic_read(&itv->decoding) > 0) {
+                       ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 0);
+               }
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+       struct v4l2_register *regs = arg;
+       unsigned long flags;
+       volatile u8 __iomem *reg_start;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE)
+               reg_start = itv->reg_mem - IVTV_REG_OFFSET;
+       else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET &&
+                       regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE)
+               reg_start = itv->dec_mem - IVTV_DECODER_OFFSET;
+       else if (regs->reg >= 0 && regs->reg < IVTV_ENCODER_SIZE)
+               reg_start = itv->enc_mem;
+       else
+               return -EINVAL;
+
+       spin_lock_irqsave(&ivtv_cards_lock, flags);
+       if (cmd == VIDIOC_DBG_G_REGISTER) {
+               regs->val = readl(regs->reg + reg_start);
+       } else {
+               writel(regs->val, regs->reg + reg_start);
+       }
+       spin_unlock_irqrestore(&ivtv_cards_lock, flags);
+       return 0;
+}
+
+static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fmt)
+{
+       switch (fmt->type) {
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+                       return -EINVAL;
+               fmt->fmt.pix.left = itv->main_rect.left;
+               fmt->fmt.pix.top = itv->main_rect.top;
+               fmt->fmt.pix.width = itv->main_rect.width;
+               fmt->fmt.pix.height = itv->main_rect.height;
+               fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+               fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+               if (itv->output_mode == OUT_UDMA_YUV) {
+                       switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) {
+                       case IVTV_YUV_MODE_INTERLACED:
+                               fmt->fmt.pix.field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ?
+                                       V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB;
+                               break;
+                       case IVTV_YUV_MODE_PROGRESSIVE:
+                               fmt->fmt.pix.field = V4L2_FIELD_NONE;
+                               break;
+                       default:
+                               fmt->fmt.pix.field = V4L2_FIELD_ANY;
+                               break;
+                       }
+                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
+                       /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+                       fmt->fmt.pix.sizeimage =
+                               fmt->fmt.pix.height * fmt->fmt.pix.width +
+                               fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
+               }
+               else if (itv->output_mode == OUT_YUV ||
+                               streamtype == IVTV_ENC_STREAM_TYPE_YUV ||
+                               streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
+                       /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+                       fmt->fmt.pix.sizeimage =
+                               fmt->fmt.pix.height * fmt->fmt.pix.width +
+                               fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
+               } else {
+                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+                       fmt->fmt.pix.sizeimage = 128 * 1024;
+               }
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               fmt->fmt.pix.left = 0;
+               fmt->fmt.pix.top = 0;
+               fmt->fmt.pix.width = itv->params.width;
+               fmt->fmt.pix.height = itv->params.height;
+               fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+               fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+               if (streamtype == IVTV_ENC_STREAM_TYPE_YUV ||
+                               streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
+                       /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+                       fmt->fmt.pix.sizeimage =
+                               fmt->fmt.pix.height * fmt->fmt.pix.width +
+                               fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
+               } else {
+                       fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+                       fmt->fmt.pix.sizeimage = 128 * 1024;
+               }
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+                       return -EINVAL;
+               fmt->fmt.win.chromakey = itv->osd_color_key;
+               fmt->fmt.win.global_alpha = itv->osd_global_alpha;
+               break;
+
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               fmt->fmt.vbi.sampling_rate = 27000000;
+               fmt->fmt.vbi.offset = 248;
+               fmt->fmt.vbi.samples_per_line = itv->vbi.raw_decoder_line_size - 4;
+               fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+               fmt->fmt.vbi.start[0] = itv->vbi.start[0];
+               fmt->fmt.vbi.start[1] = itv->vbi.start[1];
+               fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = itv->vbi.count;
+               break;
+
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+       {
+               struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+               if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+                       return -EINVAL;
+               vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+               memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+               memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+               if (itv->is_60hz) {
+                       vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+                       vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+               } else {
+                       vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625;
+                       vbifmt->service_lines[0][16] = V4L2_SLICED_VPS;
+               }
+               vbifmt->service_set = get_service_set(vbifmt);
+               break;
+       }
+
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       {
+               struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+               vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+               memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+               memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+
+               if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) {
+                       vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 :
+                                                V4L2_SLICED_VBI_525;
+                       expand_service_set(vbifmt, itv->is_50hz);
+                       break;
+               }
+
+               itv->video_dec_func(itv, VIDIOC_G_FMT, fmt);
+               vbifmt->service_set = get_service_set(vbifmt);
+               break;
+       }
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype,
+               struct v4l2_format *fmt, int set_fmt)
+{
+       struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+       u16 set;
+
+       if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+               struct v4l2_rect r;
+               int field;
+
+               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+                       return -EINVAL;
+               field = fmt->fmt.pix.field;
+               r.top = fmt->fmt.pix.top;
+               r.left = fmt->fmt.pix.left;
+               r.width = fmt->fmt.pix.width;
+               r.height = fmt->fmt.pix.height;
+               ivtv_get_fmt(itv, streamtype, fmt);
+               if (itv->output_mode != OUT_UDMA_YUV) {
+                       /* TODO: would setting the rect also be valid for this mode? */
+                       fmt->fmt.pix.top = r.top;
+                       fmt->fmt.pix.left = r.left;
+                       fmt->fmt.pix.width = r.width;
+                       fmt->fmt.pix.height = r.height;
+               }
+               if (itv->output_mode == OUT_UDMA_YUV) {
+                       /* TODO: add checks for validity */
+                       fmt->fmt.pix.field = field;
+               }
+               if (set_fmt) {
+                       if (itv->output_mode == OUT_UDMA_YUV) {
+                               switch (field) {
+                               case V4L2_FIELD_NONE:
+                                       itv->yuv_info.lace_mode = IVTV_YUV_MODE_PROGRESSIVE;
+                                       break;
+                               case V4L2_FIELD_ANY:
+                                       itv->yuv_info.lace_mode = IVTV_YUV_MODE_AUTO;
+                                       break;
+                               case V4L2_FIELD_INTERLACED_BT:
+                                       itv->yuv_info.lace_mode =
+                                               IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD;
+                                       break;
+                               case V4L2_FIELD_INTERLACED_TB:
+                               default:
+                                       itv->yuv_info.lace_mode = IVTV_YUV_MODE_INTERLACED;
+                                       break;
+                               }
+                               itv->yuv_info.lace_sync_field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1;
+
+                               /* Force update of yuv registers */
+                               itv->yuv_info.yuv_forced_update = 1;
+                               return 0;
+                       }
+                       if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+                                r.width, r.height, r.left, r.top))
+                               itv->main_rect = r;
+                       else
+                               return -EINVAL;
+               }
+               return 0;
+       }
+
+       if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) {
+               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+                       return -EINVAL;
+               if (set_fmt) {
+                       itv->osd_color_key = fmt->fmt.win.chromakey;
+                       itv->osd_global_alpha = fmt->fmt.win.global_alpha;
+                       ivtv_set_osd_alpha(itv);
+               }
+               return 0;
+       }
+
+       /* set window size */
+       if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               int w = fmt->fmt.pix.width;
+               int h = fmt->fmt.pix.height;
+
+               if (w > 720) w = 720;
+               else if (w < 1) w = 1;
+               if (h > (itv->is_50hz ? 576 : 480)) h = (itv->is_50hz ? 576 : 480);
+               else if (h < 2) h = 2;
+               ivtv_get_fmt(itv, streamtype, fmt);
+               fmt->fmt.pix.width = w;
+               fmt->fmt.pix.height = h;
+
+               if (!set_fmt || (itv->params.width == w && itv->params.height == h))
+                       return 0;
+               if (atomic_read(&itv->capturing) > 0)
+                       return -EBUSY;
+
+               itv->params.width = w;
+               itv->params.height = h;
+               if (w != 720 || h != (itv->is_50hz ? 576 : 480))
+                       itv->params.video_temporal_filter = 0;
+               else
+                       itv->params.video_temporal_filter = 8;
+               itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+               return ivtv_get_fmt(itv, streamtype, fmt);
+       }
+
+       /* set raw VBI format */
+       if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+               if (set_fmt && streamtype == IVTV_ENC_STREAM_TYPE_VBI &&
+                   itv->vbi.sliced_in->service_set &&
+                   atomic_read(&itv->capturing) > 0) {
+                       return -EBUSY;
+               }
+               if (set_fmt) {
+                       itv->vbi.sliced_in->service_set = 0;
+                       itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
+               }
+               return ivtv_get_fmt(itv, streamtype, fmt);
+       }
+
+       /* set sliced VBI output
+          In principle the user could request that only certain
+          VBI types are output and that the others are ignored.
+          I.e., suppress CC in the even fields or only output
+          WSS and no VPS. Currently though there is no choice. */
+       if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
+               return ivtv_get_fmt(itv, streamtype, fmt);
+
+       /* any else but sliced VBI capture is an error */
+       if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+               return -EINVAL;
+
+       if (streamtype == IVTV_DEC_STREAM_TYPE_VBI)
+               return ivtv_get_fmt(itv, streamtype, fmt);
+
+       /* set sliced VBI capture format */
+       vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+       memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+
+       if (vbifmt->service_set)
+               expand_service_set(vbifmt, itv->is_50hz);
+       set = check_service_set(vbifmt, itv->is_50hz);
+       vbifmt->service_set = get_service_set(vbifmt);
+
+       if (!set_fmt)
+               return 0;
+       if (set == 0)
+               return -EINVAL;
+       if (atomic_read(&itv->capturing) > 0 && itv->vbi.sliced_in->service_set == 0) {
+               return -EBUSY;
+       }
+       itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+       memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
+       return 0;
+}
+
+static int ivtv_internal_ioctls(struct file *filp, unsigned int cmd, void *arg)
+{
+       struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+       struct ivtv *itv = id->itv;
+       struct v4l2_register *reg = arg;
+
+       switch (cmd) {
+       /* ioctls to allow direct access to the encoder registers for testing */
+       case VIDIOC_DBG_G_REGISTER:
+               IVTV_DEBUG_IOCTL("VIDIOC_DBG_G_REGISTER\n");
+               if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+                       return ivtv_itvc(itv, cmd, arg);
+               if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+                       return ivtv_i2c_id(itv, reg->match_chip, cmd, arg);
+               return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg);
+
+       case VIDIOC_DBG_S_REGISTER:
+               IVTV_DEBUG_IOCTL("VIDIOC_DBG_S_REGISTER\n");
+               if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+                       return ivtv_itvc(itv, cmd, arg);
+               if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+                       return ivtv_i2c_id(itv, reg->match_chip, cmd, arg);
+               return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg);
+
+       case VIDIOC_G_CHIP_IDENT: {
+               struct v4l2_chip_ident *chip = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_G_CHIP_IDENT\n");
+               chip->ident = V4L2_IDENT_NONE;
+               chip->revision = 0;
+               if (reg->match_type == V4L2_CHIP_MATCH_HOST) {
+                       if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) {
+                               struct v4l2_chip_ident *chip = arg;
+
+                               chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
+                       }
+                       return 0;
+               }
+               if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+                       return ivtv_i2c_id(itv, reg->match_chip, cmd, arg);
+               if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
+                       return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg);
+               return -EINVAL;
+       }
+
+       case VIDIOC_INT_S_AUDIO_ROUTING: {
+               struct v4l2_routing *route = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING\n");
+               ivtv_audio_set_route(itv, route);
+               break;
+       }
+
+       case VIDIOC_INT_RESET:
+               IVTV_DEBUG_IOCTL("VIDIOC_INT_RESET\n");
+               ivtv_reset_ir_gpio(itv);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg)
+{
+       struct ivtv_open_id *id = NULL;
+
+       if (filp) id = (struct ivtv_open_id *)filp->private_data;
+
+       switch (cmd) {
+       case VIDIOC_QUERYCAP:{
+               struct v4l2_capability *vcap = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_QUERYCAP\n");
+
+               memset(vcap, 0, sizeof(*vcap));
+               strcpy(vcap->driver, IVTV_DRIVER_NAME);     /* driver name */
+               strcpy(vcap->card, itv->card_name);         /* card type */
+               strcpy(vcap->bus_info, pci_name(itv->dev)); /* bus info... */
+               vcap->version = IVTV_DRIVER_VERSION;        /* version */
+               vcap->capabilities = itv->v4l2_cap;         /* capabilities */
+
+               /* reserved.. must set to 0! */
+               vcap->reserved[0] = vcap->reserved[1] =
+                       vcap->reserved[2] = vcap->reserved[3] = 0;
+               break;
+       }
+
+       case VIDIOC_ENUMAUDIO:{
+               struct v4l2_audio *vin = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_ENUMAUDIO\n");
+
+               return ivtv_get_audio_input(itv, vin->index, vin);
+       }
+
+       case VIDIOC_G_AUDIO:{
+               struct v4l2_audio *vin = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_G_AUDIO\n");
+               vin->index = itv->audio_input;
+               return ivtv_get_audio_input(itv, vin->index, vin);
+       }
+
+       case VIDIOC_S_AUDIO:{
+               struct v4l2_audio *vout = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_S_AUDIO\n");
+
+               if (vout->index >= itv->nof_audio_inputs)
+                       return -EINVAL;
+               itv->audio_input = vout->index;
+               ivtv_audio_set_io(itv);
+               break;
+       }
+
+       case VIDIOC_ENUMAUDOUT:{
+               struct v4l2_audioout *vin = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_ENUMAUDOUT\n");
+
+               /* set it to defaults from our table */
+               return ivtv_get_audio_output(itv, vin->index, vin);
+       }
+
+       case VIDIOC_G_AUDOUT:{
+               struct v4l2_audioout *vin = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_G_AUDOUT\n");
+               vin->index = 0;
+               return ivtv_get_audio_output(itv, vin->index, vin);
+       }
+
+       case VIDIOC_S_AUDOUT:{
+               struct v4l2_audioout *vout = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_S_AUDOUT\n");
+
+               return ivtv_get_audio_output(itv, vout->index, vout);
+       }
+
+       case VIDIOC_ENUMINPUT:{
+               struct v4l2_input *vin = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_ENUMINPUT\n");
+
+               /* set it to defaults from our table */
+               return ivtv_get_input(itv, vin->index, vin);
+       }
+
+       case VIDIOC_ENUMOUTPUT:{
+               struct v4l2_output *vout = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_ENUMOUTPUT\n");
+
+               return ivtv_get_output(itv, vout->index, vout);
+       }
+
+       case VIDIOC_TRY_FMT:
+       case VIDIOC_S_FMT: {
+               struct v4l2_format *fmt = arg;
+
+               if (cmd == VIDIOC_S_FMT) {
+                       IVTV_DEBUG_IOCTL("VIDIOC_S_FMT\n");
+               } else {
+                       IVTV_DEBUG_IOCTL("VIDIOC_TRY_FMT\n");
+               }
+               return ivtv_try_or_set_fmt(itv, id->type, fmt, cmd == VIDIOC_S_FMT);
+       }
+
+       case VIDIOC_G_FMT: {
+               struct v4l2_format *fmt = arg;
+               int type = fmt->type;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_G_FMT\n");
+               memset(fmt, 0, sizeof(*fmt));
+               fmt->type = type;
+               return ivtv_get_fmt(itv, id->type, fmt);
+       }
+
+       case VIDIOC_S_CROP: {
+               struct v4l2_crop *crop = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_S_CROP\n");
+               if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       return -EINVAL;
+               return itv->video_dec_func(itv, VIDIOC_S_CROP, arg);
+       }
+
+       case VIDIOC_G_CROP: {
+               struct v4l2_crop *crop = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_G_CROP\n");
+               if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       return -EINVAL;
+               return itv->video_dec_func(itv, VIDIOC_G_CROP, arg);
+       }
+
+       case VIDIOC_ENUM_FMT: {
+               static struct v4l2_fmtdesc formats[] = {
+                       { 0, 0, 0,
+                         "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12,
+                         { 0, 0, 0, 0 }
+                       },
+                       { 1, 0, V4L2_FMT_FLAG_COMPRESSED,
+                         "MPEG", V4L2_PIX_FMT_MPEG,
+                         { 0, 0, 0, 0 }
+                       }
+               };
+               struct v4l2_fmtdesc *fmt = arg;
+               enum v4l2_buf_type type = fmt->type;
+
+               switch (type) {
+               case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+                       break;
+               case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+                       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+                               return -EINVAL;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               if (fmt->index > 1)
+                       return -EINVAL;
+               *fmt = formats[fmt->index];
+               fmt->type = type;
+               return 0;
+       }
+
+       case VIDIOC_G_INPUT:{
+               IVTV_DEBUG_IOCTL("VIDIOC_G_INPUT\n");
+
+               *(int *)arg = itv->active_input;
+               break;
+       }
+
+       case VIDIOC_S_INPUT:{
+               int inp = *(int *)arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_S_INPUT\n");
+
+               if (inp < 0 || inp >= itv->nof_inputs)
+                       return -EINVAL;
+
+               if (inp == itv->active_input) {
+                       IVTV_DEBUG_INFO("Input unchanged\n");
+                       break;
+               }
+               IVTV_DEBUG_INFO("Changing input from %d to %d\n",
+                               itv->active_input, inp);
+
+               itv->active_input = inp;
+               /* Set the audio input to whatever is appropriate for the
+                  input type. */
+               itv->audio_input = itv->card->video_inputs[inp].audio_index;
+
+               /* prevent others from messing with the streams until
+                  we're finished changing inputs. */
+               ivtv_mute(itv);
+               ivtv_video_set_io(itv);
+               ivtv_audio_set_io(itv);
+               ivtv_unmute(itv);
+               break;
+       }
+
+       case VIDIOC_G_OUTPUT:{
+               IVTV_DEBUG_IOCTL("VIDIOC_G_OUTPUT\n");
+
+               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+                       return -EINVAL;
+               *(int *)arg = itv->active_output;
+               break;
+       }
+
+       case VIDIOC_S_OUTPUT:{
+               int outp = *(int *)arg;
+               struct v4l2_routing route;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_S_OUTPUT\n");
+
+               if (outp >= itv->card->nof_outputs)
+                       return -EINVAL;
+
+               if (outp == itv->active_output) {
+                       IVTV_DEBUG_INFO("Output unchanged\n");
+                       break;
+               }
+               IVTV_DEBUG_INFO("Changing output from %d to %d\n",
+                          itv->active_output, outp);
+
+               itv->active_output = outp;
+               route.input = SAA7127_INPUT_TYPE_NORMAL;
+               route.output = itv->card->video_outputs[outp].video_output;
+               ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+               break;
+       }
+
+       case VIDIOC_G_FREQUENCY:{
+               struct v4l2_frequency *vf = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_G_FREQUENCY\n");
+
+               if (vf->tuner != 0)
+                       return -EINVAL;
+               ivtv_call_i2c_clients(itv, cmd, arg);
+               break;
+       }
+
+       case VIDIOC_S_FREQUENCY:{
+               struct v4l2_frequency vf = *(struct v4l2_frequency *)arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_S_FREQUENCY\n");
+
+               if (vf.tuner != 0)
+                       return -EINVAL;
+
+               ivtv_mute(itv);
+               IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency);
+               ivtv_call_i2c_clients(itv, cmd, &vf);
+               ivtv_unmute(itv);
+               break;
+       }
+
+       case VIDIOC_ENUMSTD:{
+               struct v4l2_standard *vs = arg;
+               int idx = vs->index;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_ENUMSTD\n");
+
+               if (idx < 0 || idx >= ARRAY_SIZE(enum_stds))
+                       return -EINVAL;
+
+               *vs = (enum_stds[idx].std & V4L2_STD_525_60) ?
+                               ivtv_std_60hz : ivtv_std_50hz;
+               vs->index = idx;
+               vs->id = enum_stds[idx].std;
+               strcpy(vs->name, enum_stds[idx].name);
+               break;
+       }
+
+       case VIDIOC_G_STD:{
+               IVTV_DEBUG_IOCTL("VIDIOC_G_STD\n");
+               *(v4l2_std_id *) arg = itv->std;
+               break;
+       }
+
+       case VIDIOC_S_STD: {
+               v4l2_std_id std = *(v4l2_std_id *) arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_S_STD\n");
+
+               if ((std & V4L2_STD_ALL) == 0)
+                       return -EINVAL;
+
+               if (std == itv->std)
+                       break;
+
+               if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ||
+                   atomic_read(&itv->capturing) > 0 ||
+                   atomic_read(&itv->decoding) > 0) {
+                       /* Switching standard would turn off the radio or mess
+                          with already running streams, prevent that by
+                          returning EBUSY. */
+                       return -EBUSY;
+               }
+
+               itv->std = std;
+               itv->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0;
+               itv->params.is_50hz = itv->is_50hz = !itv->is_60hz;
+               itv->params.width = 720;
+               itv->params.height = itv->is_50hz ? 576 : 480;
+               itv->vbi.count = itv->is_50hz ? 18 : 12;
+               itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
+               itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
+               if (itv->hw_flags & IVTV_HW_CX25840) {
+                       itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284;
+               }
+               IVTV_DEBUG_INFO("Switching standard to %llx.\n", itv->std);
+
+               /* Tuner */
+               ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+
+               if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+                       /* set display standard */
+                       itv->std_out = std;
+                       itv->is_out_60hz = itv->is_60hz;
+                       itv->is_out_50hz = itv->is_50hz;
+                       ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out);
+                       ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
+                       itv->main_rect.left = itv->main_rect.top = 0;
+                       itv->main_rect.width = 720;
+                       itv->main_rect.height = itv->params.height;
+                       ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+                               720, itv->main_rect.height, 0, 0);
+               }
+               break;
+       }
+
+       case VIDIOC_S_TUNER: {  /* Setting tuner can only set audio mode */
+               struct v4l2_tuner *vt = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_S_TUNER\n");
+
+               if (vt->index != 0)
+                       return -EINVAL;
+
+               ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt);
+               break;
+       }
+
+       case VIDIOC_G_TUNER: {
+               struct v4l2_tuner *vt = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_G_TUNER\n");
+
+               if (vt->index != 0)
+                       return -EINVAL;
+
+               memset(vt, 0, sizeof(*vt));
+               ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt);
+
+               if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+                       strcpy(vt->name, "ivtv Radio Tuner");
+                       vt->type = V4L2_TUNER_RADIO;
+               } else {
+                       strcpy(vt->name, "ivtv TV Tuner");
+                       vt->type = V4L2_TUNER_ANALOG_TV;
+               }
+               break;
+       }
+
+       case VIDIOC_G_SLICED_VBI_CAP: {
+               struct v4l2_sliced_vbi_cap *cap = arg;
+               int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+               int f, l;
+               enum v4l2_buf_type type = cap->type;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_G_SLICED_VBI_CAP\n");
+               memset(cap, 0, sizeof(*cap));
+               cap->type = type;
+               if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
+                       for (f = 0; f < 2; f++) {
+                               for (l = 0; l < 24; l++) {
+                                       if (valid_service_line(f, l, itv->is_50hz)) {
+                                               cap->service_lines[f][l] = set;
+                                       }
+                               }
+                       }
+                       return 0;
+               }
+               if (type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+                       if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+                               return -EINVAL;
+                       if (itv->is_60hz) {
+                               cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+                               cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+                       } else {
+                               cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+                               cap->service_lines[0][16] = V4L2_SLICED_VPS;
+                       }
+                       return 0;
+               }
+               return -EINVAL;
+       }
+
+       case VIDIOC_LOG_STATUS:
+       {
+               int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT;
+               struct v4l2_input vidin;
+               struct v4l2_audio audin;
+               int i;
+
+               IVTV_INFO("=================  START STATUS CARD #%d  =================\n", itv->num);
+               if (itv->hw_flags & IVTV_HW_TVEEPROM) {
+                       struct tveeprom tv;
+
+                       ivtv_read_eeprom(itv, &tv);
+               }
+               ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL);
+               ivtv_get_input(itv, itv->active_input, &vidin);
+               ivtv_get_audio_input(itv, itv->audio_input, &audin);
+               IVTV_INFO("Video Input: %s\n", vidin.name);
+               IVTV_INFO("Audio Input: %s\n", audin.name);
+               if (has_output) {
+                       struct v4l2_output vidout;
+                       struct v4l2_audioout audout;
+                       int mode = itv->output_mode;
+                       static const char * const output_modes[] = {
+                               "None",
+                               "MPEG Streaming",
+                               "YUV Streaming",
+                               "YUV Frames",
+                               "Passthrough",
+                       };
+
+                       ivtv_get_output(itv, itv->active_output, &vidout);
+                       ivtv_get_audio_output(itv, 0, &audout);
+                       IVTV_INFO("Video Output: %s\n", vidout.name);
+                       IVTV_INFO("Audio Output: %s\n", audout.name);
+                       if (mode < 0 || mode > OUT_PASSTHROUGH)
+                               mode = OUT_NONE;
+                       IVTV_INFO("Output Mode: %s\n", output_modes[mode]);
+               }
+               IVTV_INFO("Tuner: %s\n",
+                       test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
+               cx2341x_log_status(&itv->params, itv->name);
+               IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
+               for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+                       struct ivtv_stream *s = &itv->streams[i];
+
+                       if (s->v4l2dev == NULL || s->buffers == 0)
+                               continue;
+                       IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags,
+                                       (s->buffers - s->q_free.buffers) * 100 / s->buffers,
+                                       (s->buffers * s->buf_size) / 1024, s->buffers);
+               }
+               IVTV_INFO("Read MPEG/VBI: %lld/%lld bytes\n", itv->mpg_data_received, itv->vbi_data_inserted);
+               IVTV_INFO("==================  END STATUS CARD #%d  ==================\n", itv->num);
+               break;
+       }
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int ivtv_ivtv_ioctls(struct file *filp, unsigned int cmd, void *arg)
+{
+       struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+       struct ivtv *itv = id->itv;
+       int nonblocking = filp->f_flags & O_NONBLOCK;
+       struct ivtv_stream *s = &itv->streams[id->type];
+
+       switch (cmd) {
+       case IVTV_IOC_DMA_FRAME: {
+               struct ivtv_dma_frame *args = arg;
+
+               IVTV_DEBUG_IOCTL("IVTV_IOC_DMA_FRAME\n");
+               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+                       return -EINVAL;
+               if (args->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+                       return -EINVAL;
+               if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL)
+                       return 0;
+               if (ivtv_claim_stream(id, id->type)) {
+                       return -EBUSY;
+               }
+               if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) {
+                       ivtv_release_stream(s);
+                       return -EBUSY;
+               }
+               if (args->y_source == NULL)
+                       return 0;
+               return ivtv_yuv_prep_frame(itv, args);
+       }
+
+       case VIDEO_GET_PTS: {
+               u32 data[CX2341X_MBOX_MAX_DATA];
+               u64 *pts = arg;
+
+               IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n");
+               if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
+                       *pts = s->dma_pts;
+                       break;
+               }
+               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+                       return -EINVAL;
+
+               if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
+                       *pts = (u64) ((u64)itv->last_dec_timing[2] << 32) |
+                                       (u64)itv->last_dec_timing[1];
+                       break;
+               }
+               *pts = 0;
+               if (atomic_read(&itv->decoding)) {
+                       if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
+                               IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
+                               return -EIO;
+                       }
+                       memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
+                       set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+                       *pts = (u64) ((u64) data[2] << 32) | (u64) data[1];
+                       /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/
+               }
+               break;
+       }
+
+       case VIDEO_GET_FRAME_COUNT: {
+               u32 data[CX2341X_MBOX_MAX_DATA];
+               u64 *frame = arg;
+
+               IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n");
+               if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
+                       *frame = 0;
+                       break;
+               }
+               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+                       return -EINVAL;
+
+               if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
+                       *frame = itv->last_dec_timing[0];
+                       break;
+               }
+               *frame = 0;
+               if (atomic_read(&itv->decoding)) {
+                       if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
+                               IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
+                               return -EIO;
+                       }
+                       memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
+                       set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+                       *frame = data[0];
+               }
+               break;
+       }
+
+       case VIDEO_PLAY: {
+               struct video_command vc;
+
+               IVTV_DEBUG_IOCTL("VIDEO_PLAY\n");
+               memset(&vc, 0, sizeof(vc));
+               vc.cmd = VIDEO_CMD_PLAY;
+               return ivtv_video_command(itv, id, &vc, 0);
+       }
+
+       case VIDEO_STOP: {
+               struct video_command vc;
+
+               IVTV_DEBUG_IOCTL("VIDEO_STOP\n");
+               memset(&vc, 0, sizeof(vc));
+               vc.cmd = VIDEO_CMD_STOP;
+               vc.flags = VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY;
+               return ivtv_video_command(itv, id, &vc, 0);
+       }
+
+       case VIDEO_FREEZE: {
+               struct video_command vc;
+
+               IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n");
+               memset(&vc, 0, sizeof(vc));
+               vc.cmd = VIDEO_CMD_FREEZE;
+               return ivtv_video_command(itv, id, &vc, 0);
+       }
+
+       case VIDEO_CONTINUE: {
+               struct video_command vc;
+
+               IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n");
+               memset(&vc, 0, sizeof(vc));
+               vc.cmd = VIDEO_CMD_CONTINUE;
+               return ivtv_video_command(itv, id, &vc, 0);
+       }
+
+       case VIDEO_COMMAND:
+       case VIDEO_TRY_COMMAND: {
+               struct video_command *vc = arg;
+               int try = (cmd == VIDEO_TRY_COMMAND);
+
+               if (try)
+                       IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND\n");
+               else
+                       IVTV_DEBUG_IOCTL("VIDEO_COMMAND\n");
+               return ivtv_video_command(itv, id, vc, try);
+       }
+
+       case VIDEO_GET_EVENT: {
+               struct video_event *ev = arg;
+               DEFINE_WAIT(wait);
+
+               IVTV_DEBUG_IOCTL("VIDEO_GET_EVENT\n");
+               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+                       return -EINVAL;
+               memset(ev, 0, sizeof(*ev));
+               set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+
+               while (1) {
+                       if (test_and_clear_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
+                               ev->type = VIDEO_EVENT_DECODER_STOPPED;
+                       else if (test_and_clear_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) {
+                               ev->type = VIDEO_EVENT_VSYNC;
+                               ev->timestamp = test_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags) ?
+                                       1 : 0;
+                               clear_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+                       }
+                       if (ev->type)
+                               return 0;
+                       if (nonblocking)
+                               return -EAGAIN;
+                       /* wait for event */
+                       prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE);
+                       if ((itv->i_flags & (IVTV_F_I_EV_DEC_STOPPED|IVTV_F_I_EV_VSYNC)) == 0)
+                               schedule();
+                       finish_wait(&itv->event_waitq, &wait);
+                       if (signal_pending(current)) {
+                               /* return if a signal was received */
+                               IVTV_DEBUG_INFO("User stopped wait for event\n");
+                               return -EINTR;
+                       }
+               }
+               break;
+       }
+
+       case VIDIOC_G_ENC_INDEX: {
+               struct v4l2_enc_idx *idx = arg;
+               int i;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_G_ENC_INDEX\n");
+               idx->entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) %
+                                       IVTV_MAX_PGM_INDEX;
+               if (idx->entries > V4L2_ENC_IDX_ENTRIES)
+                       idx->entries = V4L2_ENC_IDX_ENTRIES;
+               for (i = 0; i < idx->entries; i++) {
+                       idx->entry[i] = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX];
+               }
+               itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX;
+               break;
+       }
+
+       case VIDIOC_ENCODER_CMD:
+       case VIDIOC_TRY_ENCODER_CMD: {
+               struct v4l2_encoder_cmd *enc = arg;
+               int try = cmd == VIDIOC_TRY_ENCODER_CMD;
+
+               if (try)
+                       IVTV_DEBUG_IOCTL("VIDIOC_TRY_ENCODER_CMD\n");
+               else
+                       IVTV_DEBUG_IOCTL("VIDIOC_ENCODER_CMD\n");
+               switch (enc->cmd) {
+               case V4L2_ENC_CMD_START:
+                       return ivtv_start_capture(id);
+
+               case V4L2_ENC_CMD_STOP:
+                       ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
+                       return 0;
+
+               case V4L2_ENC_CMD_PAUSE:
+                       if (!atomic_read(&itv->capturing))
+                               return -EPERM;
+                       if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+                               return 0;
+                       ivtv_mute(itv);
+                       ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0);
+                       break;
+
+               case V4L2_ENC_CMD_RESUME:
+                       if (!atomic_read(&itv->capturing))
+                               return -EPERM;
+                       if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+                               return 0;
+                       ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
+                       ivtv_unmute(itv);
+                       break;
+               }
+               break;
+       }
+
+       case VIDIOC_G_FBUF: {
+               struct v4l2_framebuffer *fb = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_G_FBUF\n");
+               memset(fb, 0, sizeof(*fb));
+               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+                       break;
+               fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY |
+                       V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_GLOBAL_ALPHA;
+               fb->fmt.pixelformat = itv->osd_pixelformat;
+               fb->fmt.width = itv->osd_rect.width;
+               fb->fmt.height = itv->osd_rect.height;
+               fb->fmt.left = itv->osd_rect.left;
+               fb->fmt.top = itv->osd_rect.top;
+               fb->base = (void *)itv->osd_video_pbase;
+               if (itv->osd_global_alpha_state)
+                       fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
+               if (itv->osd_local_alpha_state)
+                       fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+               if (itv->osd_color_key_state)
+                       fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+               break;
+       }
+
+       case VIDIOC_S_FBUF: {
+               struct v4l2_framebuffer *fb = arg;
+
+               IVTV_DEBUG_IOCTL("VIDIOC_S_FBUF\n");
+               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+                       break;
+               itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
+               itv->osd_local_alpha_state = (fb->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) != 0;
+               itv->osd_color_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
+               break;
+       }
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp,
+                             unsigned int cmd, void *arg)
+{
+       struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+       struct ivtv *itv = id->itv;
+
+       IVTV_DEBUG_IOCTL("v4l2 ioctl 0x%08x\n", cmd);
+
+       switch (cmd) {
+       case VIDIOC_DBG_G_REGISTER:
+       case VIDIOC_DBG_S_REGISTER:
+       case VIDIOC_G_CHIP_IDENT:
+       case VIDIOC_INT_S_AUDIO_ROUTING:
+       case VIDIOC_INT_RESET:
+               return ivtv_internal_ioctls(filp, cmd, arg);
+
+       case VIDIOC_QUERYCAP:
+       case VIDIOC_ENUMINPUT:
+       case VIDIOC_G_INPUT:
+       case VIDIOC_S_INPUT:
+       case VIDIOC_ENUMOUTPUT:
+       case VIDIOC_G_OUTPUT:
+       case VIDIOC_S_OUTPUT:
+       case VIDIOC_G_FMT:
+       case VIDIOC_S_FMT:
+       case VIDIOC_TRY_FMT:
+       case VIDIOC_ENUM_FMT:
+       case VIDIOC_G_CROP:
+       case VIDIOC_S_CROP:
+       case VIDIOC_G_FREQUENCY:
+       case VIDIOC_S_FREQUENCY:
+       case VIDIOC_ENUMSTD:
+       case VIDIOC_G_STD:
+       case VIDIOC_S_STD:
+       case VIDIOC_S_TUNER:
+       case VIDIOC_G_TUNER:
+       case VIDIOC_ENUMAUDIO:
+       case VIDIOC_S_AUDIO:
+       case VIDIOC_G_AUDIO:
+       case VIDIOC_ENUMAUDOUT:
+       case VIDIOC_S_AUDOUT:
+       case VIDIOC_G_AUDOUT:
+       case VIDIOC_G_SLICED_VBI_CAP:
+       case VIDIOC_LOG_STATUS:
+               return ivtv_v4l2_ioctls(itv, filp, cmd, arg);
+
+       case VIDIOC_QUERYMENU:
+       case VIDIOC_QUERYCTRL:
+       case VIDIOC_S_CTRL:
+       case VIDIOC_G_CTRL:
+       case VIDIOC_S_EXT_CTRLS:
+       case VIDIOC_G_EXT_CTRLS:
+       case VIDIOC_TRY_EXT_CTRLS:
+               return ivtv_control_ioctls(itv, cmd, arg);
+
+       case IVTV_IOC_DMA_FRAME:
+       case VIDEO_GET_PTS:
+       case VIDEO_GET_FRAME_COUNT:
+       case VIDEO_GET_EVENT:
+       case VIDEO_PLAY:
+       case VIDEO_STOP:
+       case VIDEO_FREEZE:
+       case VIDEO_CONTINUE:
+       case VIDEO_COMMAND:
+       case VIDEO_TRY_COMMAND:
+       case VIDIOC_G_ENC_INDEX:
+       case VIDIOC_ENCODER_CMD:
+       case VIDIOC_TRY_ENCODER_CMD:
+       case VIDIOC_G_FBUF:
+       case VIDIOC_S_FBUF:
+               return ivtv_ivtv_ioctls(filp, cmd, arg);
+
+       case 0x00005401:        /* Handle isatty() calls */
+               return -EINVAL;
+       default:
+               return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
+                                                  ivtv_v4l2_do_ioctl);
+       }
+       return 0;
+}
+
+int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+                   unsigned long arg)
+{
+       struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+       struct ivtv *itv = id->itv;
+
+       /* Filter dvb ioctls that cannot be handled by video_usercopy */
+       switch (cmd) {
+       case VIDEO_SELECT_SOURCE:
+               IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n");
+               if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+                       return -EINVAL;
+               return ivtv_passthrough_mode(itv, arg == VIDEO_SOURCE_DEMUX);
+
+       case AUDIO_SET_MUTE:
+               IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n");
+               itv->speed_mute_audio = arg;
+               return 0;
+
+       case AUDIO_CHANNEL_SELECT:
+               IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n");
+               if (arg > AUDIO_STEREO_SWAPPED)
+                       return -EINVAL;
+               itv->audio_stereo_mode = arg;
+               ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+               return 0;
+
+       case AUDIO_BILINGUAL_CHANNEL_SELECT:
+               IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n");
+               if (arg > AUDIO_STEREO_SWAPPED)
+                       return -EINVAL;
+               itv->audio_bilingual_mode = arg;
+               ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+               return 0;
+
+       default:
+               break;
+       }
+       return video_usercopy(inode, filp, cmd, arg, ivtv_v4l2_do_ioctl);
+}
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.h b/drivers/media/video/ivtv/ivtv-ioctl.h
new file mode 100644 (file)
index 0000000..cbccf7a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+    ioctl system call
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+u16 service2vbi(int type);
+void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
+u16 get_service_set(struct v4l2_sliced_vbi_format *fmt);
+int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+                   unsigned long arg);
+int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg);
+void ivtv_set_osd_alpha(struct ivtv *itv);
+int ivtv_set_speed(struct ivtv *itv, int speed);
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
new file mode 100644 (file)
index 0000000..0656e18
--- /dev/null
@@ -0,0