]> err.no Git - linux-2.6/commitdiff
V4L/DVB (5345): ivtv driver for Conexant cx23416/cx23415 MPEG encoder/decoder
authorHans Verkuil <hverkuil@xs4all.nl>
Fri, 27 Apr 2007 15:31:25 +0000 (12:31 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Fri, 27 Apr 2007 18:43:50 +0000 (15:43 -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 fa0a876791902b7d40f5c043f4a81fa17c22507a..639e8b6c35b11dd03b72bef6a26be7b0521553b6 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 384f01c133c5ee2bada4e71d1b89a9194091b673..9c2de501612f4c1c199702d0f96c80f077f06416 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 +1,818 @@
+/* interrupt handling
+    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-firmware.h"
+#include "ivtv-fileops.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-vbi.h"
+
+#define DMA_MAGIC_COOKIE 0x000001fe
+
+#define SLICED_VBI_PIO 1
+
+static void ivtv_dma_dec_start(struct ivtv_stream *s);
+
+static const int ivtv_stream_map[] = {
+       IVTV_ENC_STREAM_TYPE_MPG,
+       IVTV_ENC_STREAM_TYPE_YUV,
+       IVTV_ENC_STREAM_TYPE_PCM,
+       IVTV_ENC_STREAM_TYPE_VBI,
+};
+
+static inline int ivtv_use_pio(struct ivtv_stream *s)
+{
+       struct ivtv *itv = s->itv;
+
+       return s->dma == PCI_DMA_NONE ||
+           (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set);
+}
+
+/* Determine the required DMA size, setup enough buffers in the predma queue and
+   actually copy the data from the card to the buffers in case a PIO transfer is
+   required for this stream.
+ */
+static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+       struct ivtv *itv = s->itv;
+       struct ivtv_buffer *buf;
+       struct list_head *p;
+       u32 bytes_needed = 0;
+       u32 offset, size;
+       u32 UVoffset = 0, UVsize = 0;
+       int skip_bufs = s->q_predma.buffers;
+       int idx = s->SG_length;
+       int rc;
+
+       /* sanity checks */
+       if (s->v4l2dev == NULL) {
+               IVTV_DEBUG_WARN("Stream %s not started\n", s->name);
+               return -1;
+       }
+       if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+               IVTV_DEBUG_WARN("Stream %s not open\n", s->name);
+               return -1;
+       }
+
+       /* determine offset, size and PTS for the various streams */
+       switch (s->type) {
+               case IVTV_ENC_STREAM_TYPE_MPG:
+                       offset = data[1];
+                       size = data[2];
+                       s->dma_pts = 0;
+                       break;
+
+               case IVTV_ENC_STREAM_TYPE_YUV:
+                       offset = data[1];
+                       size = data[2];
+                       UVoffset = data[3];
+                       UVsize = data[4];
+                       s->dma_pts = ((u64) data[5] << 32) | data[6];
+                       break;
+
+               case IVTV_ENC_STREAM_TYPE_PCM:
+                       offset = data[1] + 12;
+                       size = data[2] - 12;
+                       s->dma_pts = read_dec(offset - 8) |
+                               ((u64)(read_dec(offset - 12)) << 32);
+                       if (itv->has_cx23415)
+                               offset += IVTV_DECODER_OFFSET;
+                       break;
+
+               case IVTV_ENC_STREAM_TYPE_VBI:
+                       size = itv->vbi.enc_size * itv->vbi.fpi;
+                       offset = read_enc(itv->vbi.enc_start - 4) + 12;
+                       if (offset == 12) {
+                               IVTV_DEBUG_INFO("VBI offset == 0\n");
+                               return -1;
+                       }
+                       s->dma_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32);
+                       break;
+
+               case IVTV_DEC_STREAM_TYPE_VBI:
+                       size = read_dec(itv->vbi.dec_start + 4) + 8;
+                       offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start;
+                       s->dma_pts = 0;
+                       offset += IVTV_DECODER_OFFSET;
+                       break;
+               default:
+                       /* shouldn't happen */
+                       return -1;
+       }
+
+       /* if this is the start of the DMA then fill in the magic cookie */
+       if (s->SG_length == 0) {
+               if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
+                   s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
+                       s->dma_backup = read_dec(offset - IVTV_DECODER_OFFSET);
+                       write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET);
+               }
+               else {
+                       s->dma_backup = read_enc(offset);
+                       write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset);
+               }
+               s->dma_offset = offset;
+       }
+
+       bytes_needed = size;
+       if (s->type == IVTV_ENC_STREAM_TYPE_YUV) {
+               /* The size for the Y samples needs to be rounded upwards to a
+                  multiple of the buf_size. The UV samples then start in the
+                  next buffer. */
+               bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size);
+               bytes_needed += UVsize;
+       }
+
+       IVTV_DEBUG_DMA("%s %s: 0x%08x bytes at 0x%08x\n",
+               ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset);
+
+       rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed);
+       if (rc < 0) { /* Insufficient buffers */
+               IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n",
+                               bytes_needed, s->name);
+               return -1;
+       }
+       if (rc && !s->buffers_stolen && (s->s_flags & IVTV_F_S_APPL_IO)) {
+               IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name);
+               IVTV_WARN("Cause: the application is not reading fast enough.\n");
+       }
+       s->buffers_stolen = rc;
+
+       /* got the buffers, now fill in SGarray (DMA) or copy the data from the card
+          to the buffers (PIO). */
+       buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
+       memset(buf->buf, 0, 128);
+       list_for_each(p, &s->q_predma.list) {
+               struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
+
+               if (skip_bufs-- > 0)
+                       continue;
+               if (!ivtv_use_pio(s)) {
+                       s->SGarray[idx].dst = cpu_to_le32(buf->dma_handle);
+                       s->SGarray[idx].src = cpu_to_le32(offset);
+                       s->SGarray[idx].size = cpu_to_le32(s->buf_size);
+               }
+               buf->bytesused = (size < s->buf_size) ? size : s->buf_size;
+
+               /* If PIO, then copy the data from the card to the buffer */
+               if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+                       memcpy_fromio(buf->buf, itv->dec_mem + offset - IVTV_DECODER_OFFSET, buf->bytesused);
+               }
+               else if (ivtv_use_pio(s)) {
+                       memcpy_fromio(buf->buf, itv->enc_mem + offset, buf->bytesused);
+               }
+
+               s->q_predma.bytesused += buf->bytesused;
+               size -= buf->bytesused;
+               offset += s->buf_size;
+
+               /* Sync SG buffers */
+               ivtv_buf_sync_for_device(s, buf);
+
+               if (size == 0) {        /* YUV */
+                       /* process the UV section */
+                       offset = UVoffset;
+                       size = UVsize;
+               }
+               idx++;
+       }
+       s->SG_length = idx;
+       return 0;
+}
+
+static void dma_post(struct ivtv_stream *s)
+{
+       struct ivtv *itv = s->itv;
+       struct ivtv_buffer *buf = NULL;
+       struct list_head *p;
+       u32 offset;
+       u32 *u32buf;
+       int x = 0;
+
+       if (ivtv_use_pio(s)) {
+               if (s->q_predma.bytesused)
+                       ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+               s->SG_length = 0;
+       }
+       IVTV_DEBUG_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA",
+                       s->name, s->dma_offset);
+       list_for_each(p, &s->q_dma.list) {
+               buf = list_entry(p, struct ivtv_buffer, list);
+               u32buf = (u32 *)buf->buf;
+
+               /* Sync Buffer */
+               ivtv_buf_sync_for_cpu(s, buf);
+
+               if (x == 0) {
+                       offset = s->dma_last_offset;
+                       if (u32buf[offset / 4] != DMA_MAGIC_COOKIE)
+                       {
+                               for (offset = 0; offset < 64; offset++) {
+                                       if (u32buf[offset] == DMA_MAGIC_COOKIE) {
+                                               break;
+                                       }
+                               }
+                               offset *= 4;
+                               if (offset == 256) {
+                                       IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name);
+                                       offset = s->dma_last_offset;
+                               }
+                               if (s->dma_last_offset != offset)
+                                       IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset);
+                               s->dma_last_offset = offset;
+                       }
+                       if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
+                                               s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
+                               write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET);
+                       }
+                       else {
+                               write_enc_sync(0, s->dma_offset);
+                       }
+                       if (offset) {
+                               buf->bytesused -= offset;
+                               memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset);
+                       }
+                       *u32buf = cpu_to_le32(s->dma_backup);
+               }
+               x++;
+               /* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */
+               if (s->type == IVTV_ENC_STREAM_TYPE_MPG ||
+                   s->type == IVTV_ENC_STREAM_TYPE_VBI)
+                       set_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags);
+       }
+       if (buf)
+               buf->bytesused += s->dma_last_offset;
+       if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+               /* Parse and Groom VBI Data */
+               s->q_dma.bytesused -= buf->bytesused;
+               ivtv_process_vbi_data(itv, buf, 0, s->type);
+               s->q_dma.bytesused += buf->bytesused;
+               if (s->id == -1) {
+                       ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
+                       return;
+               }
+       }
+       ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused);
+       if (s->id != -1)
+               wake_up(&s->waitq);
+}
+
+void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
+{
+       struct ivtv *itv = s->itv;
+       struct ivtv_buffer *buf;
+       struct list_head *p;
+       u32 y_size = itv->params.height * itv->params.width;
+       u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET;
+       int y_done = 0;
+       int bytes_written = 0;
+       unsigned long flags = 0;
+       int idx = 0;
+
+       IVTV_DEBUG_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset);
+       buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
+       list_for_each(p, &s->q_predma.list) {
+               struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
+
+               /* YUV UV Offset from Y Buffer */
+               if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && bytes_written >= y_size) {
+                       offset = uv_offset;
+                       y_done = 1;
+               }
+               s->SGarray[idx].src = cpu_to_le32(buf->dma_handle);
+               s->SGarray[idx].dst = cpu_to_le32(offset);
+               s->SGarray[idx].size = cpu_to_le32(buf->bytesused);
+
+               offset += buf->bytesused;
+               bytes_written += buf->bytesused;
+
+               /* Sync SG buffers */
+               ivtv_buf_sync_for_device(s, buf);
+               idx++;
+       }
+       s->SG_length = idx;
+
+       /* Mark last buffer size for Interrupt flag */
+       s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+       /* Sync Hardware SG List of buffers */
+       ivtv_stream_sync_for_device(s);
+       if (lock)
+               spin_lock_irqsave(&itv->dma_reg_lock, flags);
+       if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+               ivtv_dma_dec_start(s);
+       }
+       else {
+               set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+       }
+       if (lock)
+               spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
+}
+
+/* start the encoder DMA */
+static void ivtv_dma_enc_start(struct ivtv_stream *s)
+{
+       struct ivtv *itv = s->itv;
+       struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+       int i;
+
+       if (s->q_predma.bytesused)
+               ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+       IVTV_DEBUG_DMA("start DMA for %s\n", s->name);
+       s->SGarray[s->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s->SGarray[s->SG_length - 1].size) + 256);
+
+       /* If this is an MPEG stream, and VBI data is also pending, then append the
+          VBI DMA to the MPEG DMA and transfer both sets of data at once.
+
+          VBI DMA is a second class citizen compared to MPEG and mixing them together
+          will confuse the firmware (the end of a VBI DMA is seen as the end of a
+          MPEG DMA, thus effectively dropping an MPEG frame). So instead we make
+          sure we only use the MPEG DMA to transfer the VBI DMA if both are in
+          use. This way no conflicts occur. */
+       clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
+       if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->SG_length &&
+                       s->SG_length + s_vbi->SG_length <= s->buffers) {
+               ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused);
+               s_vbi->SGarray[s_vbi->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s_vbi->SGarray[s->SG_length - 1].size) + 256);
+               for (i = 0; i < s_vbi->SG_length; i++) {
+                       s->SGarray[s->SG_length++] = s_vbi->SGarray[i];
+               }
+               itv->vbi.dma_offset = s_vbi->dma_offset;
+               s_vbi->SG_length = 0;
+               set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
+               IVTV_DEBUG_DMA("include DMA for %s\n", s->name);
+       }
+
+       /* Mark last buffer size for Interrupt flag */
+       s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+       /* Sync Hardware SG List of buffers */
+       ivtv_stream_sync_for_device(s);
+       write_reg(s->SG_handle, IVTV_REG_ENCDMAADDR);
+       write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);
+       set_bit(IVTV_F_I_DMA, &itv->i_flags);
+       itv->cur_dma_stream = s->type;
+       itv->dma_timer.expires = jiffies + HZ / 10;
+       add_timer(&itv->dma_timer);
+}
+
+static void ivtv_dma_dec_start(struct ivtv_stream *s)
+{
+       struct ivtv *itv = s->itv;
+
+       if (s->q_predma.bytesused)
+               ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+       IVTV_DEBUG_DMA("start DMA for %s\n", s->name);
+       /* put SG Handle into register 0x0c */
+       write_reg(s->SG_handle, IVTV_REG_DECDMAADDR);
+       write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+       set_bit(IVTV_F_I_DMA, &itv->i_flags);
+       itv->cur_dma_stream = s->type;
+       itv->dma_timer.expires = jiffies + HZ / 10;
+       add_timer(&itv->dma_timer);
+}
+
+static void ivtv_irq_dma_read(struct ivtv *itv)
+{
+       struct ivtv_stream *s = NULL;
+       struct ivtv_buffer *buf;
+       int hw_stream_type;
+
+       IVTV_DEBUG_IRQ("DEC DMA READ\n");
+       del_timer(&itv->dma_timer);
+       if (read_reg(IVTV_REG_DMASTATUS) & 0x14) {
+               IVTV_DEBUG_WARN("DEC DMA ERROR %x\n", read_reg(IVTV_REG_DMASTATUS));
+               write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+       }
+       if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+               if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
+                       s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+                       hw_stream_type = 2;
+               }
+               else {
+                       s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+                       hw_stream_type = 0;
+               }
+               IVTV_DEBUG_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused);
+
+               ivtv_stream_sync_for_cpu(s);
+
+               /* For some reason must kick the firmware, like PIO mode,
+                  I think this tells the firmware we are done and the size
+                  of the xfer so it can calculate what we need next.
+                  I think we can do this part ourselves but would have to
+                  fully calculate xfer info ourselves and not use interrupts
+                */
+               ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused,
+                               hw_stream_type);
+
+               /* Free last DMA call */
+               while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) {
+                       ivtv_buf_sync_for_cpu(s, buf);
+                       ivtv_enqueue(s, buf, &s->q_free);
+               }
+               wake_up(&s->waitq);
+       }
+       clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+       clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+       itv->cur_dma_stream = -1;
+       wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_dma_complete(struct ivtv *itv)
+{
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       struct ivtv_stream *s;
+
+       del_timer(&itv->dma_timer);
+       ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);
+       IVTV_DEBUG_IRQ("ENC DMA COMPLETE %x %d\n", data[0], data[1]);
+       if (test_and_clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags))
+               data[1] = 3;
+       else if (data[1] > 2)
+               return;
+       s = &itv->streams[ivtv_stream_map[data[1]]];
+       if (data[0] & 0x18) {
+               IVTV_DEBUG_WARN("ENC DMA ERROR %x\n", data[0]);
+               write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+               ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[1]);
+       }
+       s->SG_length = 0;
+       clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+       itv->cur_dma_stream = -1;
+       dma_post(s);
+       ivtv_stream_sync_for_cpu(s);
+       if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
+               u32 tmp;
+
+               s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+               tmp = s->dma_offset;
+               s->dma_offset = itv->vbi.dma_offset;
+               dma_post(s);
+               s->dma_offset = tmp;
+       }
+       wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_dma_err(struct ivtv *itv)
+{
+       u32 data[CX2341X_MBOX_MAX_DATA];
+
+       del_timer(&itv->dma_timer);
+       ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);
+       IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1],
+                                       read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
+       if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) &&
+           itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) {
+               struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream];
+
+               /* retry */
+               write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+               if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
+                       ivtv_dma_dec_start(s);
+               else
+                       ivtv_dma_enc_start(s);
+               return;
+       }
+       clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+       clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+       itv->cur_dma_stream = -1;
+       wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_start_cap(struct ivtv *itv)
+{
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       struct ivtv_stream *s;
+
+       /* Get DMA destination and size arguments from card */
+       ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, data);
+       IVTV_DEBUG_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]);
+
+       if (data[0] > 2 || data[1] == 0 || data[2] == 0) {
+               IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n",
+                               data[0], data[1], data[2]);
+               return;
+       }
+       clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
+       s = &itv->streams[ivtv_stream_map[data[0]]];
+       if (!stream_enc_dma_append(s, data)) {
+               if (ivtv_use_pio(s)) {
+                       dma_post(s);
+                       ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[0]);
+               }
+               else {
+                       set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+               }
+       }
+}
+
+static void ivtv_irq_enc_vbi_cap(struct ivtv *itv)
+{
+       struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       struct ivtv_stream *s;
+
+       IVTV_DEBUG_IRQ("ENC START VBI CAP\n");
+       s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+
+       if (ivtv_use_pio(s)) {
+               if (stream_enc_dma_append(s, data))
+                       return;
+               if (s->q_predma.bytesused)
+                       ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+               s->SG_length = 0;
+               dma_post(s);
+               return;
+       }
+       /* If more than two VBI buffers are pending, then
+          clear the old ones and start with this new one.
+          This can happen during transition stages when MPEG capturing is
+          started, but the first interrupts haven't arrived yet. During
+          that period VBI requests can accumulate without being able to
+          DMA the data. Since at most four VBI DMA buffers are available,
+          we just drop the old requests when there are already three
+          requests queued. */
+       if (s->SG_length > 2) {
+               struct list_head *p;
+               list_for_each(p, &s->q_predma.list) {
+                       struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
+                       ivtv_buf_sync_for_cpu(s, buf);
+               }
+               ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
+               s->SG_length = 0;
+       }
+       /* if we can append the data, and the MPEG stream isn't capturing,
+          then start a DMA request for just the VBI data. */
+       if (!stream_enc_dma_append(s, data) &&
+                       !test_bit(IVTV_F_S_STREAMING, &s_mpg->s_flags)) {
+               set_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
+               set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+       }
+}
+
+static void ivtv_irq_dev_vbi_reinsert(struct ivtv *itv)
+{
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
+
+       IVTV_DEBUG_IRQ("DEC VBI REINSERT\n");
+       if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) &&
+                       !stream_enc_dma_append(s, data)) {
+               dma_post(s);
+       }
+}
+
+static void ivtv_irq_dec_data_req(struct ivtv *itv)
+{
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       struct ivtv_stream *s;
+
+       /* YUV or MPG */
+       ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
+
+       if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
+               itv->dma_data_req_size = itv->params.width * itv->params.height * 3 / 2;
+               itv->dma_data_req_offset = data[1] ? data[1] : yuv_offset[0];
+               s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+       }
+       else {
+               itv->dma_data_req_size = data[2] >= 0x10000 ? 0x10000 : data[2];
+               itv->dma_data_req_offset = data[1];
+               s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+       }
+       IVTV_DEBUG_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused,
+                      itv->dma_data_req_offset, itv->dma_data_req_size);
+       if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) {
+               set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+       }
+       else {
+               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, 0);
+       }
+}
+
+static void ivtv_irq_vsync(struct ivtv *itv)
+{
+       /* The vsync interrupt is unusual in that it won't clear until
+        * the end of the first line for the current field, at which
+        * point it clears itself. This can result in repeated vsync
+        * interrupts, or a missed vsync. Read some of the registers
+        * to determine the line being displayed and ensure we handle
+        * one vsync per frame.
+        */
+       unsigned int frame = read_reg(0x28c0) & 1;
+       int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame);
+
+       if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n");
+
+       if (((frame ^ itv->yuv_info.lace_sync_field) == 0 && ((itv->lastVsyncFrame & 1) ^ itv->yuv_info.lace_sync_field)) ||
+                       (frame != (itv->lastVsyncFrame & 1) && !itv->yuv_info.frame_interlaced)) {
+               int next_dma_frame = last_dma_frame;
+
+               if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&itv->yuv_info.next_fill_frame)) {
+                       write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c);
+                       write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
+                       write_reg(yuv_offset[next_dma_frame] >> 4, 0x834);
+                       write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
+                       next_dma_frame = (next_dma_frame + 1) & 0x3;
+                       atomic_set(&itv->yuv_info.next_dma_frame, next_dma_frame);
+               }
+       }
+       if (frame != (itv->lastVsyncFrame & 1)) {
+               struct ivtv_stream *s = ivtv_get_output_stream(itv);
+
+               itv->lastVsyncFrame += 1;
+               if (frame == 0) {
+                       clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+                       clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
+               }
+               else {
+                       set_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
+               }
+               if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) {
+                       set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags);
+                       wake_up(&itv->event_waitq);
+               }
+               wake_up(&itv->vsync_waitq);
+               if (s)
+                       wake_up(&s->waitq);
+
+               /* Send VBI to saa7127 */
+               if (frame)
+                       vbi_schedule_work(itv);
+
+               /* Check if we need to update the yuv registers */
+               if ((itv->yuv_info.yuv_forced_update || itv->yuv_info.new_frame_info[last_dma_frame].update) && last_dma_frame != -1) {
+                       if (!itv->yuv_info.new_frame_info[last_dma_frame].update)
+                               last_dma_frame = (last_dma_frame - 1) & 3;
+
+                       if (itv->yuv_info.new_frame_info[last_dma_frame].src_w) {
+                               itv->yuv_info.update_frame = last_dma_frame;
+                               itv->yuv_info.new_frame_info[last_dma_frame].update = 0;
+                               itv->yuv_info.yuv_forced_update = 0;
+                               queue_work(itv->yuv_info.work_queues, &itv->yuv_info.work_queue);
+                       }
+               }
+       }
+}
+
+#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ)
+
+irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
+{
+       struct ivtv *itv = (struct ivtv *)dev_id;
+       u32 combo;
+       u32 stat;
+       int i;
+       u8 vsync_force = 0;
+
+       spin_lock(&itv->dma_reg_lock);
+       /* get contents of irq status register */
+       stat = read_reg(IVTV_REG_IRQSTATUS);
+
+       combo = ~itv->irqmask & stat;
+
+       /* Clear out IRQ */
+       if (combo) write_reg(combo, IVTV_REG_IRQSTATUS);
+
+       if (0 == combo) {
+               /* The vsync interrupt is unusual and clears itself. If we
+                * took too long, we may have missed it. Do some checks
+                */
+               if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
+                       /* vsync is enabled, see if we're in a new field */
+                       if ((itv->lastVsyncFrame & 1) != (read_reg(0x28c0) & 1)) {
+                               /* New field, looks like we missed it */
+                               IVTV_DEBUG_YUV("VSync interrupt missed %d\n",read_reg(0x28c0)>>16);
+                               vsync_force = 1;
+                       }
+               }
+
+               if (!vsync_force) {
+                       /* No Vsync expected, wasn't for us */
+                       spin_unlock(&itv->dma_reg_lock);
+                       return IRQ_NONE;
+               }
+       }
+
+       /* Exclude interrupts noted below from the output, otherwise the log is flooded with
+          these messages */
+       if (combo & ~0xff6d0400)
+               IVTV_DEBUG_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo);
+
+       if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) {
+               IVTV_DEBUG_IRQ("DEC DMA COMPLETE\n");
+       }
+
+       if (combo & IVTV_IRQ_DMA_READ) {
+               ivtv_irq_dma_read(itv);
+       }
+
+       if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) {
+               ivtv_irq_enc_dma_complete(itv);
+       }
+
+       if (combo & IVTV_IRQ_DMA_ERR) {
+               ivtv_irq_dma_err(itv);
+       }
+
+       if (combo & IVTV_IRQ_ENC_START_CAP) {
+               ivtv_irq_enc_start_cap(itv);
+       }
+
+       if (combo & IVTV_IRQ_ENC_VBI_CAP) {
+               ivtv_irq_enc_vbi_cap(itv);
+       }
+
+       if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) {
+               ivtv_irq_dev_vbi_reinsert(itv);
+       }
+
+       if (combo & IVTV_IRQ_ENC_EOS) {
+               IVTV_DEBUG_IRQ("ENC EOS\n");
+               set_bit(IVTV_F_I_EOS, &itv->i_flags);
+               wake_up(&itv->cap_w);
+       }
+
+       if (combo & IVTV_IRQ_DEC_DATA_REQ) {
+               ivtv_irq_dec_data_req(itv);
+       }
+
+       /* Decoder Vertical Sync - We can't rely on 'combo', so check if vsync enabled */
+       if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
+               ivtv_irq_vsync(itv);
+       }
+
+       if (combo & IVTV_IRQ_ENC_VIM_RST) {
+               IVTV_DEBUG_IRQ("VIM RST\n");
+               /*ivtv_vapi(itv, CX2341X_ENC_REFRESH_INPUT, 0); */
+       }
+
+       if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) {
+               IVTV_DEBUG_INFO("Stereo mode changed\n");
+       }
+
+       if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+               for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+                       int idx = (i + itv->irq_rr_idx++) % IVTV_MAX_STREAMS;
+                       struct ivtv_stream *s = &itv->streams[idx];
+
+                       if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags))
+                               continue;
+                       if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
+                               ivtv_dma_dec_start(s);
+                       else
+                               ivtv_dma_enc_start(s);
+                       break;
+               }
+               if (i == IVTV_MAX_STREAMS && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) {
+                       ivtv_udma_start(itv);
+               }
+       }
+
+       spin_unlock(&itv->dma_reg_lock);
+
+       /* If we've just handled a 'forced' vsync, it's safest to say it
+        * wasn't ours. Another device may have triggered it at just
+        * the right time.
+        */
+       return vsync_force ? IRQ_NONE : IRQ_HANDLED;
+}
+
+void ivtv_unfinished_dma(unsigned long arg)
+{
+       struct ivtv *itv = (struct ivtv *)arg;
+
+       if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+               return;
+       IVTV_ERR("DMA TIMEOUT %08x %d\n", read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
+
+       write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+       clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+       clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+       itv->cur_dma_stream = -1;
+       wake_up(&itv->dma_waitq);
+}
diff --git a/drivers/media/video/ivtv/ivtv-irq.h b/drivers/media/video/ivtv/ivtv-irq.h
new file mode 100644 (file)
index 0000000..ed96205
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+    interrupt handling
+    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
+ */
+
+irqreturn_t ivtv_irq_handler(int irq, void *dev_id);
+void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock);
+void ivtv_unfinished_dma(unsigned long arg);
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c
new file mode 100644 (file)
index 0000000..6ae42a3
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+    mailbox 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 <stdarg.h>
+
+#include "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+
+/* Firmware mailbox flags*/
+#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
+#define IVTV_MBOX_DRIVER_DONE   0x00000002
+#define IVTV_MBOX_DRIVER_BUSY   0x00000001
+#define IVTV_MBOX_FREE                 0x00000000
+
+/* Firmware mailbox standard timeout */
+#define IVTV_API_STD_TIMEOUT   0x02000000
+
+#define API_CACHE       (1 << 0)       /* Allow the command to be stored in the cache */
+#define API_RESULT      (1 << 1)       /* Allow 1 second for this cmd to end */
+#define API_FAST_RESULT         (3 << 1)       /* Allow 0.1 second for this cmd to end */
+#define API_DMA         (1 << 3)       /* DMA mailbox, has special handling */
+#define API_NO_WAIT_MB          (1 << 4)       /* Command may not wait for a free mailbox */
+#define API_NO_WAIT_RES         (1 << 5)       /* Command may not wait for the result */
+
+struct ivtv_api_info {
+       int flags;              /* Flags, see above */
+       const char *name;       /* The name of the command */
+};
+
+#define API_ENTRY(x, f) [x] = { (f), #x }
+
+static const struct ivtv_api_info api_info[256] = {
+       /* MPEG encoder API */
+       API_ENTRY(CX2341X_ENC_PING_FW,                  API_FAST_RESULT),
+       API_ENTRY(CX2341X_ENC_START_CAPTURE,            API_RESULT),
+       API_ENTRY(CX2341X_ENC_STOP_CAPTURE,             API_RESULT),
+       API_ENTRY(CX2341X_ENC_SET_AUDIO_ID,             API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_VIDEO_ID,             API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_PCR_ID,               API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_FRAME_RATE,           API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_FRAME_SIZE,           API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_BIT_RATE,             API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_GOP_PROPERTIES,       API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_ASPECT_RATIO,         API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_MODE,      API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_PROPS,     API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_CORING_LEVELS,        API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_SPATIAL_FILTER_TYPE,  API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_VBI_LINE,             API_RESULT),
+       API_ENTRY(CX2341X_ENC_SET_STREAM_TYPE,          API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_OUTPUT_PORT,          API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_AUDIO_PROPERTIES,     API_CACHE),
+       API_ENTRY(CX2341X_ENC_HALT_FW,                  API_FAST_RESULT),
+       API_ENTRY(CX2341X_ENC_GET_VERSION,              API_FAST_RESULT),
+       API_ENTRY(CX2341X_ENC_SET_GOP_CLOSURE,          API_CACHE),
+       API_ENTRY(CX2341X_ENC_GET_SEQ_END,              API_RESULT),
+       API_ENTRY(CX2341X_ENC_SET_PGM_INDEX_INFO,       API_FAST_RESULT),
+       API_ENTRY(CX2341X_ENC_SET_VBI_CONFIG,           API_RESULT),
+       API_ENTRY(CX2341X_ENC_SET_DMA_BLOCK_SIZE,       API_CACHE),
+       API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_10,  API_FAST_RESULT),
+       API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_9,   API_FAST_RESULT),
+       API_ENTRY(CX2341X_ENC_SCHED_DMA_TO_HOST,        API_DMA),
+       API_ENTRY(CX2341X_ENC_INITIALIZE_INPUT,         API_RESULT),
+       API_ENTRY(CX2341X_ENC_SET_FRAME_DROP_RATE,      API_CACHE),
+       API_ENTRY(CX2341X_ENC_PAUSE_ENCODER,            API_RESULT),
+       API_ENTRY(CX2341X_ENC_REFRESH_INPUT,            API_NO_WAIT_MB),
+       API_ENTRY(CX2341X_ENC_SET_COPYRIGHT,            API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_EVENT_NOTIFICATION,   API_RESULT),
+       API_ENTRY(CX2341X_ENC_SET_NUM_VSYNC_LINES,      API_CACHE),
+       API_ENTRY(CX2341X_ENC_SET_PLACEHOLDER,          API_CACHE),
+       API_ENTRY(CX2341X_ENC_MUTE_VIDEO,               API_RESULT),
+       API_ENTRY(CX2341X_ENC_MUTE_AUDIO,               API_RESULT),
+       API_ENTRY(CX2341X_ENC_SET_VERT_CROP_LINE,       API_FAST_RESULT),
+       API_ENTRY(CX2341X_ENC_MISC,                     API_FAST_RESULT),
+       /* Obsolete PULLDOWN API command */
+       API_ENTRY(0xb1,                                 API_CACHE),
+
+       /* MPEG decoder API */
+       API_ENTRY(CX2341X_DEC_PING_FW,                  API_FAST_RESULT),
+       API_ENTRY(CX2341X_DEC_START_PLAYBACK,           API_RESULT),
+       API_ENTRY(CX2341X_DEC_STOP_PLAYBACK,            API_RESULT),
+       API_ENTRY(CX2341X_DEC_SET_PLAYBACK_SPEED,       API_RESULT),
+       API_ENTRY(CX2341X_DEC_STEP_VIDEO,               API_RESULT),
+       API_ENTRY(CX2341X_DEC_SET_DMA_BLOCK_SIZE,       API_CACHE),
+       API_ENTRY(CX2341X_DEC_GET_XFER_INFO,            API_FAST_RESULT),
+       API_ENTRY(CX2341X_DEC_GET_DMA_STATUS,           API_FAST_RESULT),
+       API_ENTRY(CX2341X_DEC_SCHED_DMA_FROM_HOST,      API_DMA),
+       API_ENTRY(CX2341X_DEC_PAUSE_PLAYBACK,           API_RESULT),
+       API_ENTRY(CX2341X_DEC_HALT_FW,                  API_FAST_RESULT),
+       API_ENTRY(CX2341X_DEC_SET_STANDARD,             API_CACHE),
+       API_ENTRY(CX2341X_DEC_GET_VERSION,              API_FAST_RESULT),
+       API_ENTRY(CX2341X_DEC_SET_STREAM_INPUT,         API_CACHE),
+       API_ENTRY(CX2341X_DEC_GET_TIMING_INFO,          API_RESULT /*| API_NO_WAIT_RES*/),
+       API_ENTRY(CX2341X_DEC_SET_AUDIO_MODE,           API_CACHE),
+       API_ENTRY(CX2341X_DEC_SET_EVENT_NOTIFICATION,   API_RESULT),
+       API_ENTRY(CX2341X_DEC_SET_DISPLAY_BUFFERS,      API_CACHE),
+       API_ENTRY(CX2341X_DEC_EXTRACT_VBI,              API_RESULT),
+       API_ENTRY(CX2341X_DEC_SET_DECODER_SOURCE,       API_FAST_RESULT),
+       API_ENTRY(CX2341X_DEC_SET_PREBUFFERING,         API_CACHE),
+
+       /* OSD API */
+       API_ENTRY(CX2341X_OSD_GET_FRAMEBUFFER,          API_FAST_RESULT),
+       API_ENTRY(CX2341X_OSD_GET_PIXEL_FORMAT,         API_FAST_RESULT),
+       API_ENTRY(CX2341X_OSD_SET_PIXEL_FORMAT,         API_CACHE),
+       API_ENTRY(CX2341X_OSD_GET_STATE,                API_FAST_RESULT),
+       API_ENTRY(CX2341X_OSD_SET_STATE,                API_CACHE),
+       API_ENTRY(CX2341X_OSD_GET_OSD_COORDS,           API_FAST_RESULT),
+       API_ENTRY(CX2341X_OSD_SET_OSD_COORDS,           API_CACHE),
+       API_ENTRY(CX2341X_OSD_GET_SCREEN_COORDS,        API_FAST_RESULT),
+       API_ENTRY(CX2341X_OSD_SET_SCREEN_COORDS,        API_CACHE),
+       API_ENTRY(CX2341X_OSD_GET_GLOBAL_ALPHA,         API_FAST_RESULT),
+       API_ENTRY(CX2341X_OSD_SET_GLOBAL_ALPHA,         API_CACHE),
+       API_ENTRY(CX2341X_OSD_SET_BLEND_COORDS,         API_CACHE),
+       API_ENTRY(CX2341X_OSD_GET_FLICKER_STATE,        API_FAST_RESULT),
+       API_ENTRY(CX2341X_OSD_SET_FLICKER_STATE,        API_CACHE),
+       API_ENTRY(CX2341X_OSD_BLT_COPY,                 API_RESULT),
+       API_ENTRY(CX2341X_OSD_BLT_FILL,                 API_RESULT),
+       API_ENTRY(CX2341X_OSD_BLT_TEXT,                 API_RESULT),
+       API_ENTRY(CX2341X_OSD_SET_FRAMEBUFFER_WINDOW,   API_CACHE),
+       API_ENTRY(CX2341X_OSD_SET_CHROMA_KEY,           API_CACHE),
+       API_ENTRY(CX2341X_OSD_GET_ALPHA_CONTENT_INDEX,  API_FAST_RESULT),
+       API_ENTRY(CX2341X_OSD_SET_ALPHA_CONTENT_INDEX,  API_CACHE)
+};
+
+static int try_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int mb)
+{
+       u32 flags = readl(&mbdata->mbox[mb].flags);
+       int is_free = flags == IVTV_MBOX_FREE || (flags & IVTV_MBOX_FIRMWARE_DONE);
+
+       /* if the mailbox is free, then try to claim it */
+       if (is_free && !test_and_set_bit(mb, &mbdata->busy)) {
+               write_sync(IVTV_MBOX_DRIVER_BUSY, &mbdata->mbox[mb].flags);
+               return 1;
+       }
+       return 0;
+}
+
+/* Try to find a free mailbox. Note mailbox 0 is reserved for DMA and so is not
+   attempted here. */
+static int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int flags)
+{
+       unsigned long then = jiffies;
+       int i, mb;
+       int max_mbox = mbdata->max_mbox;
+       int retries = 100;
+
+       /* All slow commands use the same mailbox, serializing them and also
+          leaving the other mailbox free for simple fast commands. */
+       if ((flags & API_FAST_RESULT) == API_RESULT)
+               max_mbox = 1;
+
+       /* find free non-DMA mailbox */
+       for (i = 0; i < retries; i++) {
+               for (mb = 1; mb <= max_mbox; mb++)
+                       if (try_mailbox(itv, mbdata, mb))
+                               return mb;
+
+               /* Sleep before a retry, if not atomic */
+               if (!(flags & API_NO_WAIT_MB)) {
+                       if (jiffies - then > retries * HZ / 100)
+                              break;
+                       ivtv_sleep_timeout(HZ / 100, 0);
+               }
+       }
+       return -ENODEV;
+}
+
+static void write_mailbox(volatile struct ivtv_mailbox __iomem *mbox, int cmd, int args, u32 data[])
+{
+       int i;
+
+       write_sync(cmd, &mbox->cmd);
+       write_sync(IVTV_API_STD_TIMEOUT, &mbox->timeout);
+
+       for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+               write_sync(data[i], &mbox->data[i]);
+
+       write_sync(IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_DRIVER_BUSY, &mbox->flags);
+}
+
+static void clear_all_mailboxes(struct ivtv *itv, struct ivtv_mailbox_data *mbdata)
+{
+       int i;
+
+       for (i = 0; i <= mbdata->max_mbox; i++) {
+               IVTV_DEBUG_WARN("Clearing mailbox %d: cmd 0x%08x flags 0x%08x\n",
+                       i, readl(&mbdata->mbox[i].cmd), readl(&mbdata->mbox[i].flags));
+               write_sync(0, &mbdata->mbox[i].flags);
+               clear_bit(i, &mbdata->busy);
+       }
+}
+
+static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[])
+{
+       struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox;
+       volatile struct ivtv_mailbox __iomem *mbox;
+       int api_timeout = HZ;
+       int flags, mb, i;
+       unsigned long then;
+
+       /* sanity checks */
+       if (NULL == mbdata) {
+               IVTV_ERR("No mailbox allocated\n");
+               return -ENODEV;
+       }
+       if (args < 0 || args > CX2341X_MBOX_MAX_DATA ||
+           cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) {
+               IVTV_ERR("Invalid API call: cmd = 0x%02x, args = %d\n", cmd, args);
+               return -EINVAL;
+       }
+
+       IVTV_DEBUG_API("API Call: %s\n", api_info[cmd].name);
+
+       /* clear possibly uninitialized part of data array */
+       for (i = args; i < CX2341X_MBOX_MAX_DATA; i++)
+               data[i] = 0;
+
+       /* If this command was issued within the last 30 minutes and with identical
+          data, then just return 0 as there is no need to issue this command again.
+          Just an optimization to prevent unnecessary use of mailboxes. */
+       if (itv->api_cache[cmd].last_jiffies &&
+           jiffies - itv->api_cache[cmd].last_jiffies < HZ * 1800 &&
+           !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) {
+               itv->api_cache[cmd].last_jiffies = jiffies;
+               return 0;
+       }
+
+       flags = api_info[cmd].flags;
+
+       if (flags & API_DMA) {
+               for (i = 0; i < 100; i++) {
+                       mb = i % (mbdata->max_mbox + 1);
+                       if (try_mailbox(itv, mbdata, mb)) {
+                               write_mailbox(&mbdata->mbox[mb], cmd, args, data);
+                               clear_bit(mb, &mbdata->busy);
+                               return 0;
+                       }
+                       IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n",
+                                       api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags));
+               }
+               IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name);
+               clear_all_mailboxes(itv, mbdata);
+               return -EBUSY;
+       }
+
+       if ((flags & API_FAST_RESULT) == API_FAST_RESULT)
+               api_timeout = HZ / 10;
+
+       mb = get_mailbox(itv, mbdata, flags);
+       if (mb < 0) {
+               IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name);
+               clear_all_mailboxes(itv, mbdata);
+               return -EBUSY;
+       }
+       mbox = &mbdata->mbox[mb];
+       write_mailbox(mbox, cmd, args, data);
+       if (flags & API_CACHE) {
+               memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data));
+               itv->api_cache[cmd].last_jiffies = jiffies;
+       }
+       if ((flags & API_RESULT) == 0) {
+               clear_bit(mb, &mbdata->busy);
+               return 0;
+       }
+
+       /* Get results */
+       then = jiffies;
+
+       while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) {
+               if (jiffies - then > api_timeout) {
+                       IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name);
+                       /* reset the mailbox, but it is likely too late already */
+                       write_sync(0, &mbox->flags);
+                       clear_bit(mb, &mbdata->busy);
+                       return -EIO;
+               }
+               if (flags & API_NO_WAIT_RES)
+                       mdelay(1);
+               else
+                       ivtv_sleep_timeout(HZ / 100, 0);
+       }
+       if (jiffies - then > HZ / 10)
+               IVTV_DEBUG_WARN("%s took %lu jiffies (%d per HZ)\n",
+                               api_info[cmd].name, jiffies - then, HZ);
+
+       for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+               data[i] = readl(&mbox->data[i]);
+       write_sync(0, &mbox->flags);
+       clear_bit(mb, &mbdata->busy);
+       return 0;
+}
+
+int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[])
+{
+       int res = ivtv_api_call(itv, cmd, args, data);
+
+       /* Allow a single retry, probably already too late though.
+          If there is no free mailbox then that is usually an indication
+          of a more serious problem. */
+       return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res;
+}
+
+int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+       return ivtv_api(priv, cmd, in, data);
+}
+
+int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...)
+{
+       va_list ap;
+       int i;
+
+       va_start(ap, args);
+       for (i = 0; i < args; i++) {
+               data[i] = va_arg(ap, u32);
+       }
+       va_end(ap);
+       return ivtv_api(itv, cmd, args, data);
+}
+
+int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...)
+{
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       va_list ap;
+       int i;
+
+       va_start(ap, args);
+       for (i = 0; i < args; i++) {
+               data[i] = va_arg(ap, u32);
+       }
+       va_end(ap);
+       return ivtv_api(itv, cmd, args, data);
+}
+
+/* This one is for stuff that can't sleep.. irq handlers, etc.. */
+void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, u32 data[])
+{
+       int i;
+
+       for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+               data[i] = readl(&mbdata->mbox[mb].data[i]);
+}
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/video/ivtv/ivtv-mailbox.h
new file mode 100644 (file)
index 0000000..79b8aec
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+    mailbox 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
+ */
+
+void ivtv_api_get_data(struct ivtv_mailbox_data *mbox, int mb, u32 data[]);
+int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]);
+int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...);
+int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...);
+int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]);
diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c
new file mode 100644 (file)
index 0000000..ccfcef1
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+    buffer queues.
+    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-streams.h"
+#include "ivtv-queue.h"
+#include "ivtv-mailbox.h"
+
+int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes)
+{
+       if (s->buf_size - buf->bytesused < copybytes)
+               copybytes = s->buf_size - buf->bytesused;
+       if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) {
+               return -EFAULT;
+       }
+       buf->bytesused += copybytes;
+       return copybytes;
+}
+
+void ivtv_buf_swap(struct ivtv_buffer *buf)
+{
+       int i;
+
+       for (i = 0; i < buf->bytesused; i += 4)
+               swab32s((u32 *)(buf->buf + i));
+}
+
+void ivtv_queue_init(struct ivtv_queue *q)
+{
+       INIT_LIST_HEAD(&q->list);
+       q->buffers = 0;
+       q->length = 0;
+       q->bytesused = 0;
+}
+
+void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q)
+{
+       unsigned long flags = 0;
+
+       /* clear the buffer if it is going to be enqueued to the free queue */
+       if (q == &s->q_free) {
+               buf->bytesused = 0;
+               buf->readpos = 0;
+               buf->b_flags = 0;
+       }
+       spin_lock_irqsave(&s->qlock, flags);
+       list_add_tail(&buf->list, &q->list);
+       q->buffers++;
+       q->length += s->buf_size;
+       q->bytesused += buf->bytesused - buf->readpos;
+       spin_unlock_irqrestore(&s->qlock, flags);
+}
+
+struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q)
+{
+       struct ivtv_buffer *buf = NULL;
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&s->qlock, flags);
+       if (!list_empty(&q->list)) {
+               buf = list_entry(q->list.next, struct ivtv_buffer, list);
+               list_del_init(q->list.next);
+               q->buffers--;
+               q->length -= s->buf_size;
+               q->bytesused -= buf->bytesused - buf->readpos;
+       }
+       spin_unlock_irqrestore(&s->qlock, flags);
+       return buf;
+}
+
+static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from,
+               struct ivtv_queue *to, int clear, int full)
+{
+       struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list);
+
+       list_move_tail(from->list.next, &to->list);
+       from->buffers--;
+       from->length -= s->buf_size;
+       from->bytesused -= buf->bytesused - buf->readpos;
+       /* special handling for q_free */
+       if (clear)
+               buf->bytesused = buf->readpos = buf->b_flags = 0;
+       else if (full) {
+               /* special handling for stolen buffers, assume
+                  all bytes are used. */
+               buf->bytesused = s->buf_size;
+               buf->readpos = buf->b_flags = 0;
+       }
+       to->buffers++;
+       to->length += s->buf_size;
+       to->bytesused += buf->bytesused - buf->readpos;
+}
+
+/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
+   If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
+   If 'steal' != NULL, then buffers may also taken from that queue if
+   needed.
+
+   The buffer is automatically cleared if it goes to the free queue. It is
+   also cleared if buffers need to be taken from the 'steal' queue and
+   the 'from' queue is the free queue.
+
+   When 'from' is q_free, then needed_bytes is compared to the total
+   available buffer length, otherwise needed_bytes is compared to the
+   bytesused value. For the 'steal' queue the total available buffer
+   length is always used.
+
+   -ENOMEM is returned if the buffers could not be obtained, 0 if all
+   buffers where obtained from the 'from' list and if non-zero then
+   the number of stolen buffers is returned. */
+int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
+                   struct ivtv_queue *to, int needed_bytes)
+{
+       unsigned long flags;
+       int rc = 0;
+       int from_free = from == &s->q_free;
+       int to_free = to == &s->q_free;
+       int bytes_available;
+
+       spin_lock_irqsave(&s->qlock, flags);
+       if (needed_bytes == 0) {
+               from_free = 1;
+               needed_bytes = from->length;
+       }
+
+       bytes_available = from_free ? from->length : from->bytesused;
+       bytes_available += steal ? steal->length : 0;
+
+       if (bytes_available < needed_bytes) {
+               spin_unlock_irqrestore(&s->qlock, flags);
+               return -ENOMEM;
+       }
+       if (from_free) {
+               u32 old_length = to->length;
+
+               while (to->length - old_length < needed_bytes) {
+                       if (list_empty(&from->list))
+                               from = steal;
+                       if (from == steal)
+                               rc++;           /* keep track of 'stolen' buffers */
+                       ivtv_queue_move_buf(s, from, to, 1, 0);
+               }
+       }
+       else {
+               u32 old_bytesused = to->bytesused;
+
+               while (to->bytesused - old_bytesused < needed_bytes) {
+                       if (list_empty(&from->list))
+                               from = steal;
+                       if (from == steal)
+                               rc++;           /* keep track of 'stolen' buffers */
+                       ivtv_queue_move_buf(s, from, to, to_free, rc);
+               }
+       }
+       spin_unlock_irqrestore(&s->qlock, flags);
+       return rc;
+}
+
+void ivtv_flush_queues(struct ivtv_stream *s)
+{
+       ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
+       ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
+       ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
+       ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
+}
+
+int ivtv_stream_alloc(struct ivtv_stream *s)
+{
+       struct ivtv *itv = s->itv;
+       int SGsize = sizeof(struct ivtv_SG_element) * s->buffers;
+       int i;
+
+       if (s->buffers == 0)
+               return 0;
+
+       IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n",
+               s->dma != PCI_DMA_NONE ? "DMA " : "",
+               s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024);
+
+       /* Allocate DMA SG Arrays */
+       if (s->dma != PCI_DMA_NONE) {
+               s->SGarray = (struct ivtv_SG_element *)kzalloc(SGsize, GFP_KERNEL);
+               if (s->SGarray == NULL) {
+                       IVTV_ERR("Could not allocate SGarray for %s stream\n", s->name);
+                       return -ENOMEM;
+               }
+               s->SG_length = 0;
+               s->SG_handle = pci_map_single(itv->dev, s->SGarray, SGsize, s->dma);
+               ivtv_stream_sync_for_cpu(s);
+       }
+
+       /* allocate stream buffers. Initially all buffers are in q_free. */
+       for (i = 0; i < s->buffers; i++) {
+               struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer), GFP_KERNEL);
+
+               if (buf == NULL)
+                       break;
+               buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL);
+               if (buf->buf == NULL) {
+                       kfree(buf);
+                       break;
+               }
+               INIT_LIST_HEAD(&buf->list);
+               if (s->dma != PCI_DMA_NONE) {
+                       buf->dma_handle = pci_map_single(s->itv->dev,
+                               buf->buf, s->buf_size + 256, s->dma);
+                       ivtv_buf_sync_for_cpu(s, buf);
+               }
+               ivtv_enqueue(s, buf, &s->q_free);
+       }
+       if (i == s->buffers)
+               return 0;
+       IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name);
+       ivtv_stream_free(s);
+       return -ENOMEM;
+}
+
+void ivtv_stream_free(struct ivtv_stream *s)
+{
+       struct ivtv_buffer *buf;
+
+       /* move all buffers to q_free */
+       ivtv_flush_queues(s);
+
+       /* empty q_free */
+       while ((buf = ivtv_dequeue(s, &s->q_free))) {
+               if (s->dma != PCI_DMA_NONE)
+                       pci_unmap_single(s->itv->dev, buf->dma_handle,
+                               s->buf_size + 256, s->dma);
+               kfree(buf->buf);
+               kfree(buf);
+       }
+
+       /* Free SG Array/Lists */
+       if (s->SGarray != NULL) {
+               if (s->SG_handle != IVTV_DMA_UNMAPPED) {
+                       pci_unmap_single(s->itv->dev, s->SG_handle,
+                                sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
+                       s->SG_handle = IVTV_DMA_UNMAPPED;
+               }
+               s->SGarray = NULL;
+               s->SG_length = 0;
+       }
+}
diff --git a/drivers/media/video/ivtv/ivtv-queue.h b/drivers/media/video/ivtv/ivtv-queue.h
new file mode 100644 (file)
index 0000000..903edd4
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+    buffer queues.
+    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
+ */
+
+#define IVTV_DMA_UNMAPPED      ((u32) -1)
+
+/* ivtv_buffer utility functions */
+static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf)
+{
+       if (s->dma != PCI_DMA_NONE)
+               pci_dma_sync_single_for_cpu(s->itv->dev, buf->dma_handle,
+                               s->buf_size + 256, s->dma);
+}
+
+static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf)
+{
+       if (s->dma != PCI_DMA_NONE)
+               pci_dma_sync_single_for_device(s->itv->dev, buf->dma_handle,
+                               s->buf_size + 256, s->dma);
+}
+
+int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes);
+void ivtv_buf_swap(struct ivtv_buffer *buf);
+
+/* ivtv_queue utility functions */
+void ivtv_queue_init(struct ivtv_queue *q);
+void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q);
+struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q);
+int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
+                   struct ivtv_queue *to, int needed_bytes);
+void ivtv_flush_queues(struct ivtv_stream *s);
+
+/* ivtv_stream utility functions */
+int ivtv_stream_alloc(struct ivtv_stream *s);
+void ivtv_stream_free(struct ivtv_stream *s);
+
+static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s)
+{
+       pci_dma_sync_single_for_cpu(s->itv->dev, s->SG_handle,
+               sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
+}
+
+static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s)
+{
+       pci_dma_sync_single_for_device(s->itv->dev, s->SG_handle,
+               sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
+}
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
new file mode 100644 (file)
index 0000000..73a1c93
--- /dev/null
@@ -0,0 +1,977 @@
+/*
+    init/start/stop/exit stream 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
+ */
+
+/* License: GPL
+ * Author: Kevin Thayer <nufan_wfk at yahoo dot com>
+ *
+ * This file will hold API related functions, both internal (firmware api)
+ * and external (v4l2, etc)
+ *
+ * -----
+ * 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 "ivtv-driver.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-audio.h"
+#include "ivtv-video.h"
+#include "ivtv-vbi.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-irq.h"
+#include "ivtv-streams.h"
+#include "ivtv-cards.h"
+
+static struct file_operations ivtv_v4l2_enc_fops = {
+      .owner = THIS_MODULE,
+      .read = ivtv_v4l2_read,
+      .write = ivtv_v4l2_write,
+      .open = ivtv_v4l2_open,
+      .ioctl = ivtv_v4l2_ioctl,
+      .release = ivtv_v4l2_close,
+      .poll = ivtv_v4l2_enc_poll,
+};
+
+static struct file_operations ivtv_v4l2_dec_fops = {
+      .owner = THIS_MODULE,
+      .read = ivtv_v4l2_read,
+      .write = ivtv_v4l2_write,
+      .open = ivtv_v4l2_open,
+      .ioctl = ivtv_v4l2_ioctl,
+      .release = ivtv_v4l2_close,
+      .poll = ivtv_v4l2_dec_poll,
+};
+
+struct {
+       const char *name;
+       int vfl_type;
+       int minor_offset;
+       int dma, pio;
+       enum v4l2_buf_type buf_type;
+       struct file_operations *fops;
+} ivtv_stream_info[] = {
+       {       /* IVTV_ENC_STREAM_TYPE_MPG */
+               "encoder MPEG",
+               VFL_TYPE_GRABBER, 0,
+               PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+               &ivtv_v4l2_enc_fops
+       },
+       {       /* IVTV_ENC_STREAM_TYPE_YUV */
+               "encoder YUV",
+               VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET,
+               PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+               &ivtv_v4l2_enc_fops
+       },
+       {       /* IVTV_ENC_STREAM_TYPE_VBI */
+               "encoder VBI",
+               VFL_TYPE_VBI, 0,
+               PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE,
+               &ivtv_v4l2_enc_fops
+       },
+       {       /* IVTV_ENC_STREAM_TYPE_PCM */
+               "encoder PCM audio",
+               VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET,
+               PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE,
+               &ivtv_v4l2_enc_fops
+       },
+       {       /* IVTV_ENC_STREAM_TYPE_RAD */
+               "encoder radio",
+               VFL_TYPE_RADIO, 0,
+               PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE,
+               &ivtv_v4l2_enc_fops
+       },
+       {       /* IVTV_DEC_STREAM_TYPE_MPG */
+               "decoder MPEG",
+               VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET,
+               PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+               &ivtv_v4l2_dec_fops
+       },
+       {       /* IVTV_DEC_STREAM_TYPE_VBI */
+               "decoder VBI",
+               VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET,
+               PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE,
+               &ivtv_v4l2_enc_fops
+       },
+       {       /* IVTV_DEC_STREAM_TYPE_VOUT */
+               "decoder VOUT",
+               VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET,
+               PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT,
+               &ivtv_v4l2_dec_fops
+       },
+       {       /* IVTV_DEC_STREAM_TYPE_YUV */
+               "decoder YUV",
+               VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET,
+               PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+               &ivtv_v4l2_dec_fops
+       }
+};
+
+static void ivtv_stream_init(struct ivtv *itv, int type)
+{
+       struct ivtv_stream *s = &itv->streams[type];
+       struct video_device *dev = s->v4l2dev;
+
+       /* we need to keep v4l2dev, so restore it afterwards */
+       memset(s, 0, sizeof(*s));
+       s->v4l2dev = dev;
+
+       /* initialize ivtv_stream fields */
+       s->itv = itv;
+       s->type = type;
+       s->name = ivtv_stream_info[type].name;
+
+       if (ivtv_stream_info[type].pio)
+               s->dma = PCI_DMA_NONE;
+       else
+               s->dma = ivtv_stream_info[type].dma;
+       s->buf_size = itv->stream_buf_size[type];
+       if (s->buf_size)
+               s->buffers = itv->options.megabytes[type] * 1024 * 1024 / s->buf_size;
+       spin_lock_init(&s->qlock);
+       init_waitqueue_head(&s->waitq);
+       s->id = -1;
+       s->SG_handle = IVTV_DMA_UNMAPPED;
+       ivtv_queue_init(&s->q_free);
+       ivtv_queue_init(&s->q_full);
+       ivtv_queue_init(&s->q_dma);
+       ivtv_queue_init(&s->q_predma);
+       ivtv_queue_init(&s->q_io);
+}
+
+static int ivtv_reg_dev(struct ivtv *itv, int type)
+{
+       struct ivtv_stream *s = &itv->streams[type];
+       int vfl_type = ivtv_stream_info[type].vfl_type;
+       int minor_offset = ivtv_stream_info[type].minor_offset;
+       int minor;
+
+       /* These four fields are always initialized. If v4l2dev == NULL, then
+          this stream is not in use. In that case no other fields but these
+          four can be used. */
+       s->v4l2dev = NULL;
+       s->itv = itv;
+       s->type = type;
+       s->name = ivtv_stream_info[type].name;
+
+       /* Check whether the radio is supported */
+       if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO))
+               return 0;
+       if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+               return 0;
+
+       if (minor_offset >= 0)
+               /* card number + user defined offset + device offset */
+               minor = itv->num + ivtv_first_minor + minor_offset;
+       else
+               minor = -1;
+
+       /* User explicitly selected 0 buffers for these streams, so don't
+          create them. */
+       if (minor >= 0 && ivtv_stream_info[type].dma != PCI_DMA_NONE &&
+           itv->options.megabytes[type] == 0) {
+               IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name);
+               return 0;
+       }
+
+       ivtv_stream_init(itv, type);
+
+       /* allocate and initialize the v4l2 video device structure */
+       s->v4l2dev = video_device_alloc();
+       if (s->v4l2dev == NULL) {
+               IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name);
+               return -ENOMEM;
+       }
+
+       s->v4l2dev->type = VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT |
+                   VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER;
+       if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+               s->v4l2dev->type |= VID_TYPE_MPEG_DECODER;
+       }
+       snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s",
+                       itv->num, s->name);
+
+       s->v4l2dev->minor = minor;
+       s->v4l2dev->dev = &itv->dev->dev;
+       s->v4l2dev->fops = ivtv_stream_info[type].fops;
+       s->v4l2dev->release = video_device_release;
+
+       if (minor >= 0) {
+               /* Register device. First try the desired minor, then any free one. */
+               if (video_register_device(s->v4l2dev, vfl_type, minor) &&
+                   video_register_device(s->v4l2dev, vfl_type, -1)) {
+                       IVTV_ERR("Couldn't register v4l2 device for %s minor %d\n",
+                                       s->name, minor);
+                       video_device_release(s->v4l2dev);
+                       s->v4l2dev = NULL;
+                       return -ENOMEM;
+               }
+       }
+       else {
+               /* Don't register a 'hidden' stream (OSD) */
+               IVTV_INFO("Created framebuffer stream for %s\n", s->name);
+               return 0;
+       }
+
+       switch (vfl_type) {
+       case VFL_TYPE_GRABBER:
+               IVTV_INFO("Registered device video%d for %s (%d MB)\n",
+                       s->v4l2dev->minor, s->name, itv->options.megabytes[type]);
+               break;
+       case VFL_TYPE_RADIO:
+               IVTV_INFO("Registered device radio%d for %s\n",
+                       s->v4l2dev->minor - MINOR_VFL_TYPE_RADIO_MIN, s->name);
+               break;
+       case VFL_TYPE_VBI:
+               if (itv->options.megabytes[type])
+                       IVTV_INFO("Registered device vbi%d for %s (%d MB)\n",
+                               s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN,
+                               s->name, itv->options.megabytes[type]);
+               else
+                       IVTV_INFO("Registered device vbi%d for %s\n",
+                               s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, s->name);
+               break;
+       }
+       return 0;
+}
+
+/* Initialize v4l2 variables and register v4l2 devices */
+int ivtv_streams_setup(struct ivtv *itv)
+{
+       int type;
+
+       /* Setup V4L2 Devices */
+       for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+               /* Register Device */
+               if (ivtv_reg_dev(itv, type))
+                       break;
+
+               if (itv->streams[type].v4l2dev == NULL)
+                       continue;
+
+               /* Allocate Stream */
+               if (ivtv_stream_alloc(&itv->streams[type]))
+                       break;
+       }
+       if (type == IVTV_MAX_STREAMS) {
+               return 0;
+       }
+
+       /* One or more streams could not be initialized. Clean 'em all up. */
+       ivtv_streams_cleanup(itv);
+       return -ENOMEM;
+}
+
+/* Unregister v4l2 devices */
+void ivtv_streams_cleanup(struct ivtv *itv)
+{
+       int type;
+
+       /* Teardown all streams */
+       for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+               struct video_device *vdev = itv->streams[type].v4l2dev;
+
+               itv->streams[type].v4l2dev = NULL;
+               if (vdev == NULL)
+                       continue;
+
+               ivtv_stream_free(&itv->streams[type]);
+               /* Free Device */
+               if (vdev->minor == -1) /* 'Hidden' never registered stream (OSD) */
+                       video_device_release(vdev);
+               else    /* All others, just unregister. */
+                       video_unregister_device(vdev);
+       }
+}
+
+static void ivtv_vbi_setup(struct ivtv *itv)
+{
+       int raw = itv->vbi.sliced_in->service_set == 0;
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       int lines;
+       int i;
+
+       /* If Embed then streamtype must be Program */
+       /* TODO: should we really do this? */
+       if (0 && !raw && itv->vbi.insert_mpeg) {
+               itv->params.stream_type = 0;
+
+               /* assign stream type */
+               ivtv_vapi(itv, CX2341X_ENC_SET_STREAM_TYPE, 1, itv->params.stream_type);
+       }
+
+       /* Reset VBI */
+       ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0);
+
+       if (itv->is_60hz) {
+               itv->vbi.count = 12;
+               itv->vbi.start[0] = 10;
+               itv->vbi.start[1] = 273;
+       } else {        /* PAL/SECAM */
+               itv->vbi.count = 18;
+               itv->vbi.start[0] = 6;
+               itv->vbi.start[1] = 318;
+       }
+
+       /* setup VBI registers */
+       itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
+
+       /* determine number of lines and total number of VBI bytes.
+          A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
+          The '- 1' byte is probably an unused U or V byte. Or something...
+          A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
+          header, 42 data bytes + checksum (to be confirmed) */
+       if (raw) {
+               lines = itv->vbi.count * 2;
+       } else {
+               lines = itv->is_60hz ? 24 : 38;
+               if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840))
+                       lines += 2;
+       }
+
+       itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
+
+       /* Note: sliced vs raw flag doesn't seem to have any effect
+          TODO: check mode (0x02) value with older ivtv versions. */
+       data[0] = raw | 0x02 | (0xbd << 8);
+
+       /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */
+       data[1] = 1;
+       /* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */
+       data[2] = raw ? 4 : 8;
+       /* The start/stop codes determine which VBI lines end up in the raw VBI data area.
+          The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line
+          is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video)
+          code. These values for raw VBI are obtained from a driver disassembly. The sliced
+          start/stop codes was deduced from this, but they do not appear in the driver.
+          Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54.
+          However, I have no idea what these values are for. */
+       if (itv->hw_flags & IVTV_HW_CX25840) {
+               /* Setup VBI for the cx25840 digitizer */
+               if (raw) {
+                       data[3] = 0x20602060;
+                       data[4] = 0x30703070;
+               } else {
+                       data[3] = 0xB0F0B0F0;
+                       data[4] = 0xA0E0A0E0;
+               }
+               /* Lines per frame */
+               data[5] = lines;
+               /* bytes per line */
+               data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
+       } else {
+               /* Setup VBI for the saa7115 digitizer */
+               if (raw) {
+                       data[3] = 0x25256262;
+                       data[4] = 0x387F7F7F;
+               } else {
+                       data[3] = 0xABABECEC;
+                       data[4] = 0xB6F1F1F1;
+               }
+               /* Lines per frame */
+               data[5] = lines;
+               /* bytes per line */
+               data[6] = itv->vbi.enc_size / lines;
+       }
+
+       IVTV_DEBUG_INFO(
+               "Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n",
+                       data[0], data[1], data[2], data[5], data[6]);
+
+       ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data);
+
+       /* returns the VBI encoder memory area. */
+       itv->vbi.enc_start = data[2];
+       itv->vbi.fpi = data[0];
+       if (!itv->vbi.fpi)
+               itv->vbi.fpi = 1;
+
+       IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d lines 0x%08x\n",
+               itv->vbi.enc_start, data[1], itv->vbi.fpi, itv->digitizer);
+
+       /* select VBI lines.
+          Note that the sliced argument seems to have no effect. */
+       for (i = 2; i <= 24; i++) {
+               int valid;
+
+               if (itv->is_60hz) {
+                       valid = i >= 10 && i < 22;
+               } else {
+                       valid = i >= 6 && i < 24;
+               }
+               ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1,
+                               valid, 0 , 0, 0);
+               ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000,
+                               valid, 0, 0, 0);
+       }
+
+       /* Remaining VBI questions:
+          - Is it possible to select particular VBI lines only for inclusion in the MPEG
+          stream? Currently you can only get the first X lines.
+          - Is mixed raw and sliced VBI possible?
+          - What's the meaning of the raw/sliced flag?
+          - What's the meaning of params 2, 3 & 4 of the Select VBI command? */
+}
+
+int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
+{
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       struct ivtv *itv = s->itv;
+       int captype = 0, subtype = 0;
+       int enable_passthrough = 0;
+
+       if (s->v4l2dev == NULL)
+               return -EINVAL;
+
+       IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name);
+
+       switch (s->type) {
+       case IVTV_ENC_STREAM_TYPE_MPG:
+               captype = 0;
+               subtype = 3;
+
+               /* Stop Passthrough */
+               if (itv->output_mode == OUT_PASSTHROUGH) {
+                       ivtv_passthrough_mode(itv, 0);
+                       enable_passthrough = 1;
+               }
+               itv->mpg_data_received = itv->vbi_data_inserted = 0;
+               itv->dualwatch_jiffies = jiffies;
+               itv->dualwatch_stereo_mode = itv->params.audio_properties & 0x0300;
+               itv->search_pack_header = 0;
+               break;
+
+       case IVTV_ENC_STREAM_TYPE_YUV:
+               if (itv->output_mode == OUT_PASSTHROUGH) {
+                       captype = 2;
+                       subtype = 11;   /* video+audio+decoder */
+                       break;
+               }
+               captype = 1;
+               subtype = 1;
+               break;
+       case IVTV_ENC_STREAM_TYPE_PCM:
+               captype = 1;
+               subtype = 2;
+               break;
+       case IVTV_ENC_STREAM_TYPE_VBI:
+               captype = 1;
+               subtype = 4;
+
+               itv->vbi.frame = 0;
+               itv->vbi.inserted_frame = 0;
+               memset(itv->vbi.sliced_mpeg_size,
+                       0, sizeof(itv->vbi.sliced_mpeg_size));
+               break;
+       default:
+               return -EINVAL;
+       }
+       s->subtype = subtype;
+       s->buffers_stolen = 0;
+
+       /* mute/unmute video */
+       ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? 1 : 0);
+
+       /* Clear Streamoff flags in case left from last capture */
+       clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+       if (atomic_read(&itv->capturing) == 0) {
+               /* Always use frame based mode. Experiments have demonstrated that byte
+                  stream based mode results in dropped frames and corruption. Not often,
+                  but occasionally. Many thanks go to Leonard Orb who spent a lot of
+                  effort and time trying to trace the cause of the drop outs. */
+               /* 1 frame per DMA */
+               /*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */
+               ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1);
+
+               /* Stuff from Windows, we don't know what it is */
+               ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0);
+               /* According to the docs, this should be correct. However, this is
+                  untested. I don't dare enable this without having tested it.
+                  Only very few old cards actually have this hardware combination.
+               ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1,
+                       ((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0);
+               */
+               ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415);
+               ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0);
+               ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1);
+               ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
+
+               /* assign placeholder */
+               ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12,
+                       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+               ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, itv->digitizer, itv->digitizer);
+
+               /* Setup VBI */
+               if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) {
+                       ivtv_vbi_setup(itv);
+               }
+
+               /* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */
+               ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400);
+               itv->pgm_info_offset = data[0];
+               itv->pgm_info_num = data[1];
+               itv->pgm_info_write_idx = 0;
+               itv->pgm_info_read_idx = 0;
+
+               IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n",
+                               itv->pgm_info_offset, itv->pgm_info_num);
+
+               /* Setup API for Stream */
+               cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
+       }
+
+       /* Vsync Setup */
+       if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
+               /* event notification (on) */
+               ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1);
+               ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
+       }
+
+       if (atomic_read(&itv->capturing) == 0) {
+               /* Clear all Pending Interrupts */
+               ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+               clear_bit(IVTV_F_I_EOS, &itv->i_flags);
+
+               /* Initialize Digitizer for Capture */
+               ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
+
+               ivtv_sleep_timeout(HZ / 10, 0);
+       }
+
+       /* begin_capture */
+       if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype))
+       {
+               IVTV_DEBUG_WARN( "Error starting capture!\n");
+               return -EINVAL;
+       }
+
+       /* Start Passthrough */
+       if (enable_passthrough) {
+               ivtv_passthrough_mode(itv, 1);
+       }
+
+       if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
+               ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
+       else
+               ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+       /* you're live! sit back and await interrupts :) */
+       atomic_inc(&itv->capturing);
+       return 0;
+}
+
+static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
+{
+       u32 data[CX2341X_MBOX_MAX_DATA];
+       struct ivtv *itv = s->itv;
+       int datatype;
+
+       if (s->v4l2dev == NULL)
+               return -EINVAL;
+
+       IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
+
+       /* disable VBI signals, if the MPEG stream contains VBI data,
+          then that data will be processed automatically for you. */
+       ivtv_disable_vbi(itv);
+
+       /* set audio mode to left/stereo  for dual/stereo mode. */
+       ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+
+       /* set number of internal decoder buffers */
+       ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0);
+
+       /* prebuffering */
+       ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1);
+
+       /* extract from user packets */
+       ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1);
+       itv->vbi.dec_start = data[0];
+
+       IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n",
+               itv->vbi.dec_start, data[1]);
+
+       /* set decoder source settings */
+       /* Data type: 0 = mpeg from host,
+          1 = yuv from encoder,
+          2 = yuv_from_host */
+       switch (s->type) {
+       case IVTV_DEC_STREAM_TYPE_YUV:
+               datatype = itv->output_mode == OUT_PASSTHROUGH ? 1 : 2;
+               IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype);
+               break;
+       case IVTV_DEC_STREAM_TYPE_MPG:
+       default:
+               datatype = 0;
+               break;
+       }
+       if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
+                       itv->params.width, itv->params.height, itv->params.audio_properties)) {
+               IVTV_DEBUG_WARN("COULDN'T INITIALIZE DECODER SOURCE\n");
+       }
+       return 0;
+}
+
+int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
+{
+       struct ivtv *itv = s->itv;
+
+       if (s->v4l2dev == NULL)
+               return -EINVAL;
+
+       if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags))
+               return 0;       /* already started */
+
+       IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset);
+
+       /* Clear Streamoff */
+       if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
+               /* Initialize Decoder */
+               /* Reprogram Decoder YUV Buffers for YUV */
+               write_reg(yuv_offset[0] >> 4, 0x82c);
+               write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
+               write_reg(yuv_offset[0] >> 4, 0x834);
+               write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
+
+               write_reg_sync(0x00000000 | (0x0c << 16) | (0x0b << 8), 0x2d24);
+
+               write_reg_sync(0x00108080, 0x2898);
+               /* Enable YUV decoder output */
+               write_reg_sync(0x01, IVTV_REG_VDM);
+       }
+
+       ivtv_setup_v4l2_decode_stream(s);
+
+       /* set dma size to 65536 bytes */
+       ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536);
+
+       clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+       /* Zero out decoder counters */
+       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[0]);
+       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[1]);
+       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[2]);
+       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[3]);
+       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]);
+       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]);
+       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]);
+       writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]);
+
+       /* turn on notification of dual/stereo mode change */
+       ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
+
+       /* start playback */
+       ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0);
+
+       /* Clear the following Interrupt mask bits for decoding */
+       ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+       IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask);
+
+       /* you're live! sit back and await interrupts :) */
+       atomic_inc(&itv->decoding);
+       return 0;
+}
+
+void ivtv_stop_all_captures(struct ivtv *itv)
+{
+       int i;
+
+       for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) {
+               struct ivtv_stream *s = &itv->streams[i];
+
+               if (s->v4l2dev == NULL)
+                       continue;
+               if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+                       ivtv_stop_v4l2_encode_stream(s, 0);
+               }
+       }
+}
+
+int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
+{
+       struct ivtv *itv = s->itv;
+       DECLARE_WAITQUEUE(wait, current);
+       int cap_type;
+       unsigned long then;
+       int stopmode;
+       u32 data[CX2341X_MBOX_MAX_DATA];
+
+       if (s->v4l2dev == NULL)
+               return -EINVAL;
+
+       /* This function assumes that you are allowed to stop the capture
+          and that we are actually capturing */
+
+       IVTV_DEBUG_INFO("Stop Capture\n");
+
+       if (s->type == IVTV_DEC_STREAM_TYPE_VOUT)
+               return 0;
+       if (atomic_read(&itv->capturing) == 0)
+               return 0;
+
+       switch (s->type) {
+       case IVTV_ENC_STREAM_TYPE_YUV:
+               cap_type = 1;
+               break;
+       case IVTV_ENC_STREAM_TYPE_PCM:
+               cap_type = 1;
+               break;
+       case IVTV_ENC_STREAM_TYPE_VBI:
+               cap_type = 1;
+               break;
+       case IVTV_ENC_STREAM_TYPE_MPG:
+       default:
+               cap_type = 0;
+               break;
+       }
+
+       /* Stop Capture Mode */
+       if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
+               stopmode = 0;
+       } else {
+               stopmode = 1;
+       }
+
+       /* end_capture */
+       /* when: 0 =  end of GOP  1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */
+       ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype);
+
+       /* only run these if we're shutting down the last cap */
+       if (atomic_read(&itv->capturing) - 1 == 0) {
+               /* event notification (off) */
+               if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
+                       /* type: 0 = refresh */
+                       /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */
+                       ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1);
+                       ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
+               }
+       }
+
+       then = jiffies;
+
+       if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
+               if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
+                       /* only run these if we're shutting down the last cap */
+                       unsigned long duration;
+
+                       then = jiffies;
+                       add_wait_queue(&itv->cap_w, &wait);
+
+                       set_current_state(TASK_INTERRUPTIBLE);
+
+                       /* wait 2s for EOS interrupt */
+                       while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) && jiffies < then + 2 * HZ) {
+                               schedule_timeout(HZ / 100);
+                       }
+
+                       /* To convert jiffies to ms, we must multiply by 1000
+                        * and divide by HZ.  To avoid runtime division, we
+                        * convert this to multiplication by 1000/HZ.
+                        * Since integer division truncates, we get the best
+                        * accuracy if we do a rounding calculation of the constant.
+                        * Think of the case where HZ is 1024.
+                        */
+                       duration = ((1000 + HZ / 2) / HZ) * (jiffies - then);
+
+                       if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) {
+                               IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name);
+                               IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration);
+                       } else {
+                               IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration);
+                       }
+                       set_current_state(TASK_RUNNING);
+                       remove_wait_queue(&itv->cap_w, &wait);
+               }
+
+               then = jiffies;
+               /* Make sure DMA is complete */
+               add_wait_queue(&s->waitq, &wait);
+               set_current_state(TASK_INTERRUPTIBLE);
+               do {
+                       /* check if DMA is pending */
+                       if ((s->type == IVTV_ENC_STREAM_TYPE_MPG) &&    /* MPG Only */
+                           (read_reg(IVTV_REG_DMASTATUS) & 0x02)) {
+                               /* Check for last DMA */
+                               ivtv_vapi_result(itv, data, CX2341X_ENC_GET_SEQ_END, 2, 0, 0);
+
+                               if (data[0] == 1) {
+                                       IVTV_DEBUG_DMA("%s: Last DMA of size 0x%08x\n", s->name, data[1]);
+                                       break;
+                               }
+                       } else if (read_reg(IVTV_REG_DMASTATUS) & 0x02) {
+                               break;
+                       }
+
+                       ivtv_sleep_timeout(HZ / 100, 1);
+               } while (then + HZ * 2 > jiffies);
+
+               set_current_state(TASK_RUNNING);
+               remove_wait_queue(&s->waitq, &wait);
+       }
+
+       atomic_dec(&itv->capturing);
+
+       /* Clear capture and no-read bits */
+       clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+
+       if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
+               ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
+
+       if (atomic_read(&itv->capturing) > 0) {
+               return 0;
+       }
+
+       /* Set the following Interrupt mask bits for capture */
+       ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+       wake_up(&s->waitq);
+
+       return 0;
+}
+
+int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts)
+{
+       struct ivtv *itv = s->itv;
+
+       if (s->v4l2dev == NULL)
+               return -EINVAL;
+
+       if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG)
+               return -EINVAL;
+
+       if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags))
+               return 0;
+
+       IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", pts, flags);
+
+       /* Stop Decoder */
+       if (!(flags & VIDEO_CMD_STOP_IMMEDIATELY) || pts) {
+               u32 tmp = 0;
+
+               /* Wait until the decoder is no longer running */
+               if (pts) {
+                       ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3,
+                               0, (u32)(pts & 0xffffffff), (u32)(pts >> 32));
+               }
+               while (1) {
+                       u32 data[CX2341X_MBOX_MAX_DATA];
+                       ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0);
+                       if (s->q_full.buffers + s->q_dma.buffers == 0) {
+                               if (tmp == data[3])
+                                       break;
+                               tmp = data[3];
+                       }
+                       if (ivtv_sleep_timeout(HZ/10, 1))
+                               break;
+               }
+       }
+       ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & VIDEO_CMD_STOP_TO_BLACK, 0, 0);
+
+       /* turn off notification of dual/stereo mode change */
+       ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
+
+       ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+
+       clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+       clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+       ivtv_flush_queues(s);
+
+       if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
+               /* disable VBI on TV-out */
+               ivtv_disable_vbi(itv);
+       }
+
+       /* decrement decoding */
+       atomic_dec(&itv->decoding);
+
+       set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags);
+       wake_up(&itv->event_waitq);
+
+       /* wake up wait queues */
+       wake_up(&s->waitq);
+
+       return 0;
+}
+
+int ivtv_passthrough_mode(struct ivtv *itv, int enable)
+{
+       struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV];
+       struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+
+       if (yuv_stream->v4l2dev == NULL || dec_stream->v4l2dev == NULL)
+               return -EINVAL;
+
+       IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n");
+
+       /* Prevent others from starting/stopping streams while we
+          initiate/terminate passthrough mode */
+       if (enable) {
+               if (itv->output_mode == OUT_PASSTHROUGH) {
+                       return 0;
+               }
+               if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH)
+                       return -EBUSY;
+
+               /* Fully initialize stream, and then unflag init */
+               set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
+               set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
+
+               /* Setup YUV Decoder */
+               ivtv_setup_v4l2_decode_stream(dec_stream);
+
+               /* Start Decoder */
+               ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1);
+               atomic_inc(&itv->decoding);
+
+               /* Setup capture if not already done */
+               if (atomic_read(&itv->capturing) == 0) {
+                       cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
+               }
+
+               /* Start Passthrough Mode */
+               ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11);
+               atomic_inc(&itv->capturing);
+               return 0;
+       }
+
+       if (itv->output_mode != OUT_PASSTHROUGH)
+               return 0;
+
+       /* Stop Passthrough Mode */
+       ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11);
+       ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0);
+
+       atomic_dec(&itv->capturing);
+       atomic_dec(&itv->decoding);
+       clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
+       clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
+       itv->output_mode = OUT_NONE;
+
+       return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-streams.h b/drivers/media/video/ivtv/ivtv-streams.h
new file mode 100644 (file)
index 0000000..8597b75
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+    init/start/stop/exit stream 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_streams_setup(struct ivtv *itv);
+void ivtv_streams_cleanup(struct ivtv *itv);
+
+/* Capture related */
+int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s);
+int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end);
+int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset);
+int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts);
+
+void ivtv_stop_all_captures(struct ivtv *itv);
+int ivtv_passthrough_mode(struct ivtv *itv, int enable);
diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c
new file mode 100644 (file)
index 0000000..bd642e1
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+    User DMA
+
+    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-streams.h"
+#include "ivtv-udma.h"
+
+void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size)
+{
+       dma_page->uaddr = first & PAGE_MASK;
+       dma_page->offset = first & ~PAGE_MASK;
+       dma_page->tail = 1 + ((first+size-1) & ~PAGE_MASK);
+       dma_page->first = (first & PAGE_MASK) >> PAGE_SHIFT;
+       dma_page->last = ((first+size-1) & PAGE_MASK) >> PAGE_SHIFT;
+       dma_page->page_count = dma_page->last - dma_page->first + 1;
+       if (dma_page->page_count == 1) dma_page->tail -= dma_page->offset;
+}
+
+int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset)
+{
+       int i, offset;
+
+       offset = dma_page->offset;
+
+       /* Fill SG Array with new values */
+       for (i = 0; i < dma_page->page_count; i++) {
+               if (i == dma_page->page_count - 1) {
+                       dma->SGlist[map_offset].length = dma_page->tail;
+               }
+               else {
+                       dma->SGlist[map_offset].length = PAGE_SIZE - offset;
+               }
+               dma->SGlist[map_offset].offset = offset;
+               dma->SGlist[map_offset].page = dma->map[map_offset];
+               offset = 0;
+               map_offset++;
+       }
+       return map_offset;
+}
+
+void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) {
+       int i;
+       struct scatterlist *sg;
+
+       for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg++) {
+               dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg));
+               dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg));
+               dma->SGarray[i].dst = cpu_to_le32(buffer_offset);
+               buffer_offset += sg_dma_len(sg);
+
+               split -= sg_dma_len(sg);
+               if (split == 0)
+                       buffer_offset = buffer_offset_2;
+       }
+}
+
+/* User DMA Buffers */
+void ivtv_udma_alloc(struct ivtv *itv)
+{
+       if (itv->udma.SG_handle == 0) {
+               /* Map DMA Page Array Buffer */
+               itv->udma.SG_handle = pci_map_single(itv->dev, itv->udma.SGarray,
+                          sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+               ivtv_udma_sync_for_cpu(itv);
+       }
+}
+
+int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
+                      void __user *userbuf, int size_in_bytes)
+{
+       struct ivtv_dma_page_info user_dma;
+       struct ivtv_user_dma *dma = &itv->udma;
+       int err;
+
+       IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr);
+
+       /* Still in USE */
+       if (dma->SG_length || dma->page_count) {
+               IVTV_DEBUG_WARN("ivtv_udma_setup: SG_length %d page_count %d still full?\n",
+                          dma->SG_length, dma->page_count);
+               return -EBUSY;
+       }
+
+       ivtv_udma_get_page_info(&user_dma, (unsigned long)userbuf, size_in_bytes);
+
+       if (user_dma.page_count <= 0) {
+               IVTV_DEBUG_WARN("ivtv_udma_setup: Error %d page_count from %d bytes %d offset\n",
+                          user_dma.page_count, size_in_bytes, user_dma.offset);
+               return -EINVAL;
+       }
+
+       /* Get user pages for DMA Xfer */
+       down_read(&current->mm->mmap_sem);
+       err = get_user_pages(current, current->mm,
+                       user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL);
+       up_read(&current->mm->mmap_sem);
+
+       if (user_dma.page_count != err) {
+               IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
+                          err, user_dma.page_count);
+               return -EINVAL;
+       }
+
+       dma->page_count = user_dma.page_count;
+
+       /* Fill SG List with new values */
+       ivtv_udma_fill_sg_list(dma, &user_dma, 0);
+
+       /* Map SG List */
+       dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+
+       /* Fill SG Array with new values */
+       ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1);
+
+       /* Tag SG Array with Interrupt Bit */
+       dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+       ivtv_udma_sync_for_device(itv);
+       return dma->page_count;
+}
+
+void ivtv_udma_unmap(struct ivtv *itv)
+{
+       struct ivtv_user_dma *dma = &itv->udma;
+       int i;
+
+       IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n");
+
+       /* Nothing to free */
+       if (dma->page_count == 0)
+               return;
+
+       /* Unmap Scatterlist */
+       if (dma->SG_length) {
+               pci_unmap_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+               dma->SG_length = 0;
+       }
+       /* sync DMA */
+       ivtv_udma_sync_for_cpu(itv);
+
+       /* Release User Pages */
+       for (i = 0; i < dma->page_count; i++) {
+               put_page(dma->map[i]);
+       }
+       dma->page_count = 0;
+}
+
+void ivtv_udma_free(struct ivtv *itv)
+{
+       /* Unmap SG Array */
+       if (itv->udma.SG_handle) {
+               pci_unmap_single(itv->dev, itv->udma.SG_handle,
+                        sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+       }
+
+       /* Unmap Scatterlist */
+       if (itv->udma.SG_length) {
+               pci_unmap_sg(itv->dev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE);
+       }
+}
+
+void ivtv_udma_start(struct ivtv *itv)
+{
+       IVTV_DEBUG_DMA("start UDMA\n");
+       write_reg(itv->udma.SG_handle, IVTV_REG_DECDMAADDR);
+       write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+       set_bit(IVTV_F_I_DMA, &itv->i_flags);
+       set_bit(IVTV_F_I_UDMA, &itv->i_flags);
+}
+
+void ivtv_udma_prepare(struct ivtv *itv)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&itv->dma_reg_lock, flags);
+       if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+               ivtv_udma_start(itv);
+       else
+               set_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags);
+       spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
+}
diff --git a/drivers/media/video/ivtv/ivtv-udma.h b/drivers/media/video/ivtv/ivtv-udma.h
new file mode 100644 (file)
index 0000000..e131bcc
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
+    Copyright (C) 2006-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
+ */
+
+/* User DMA functions */
+void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size);
+int ivtv_udma_fill_sg_list(struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset);
+void ivtv_udma_fill_sg_array(struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split);
+int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
+                      void __user *userbuf, int size_in_bytes);
+void ivtv_udma_unmap(struct ivtv *itv);
+void ivtv_udma_free(struct ivtv *itv);
+void ivtv_udma_alloc(struct ivtv *itv);
+void ivtv_udma_prepare(struct ivtv *itv);
+void ivtv_udma_start(struct ivtv *itv);
+
+static inline void ivtv_udma_sync_for_device(struct ivtv *itv)
+{
+       pci_dma_sync_single_for_device((struct pci_dev *)itv->dev, itv->udma.SG_handle,
+               sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+}
+
+static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv)
+{
+       pci_dma_sync_single_for_cpu((struct pci_dev *)itv->dev, itv->udma.SG_handle,
+               sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+}
diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c
new file mode 100644 (file)
index 0000000..b53ca50
--- /dev/null
@@ -0,0 +1,545 @@
+/*
+    Vertical Blank Interval support functions
+    Copyright (C) 2004-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-video.h"
+#include "ivtv-vbi.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-queue.h"
+
+static int odd_parity(u8 c)
+{
+       c ^= (c >> 4);
+       c ^= (c >> 2);
+       c ^= (c >> 1);
+
+       return c & 1;
+}
+
+void vbi_schedule_work(struct ivtv *itv)
+{
+       queue_work(itv->vbi.work_queues, &itv->vbi.work_queue);
+}
+
+static void passthrough_vbi_data(struct ivtv *itv, int cnt)
+{
+       int wss = 0;
+       u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 };
+       u8 vps[13];
+       int found_cc = 0;
+       int found_wss = 0;
+       int found_vps = 0;
+       int cc_pos = itv->vbi.cc_pos;
+       int i;
+
+       for (i = 0; i < cnt; i++) {
+               struct v4l2_sliced_vbi_data *d = itv->vbi.sliced_dec_data + i;
+
+               if (d->id == V4L2_SLICED_CAPTION_525 && d->line == 21) {
+                       found_cc = 1;
+                       if (d->field) {
+                               cc[2] = d->data[0];
+                               cc[3] = d->data[1];
+                       } else {
+                               cc[0] = d->data[0];
+                               cc[1] = d->data[1];
+                       }
+               }
+               else if (d->id == V4L2_SLICED_VPS && d->line == 16 && d->field == 0) {
+                       memcpy(vps, d->data, sizeof(vps));
+                       found_vps = 1;
+               }
+               else if (d->id == V4L2_SLICED_WSS_625 && d->line == 23 && d->field == 0) {
+                       wss = d->data[0] | d->data[1] << 8;
+                       found_wss = 1;
+               }
+       }
+
+       if (itv->vbi.wss_found != found_wss || itv->vbi.wss != wss) {
+               itv->vbi.wss = wss;
+               itv->vbi.wss_found = found_wss;
+               set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
+       }
+
+       if (found_vps || itv->vbi.vps_found) {
+               itv->vbi.vps[0] = vps[2];
+               itv->vbi.vps[1] = vps[8];
+               itv->vbi.vps[2] = vps[9];
+               itv->vbi.vps[3] = vps[10];
+               itv->vbi.vps[4] = vps[11];
+               itv->vbi.vps_found = found_vps;
+               set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
+       }
+
+       if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) {
+               itv->vbi.cc_data_odd[cc_pos] = cc[0];
+               itv->vbi.cc_data_odd[cc_pos + 1] = cc[1];
+               itv->vbi.cc_data_even[cc_pos] = cc[2];
+               itv->vbi.cc_data_even[cc_pos + 1] = cc[3];
+               itv->vbi.cc_pos = cc_pos + 2;
+               set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+       }
+}
+
+static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp)
+{
+       int line = 0;
+       int i;
+       u32 linemask[2] = { 0, 0 };
+       unsigned short size;
+       static const u8 mpeg_hdr_data[] = {
+               0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66,
+               0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff,
+               0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80,
+               0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff
+       };
+       const int sd = sizeof(mpeg_hdr_data);   /* start of vbi data */
+       int idx = itv->vbi.frame % IVTV_VBI_FRAMES;
+       u8 *dst = &itv->vbi.sliced_mpeg_data[idx][0];
+
+       for (i = 0; i < lines; i++) {
+               int f, l;
+
+               if (itv->vbi.sliced_data[i].id == 0)
+                       continue;
+
+               l = itv->vbi.sliced_data[i].line - 6;
+               f = itv->vbi.sliced_data[i].field;
+               if (f)
+                       l += 18;
+               if (l < 32)
+                       linemask[0] |= (1 << l);
+               else
+                       linemask[1] |= (1 << (l - 32));
+               dst[sd + 12 + line * 43] = service2vbi(itv->vbi.sliced_data[i].id);
+               memcpy(dst + sd + 12 + line * 43 + 1, itv->vbi.sliced_data[i].data, 42);
+               line++;
+       }
+       memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data));
+       if (line == 36) {
+               /* All lines are used, so there is no space for the linemask
+                  (the max size of the VBI data is 36 * 43 + 4 bytes).
+                  So in this case we use the magic number 'ITV0'. */
+               memcpy(dst + sd, "ITV0", 4);
+               memcpy(dst + sd + 4, dst + sd + 12, line * 43);
+               size = 4 + ((43 * line + 3) & ~3);
+       } else {
+               memcpy(dst + sd, "itv0", 4);
+               memcpy(dst + sd + 4, &linemask[0], 8);
+               size = 12 + ((43 * line + 3) & ~3);
+       }
+       dst[4+16] = (size + 10) >> 8;
+       dst[5+16] = (size + 10) & 0xff;
+       dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6);
+       dst[10+16] = (pts_stamp >> 22) & 0xff;
+       dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff);
+       dst[12+16] = (pts_stamp >> 7) & 0xff;
+       dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1);
+       itv->vbi.sliced_mpeg_size[idx] = sd + size;
+}
+
+static int ivtv_convert_ivtv_vbi(struct ivtv *itv, u8 *p)
+{
+       u32 linemask[2];
+       int i, l, id2;
+       int line = 0;
+
+       if (!memcmp(p, "itv0", 4)) {
+               memcpy(linemask, p + 4, 8);
+               p += 12;
+       } else if (!memcmp(p, "ITV0", 4)) {
+               linemask[0] = 0xffffffff;
+               linemask[1] = 0xf;
+               p += 4;
+       } else {
+               /* unknown VBI data stream */
+               return 0;
+       }
+       for (i = 0; i < 36; i++) {
+               int err = 0;
+
+               if (i < 32 && !(linemask[0] & (1 << i)))
+                       continue;
+               if (i >= 32 && !(linemask[1] & (1 << (i - 32))))
+                       continue;
+               id2 = *p & 0xf;
+               switch (id2) {
+               case IVTV_SLICED_TYPE_TELETEXT_B:
+                       id2 = V4L2_SLICED_TELETEXT_B;
+                       break;
+               case IVTV_SLICED_TYPE_CAPTION_525:
+                       id2 = V4L2_SLICED_CAPTION_525;
+                       err = !odd_parity(p[1]) || !odd_parity(p[2]);
+                       break;
+               case IVTV_SLICED_TYPE_VPS:
+                       id2 = V4L2_SLICED_VPS;
+                       break;
+               case IVTV_SLICED_TYPE_WSS_625:
+                       id2 = V4L2_SLICED_WSS_625;
+                       break;
+               default:
+                       id2 = 0;
+                       break;
+               }
+               if (err == 0) {
+                       l = (i < 18) ? i + 6 : i - 18 + 6;
+                       itv->vbi.sliced_dec_data[line].line = l;
+                       itv->vbi.sliced_dec_data[line].field = i >= 18;
+                       itv->vbi.sliced_dec_data[line].id = id2;
+                       memcpy(itv->vbi.sliced_dec_data[line].data, p + 1, 42);
+                       line++;
+               }
+               p += 43;
+       }
+       while (line < 36) {
+               itv->vbi.sliced_dec_data[line].id = 0;
+               itv->vbi.sliced_dec_data[line].line = 0;
+               itv->vbi.sliced_dec_data[line].field = 0;
+               line++;
+       }
+       return line * sizeof(itv->vbi.sliced_dec_data[0]);
+}
+
+ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count)
+{
+       /* Should be a __user pointer, but sparse doesn't parse this bit correctly. */
+       const struct v4l2_sliced_vbi_data *p = (const struct v4l2_sliced_vbi_data *)ubuf;
+       u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 };
+       int found_cc = 0;
+       int cc_pos = itv->vbi.cc_pos;
+
+       if (itv->vbi.service_set_out == 0)
+               return -EPERM;
+
+       while (count >= sizeof(struct v4l2_sliced_vbi_data)) {
+               switch (p->id) {
+               case V4L2_SLICED_CAPTION_525:
+                       if (p->id == V4L2_SLICED_CAPTION_525 &&
+                           p->line == 21 &&
+                           (itv->vbi.service_set_out &
+                               V4L2_SLICED_CAPTION_525) == 0) {
+                               break;
+                       }
+                       found_cc = 1;
+                       if (p->field) {
+                               cc[2] = p->data[0];
+                               cc[3] = p->data[1];
+                       } else {
+                               cc[0] = p->data[0];
+                               cc[1] = p->data[1];
+                       }
+                       break;
+
+               case V4L2_SLICED_VPS:
+                       if (p->line == 16 && p->field == 0 &&
+                           (itv->vbi.service_set_out & V4L2_SLICED_VPS)) {
+                               itv->vbi.vps[0] = p->data[2];
+                               itv->vbi.vps[1] = p->data[8];
+                               itv->vbi.vps[2] = p->data[9];
+                               itv->vbi.vps[3] = p->data[10];
+                               itv->vbi.vps[4] = p->data[11];
+                               itv->vbi.vps_found = 1;
+                               set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
+                       }
+                       break;
+
+               case V4L2_SLICED_WSS_625:
+                       if (p->line == 23 && p->field == 0 &&
+                           (itv->vbi.service_set_out & V4L2_SLICED_WSS_625)) {
+                               /* No lock needed for WSS */
+                               itv->vbi.wss = p->data[0] | (p->data[1] << 8);
+                               itv->vbi.wss_found = 1;
+                               set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+               count -= sizeof(*p);
+               p++;
+       }
+
+       if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) {
+               itv->vbi.cc_data_odd[cc_pos] = cc[0];
+               itv->vbi.cc_data_odd[cc_pos + 1] = cc[1];
+               itv->vbi.cc_data_even[cc_pos] = cc[2];
+               itv->vbi.cc_data_even[cc_pos + 1] = cc[3];
+               itv->vbi.cc_pos = cc_pos + 2;
+               set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+       }
+
+       return (const char __user *)p - ubuf;
+}
+
+/* Compress raw VBI format, removes leading SAV codes and surplus space after the
+   field.
+   Returns new compressed size. */
+static u32 compress_raw_buf(struct ivtv *itv, u8 *buf, u32 size)
+{
+       u32 line_size = itv->vbi.raw_decoder_line_size;
+       u32 lines = itv->vbi.count;
+       u8 sav1 = itv->vbi.raw_decoder_sav_odd_field;
+       u8 sav2 = itv->vbi.raw_decoder_sav_even_field;
+       u8 *q = buf;
+       u8 *p;
+       int i;
+
+       for (i = 0; i < lines; i++) {
+               p = buf + i * line_size;
+
+               /* Look for SAV code */
+               if (p[0] != 0xff || p[1] || p[2] || (p[3] != sav1 && p[3] != sav2)) {
+                       break;
+               }
+               memcpy(q, p + 4, line_size - 4);
+               q += line_size - 4;
+       }
+       return lines * (line_size - 4);
+}
+
+
+/* Compressed VBI format, all found sliced blocks put next to one another
+   Returns new compressed size */
+static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 sav)
+{
+       u32 line_size = itv->vbi.sliced_decoder_line_size;
+       struct v4l2_decode_vbi_line vbi;
+       int i;
+
+       /* find the first valid line */
+       for (i = 0; i < size; i++, buf++) {
+               if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav)
+                       break;
+       }
+
+       size -= i;
+       if (size < line_size) {
+               return line;
+       }
+       for (i = 0; i < size / line_size; i++) {
+               u8 *p = buf + i * line_size;
+
+               /* Look for SAV code  */
+               if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) {
+                       continue;
+               }
+               vbi.p = p + 4;
+               itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi);
+               if (vbi.type) {
+                       itv->vbi.sliced_data[line].id = vbi.type;
+                       itv->vbi.sliced_data[line].field = vbi.is_second_field;
+                       itv->vbi.sliced_data[line].line = vbi.line;
+                       memcpy(itv->vbi.sliced_data[line].data, vbi.p, 42);
+                       line++;
+               }
+       }
+       return line;
+}
+
+void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
+                          u64 pts_stamp, int streamtype)
+{
+       u8 *p = (u8 *) buf->buf;
+       u32 size = buf->bytesused;
+       int y;
+
+       /* Raw VBI data */
+       if (streamtype == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set == 0) {
+               u8 type;
+
+               ivtv_buf_swap(buf);
+
+               type = p[3];
+
+               size = buf->bytesused = compress_raw_buf(itv, p, size);
+
+               /* second field of the frame? */
+               if (type == itv->vbi.raw_decoder_sav_even_field) {
+                       /* Dirty hack needed for backwards
+                          compatibility of old VBI software. */
+                       p += size - 4;
+                       memcpy(p, &itv->vbi.frame, 4);
+                       itv->vbi.frame++;
+               }
+               return;
+       }
+
+       /* Sliced VBI data with data insertion */
+       if (streamtype == IVTV_ENC_STREAM_TYPE_VBI) {
+               int lines;
+
+               ivtv_buf_swap(buf);
+
+               /* first field */
+               lines = compress_sliced_buf(itv, 0, p, size / 2,
+                       itv->vbi.sliced_decoder_sav_odd_field);
+               /* second field */
+               /* experimentation shows that the second half does not always begin
+                  at the exact address. So start a bit earlier (hence 32). */
+               lines = compress_sliced_buf(itv, lines, p + size / 2 - 32, size / 2 + 32,
+                       itv->vbi.sliced_decoder_sav_even_field);
+               /* always return at least one empty line */
+               if (lines == 0) {
+                       itv->vbi.sliced_data[0].id = 0;
+                       itv->vbi.sliced_data[0].line = 0;
+                       itv->vbi.sliced_data[0].field = 0;
+                       lines = 1;
+               }
+               buf->bytesused = size = lines * sizeof(itv->vbi.sliced_data[0]);
+               memcpy(p, &itv->vbi.sliced_data[0], size);
+
+               if (itv->vbi.insert_mpeg) {
+                       copy_vbi_data(itv, lines, pts_stamp);
+               }
+               itv->vbi.frame++;
+               return;
+       }
+
+       /* Sliced VBI re-inserted from an MPEG stream */
+       if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) {
+               /* If the size is not 4-byte aligned, then the starting address
+                  for the swapping is also shifted. After swapping the data the
+                  real start address of the VBI data is exactly 4 bytes after the
+                  original start. It's a bit fiddly but it works like a charm.
+                  Non-4-byte alignment happens when an lseek is done on the input
+                  mpeg file to a non-4-byte aligned position. So on arrival here
+                  the VBI data is also non-4-byte aligned. */
+               int offset = size & 3;
+               int cnt;
+
+               if (offset) {
+                       p += 4 - offset;
+               }
+               /* Swap Buffer */
+               for (y = 0; y < size; y += 4) {
+                      swab32s((u32 *)(p + y));
+               }
+
+               cnt = ivtv_convert_ivtv_vbi(itv, p + offset);
+               memcpy(buf->buf, itv->vbi.sliced_dec_data, cnt);
+               buf->bytesused = cnt;
+
+               passthrough_vbi_data(itv, cnt / sizeof(itv->vbi.sliced_dec_data[0]));
+               return;
+       }
+}
+
+void ivtv_disable_vbi(struct ivtv *itv)
+{
+       clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
+       clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
+       clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+       ivtv_set_wss(itv, 0, 0);
+       ivtv_set_cc(itv, 0, 0, 0, 0, 0);
+       ivtv_set_vps(itv, 0, 0, 0, 0, 0, 0);
+       itv->vbi.vps_found = itv->vbi.wss_found = 0;
+       itv->vbi.wss = 0;
+       itv->vbi.cc_pos = 0;
+}
+
+void vbi_work_handler(struct work_struct *work)
+{
+       struct vbi_info *info = container_of(work, struct vbi_info, work_queue);
+       struct ivtv *itv = container_of(info, struct ivtv, vbi);
+       struct v4l2_sliced_vbi_data data;
+       DEFINE_WAIT(wait);
+
+       /* Lock */
+       if (itv->output_mode == OUT_PASSTHROUGH) {
+               /* Note: currently only the saa7115 is used in a PVR350,
+                  so these commands are for now saa7115 specific. */
+               if (itv->is_50hz) {
+                       data.id = V4L2_SLICED_WSS_625;
+                       data.field = 0;
+
+                       if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+                               ivtv_set_wss(itv, 1, data.data[0] & 0xf);
+                               itv->vbi.wss_no_update = 0;
+                       } else if (itv->vbi.wss_no_update == 4) {
+                               ivtv_set_wss(itv, 1, 0x8);  /* 4x3 full format */
+                       } else {
+                               itv->vbi.wss_no_update++;
+                       }
+               }
+               else {
+                       u8 c1 = 0, c2 = 0, c3 = 0, c4 = 0;
+                       int mode = 0;
+
+                       data.id = V4L2_SLICED_CAPTION_525;
+                       data.field = 0;
+                       if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+                               mode |= 1;
+                               c1 = data.data[0];
+                               c2 = data.data[1];
+                       }
+                       data.field = 1;
+                       if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+                               mode |= 2;
+                               c3 = data.data[0];
+                               c4 = data.data[1];
+                       }
+                       if (mode) {
+                               itv->vbi.cc_no_update = 0;
+                               ivtv_set_cc(itv, mode, c1, c2, c3, c4);
+                       } else if (itv->vbi.cc_no_update == 4) {
+                               ivtv_set_cc(itv, 0, 0, 0, 0, 0);
+                       } else {
+                               itv->vbi.cc_no_update++;
+                       }
+               }
+               return;
+       }
+
+       if (test_and_clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags)) {
+               /* Lock */
+               ivtv_set_wss(itv, itv->vbi.wss_found, itv->vbi.wss & 0xf);
+       }
+
+       if (test_and_clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags)) {
+               if (itv->vbi.cc_pos == 0) {
+                       ivtv_set_cc(itv, 3, 0x80, 0x80, 0x80, 0x80);
+               }
+               while (itv->vbi.cc_pos) {
+                       u8 cc_odd0 = itv->vbi.cc_data_odd[0];
+                       u8 cc_odd1 = itv->vbi.cc_data_odd[1];
+                       u8 cc_even0 = itv->vbi.cc_data_even[0];
+                       u8 cc_even1 = itv->vbi.cc_data_even[1];
+
+                       memcpy(itv->vbi.cc_data_odd, itv->vbi.cc_data_odd + 2, sizeof(itv->vbi.cc_data_odd) - 2);
+                       memcpy(itv->vbi.cc_data_even, itv->vbi.cc_data_even + 2, sizeof(itv->vbi.cc_data_even) - 2);
+                       itv->vbi.cc_pos -= 2;
+                       if (itv->vbi.cc_pos && cc_odd0 == 0x80 && cc_odd1 == 0x80)
+                               continue;
+
+                       /* Send to Saa7127 */
+                       ivtv_set_cc(itv, 3, cc_odd0, cc_odd1, cc_even0, cc_even1);
+                       if (itv->vbi.cc_pos == 0)
+                               set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+                       break;
+               }
+       }
+
+       if (test_and_clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags)) {
+               /* Lock */
+               ivtv_set_vps(itv, itv->vbi.vps_found,
+                       itv->vbi.vps[0], itv->vbi.vps[1],
+                       itv->vbi.vps[2], itv->vbi.vps[3], itv->vbi.vps[4]);
+       }
+}
diff --git a/drivers/media/video/ivtv/ivtv-vbi.h b/drivers/media/video/ivtv/ivtv-vbi.h
new file mode 100644 (file)
index 0000000..c897e9b
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+    Vertical Blank Interval support functions
+    Copyright (C) 2004-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
+ */
+
+ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count);
+void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
+                          u64 pts_stamp, int streamtype);
+int ivtv_used_line(struct ivtv *itv, int line, int field);
+void ivtv_disable_vbi(struct ivtv *itv);
+void ivtv_set_vbi(unsigned long arg);
+void vbi_work_handler(struct work_struct *work);
+void vbi_schedule_work(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h
new file mode 100644 (file)
index 0000000..85530a3
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+    ivtv driver version information
+    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
+ */
+
+#define IVTV_DRIVER_NAME "ivtv"
+#define IVTV_DRIVER_VERSION_MAJOR 1
+#define IVTV_DRIVER_VERSION_MINOR 0
+#define IVTV_DRIVER_VERSION_PATCHLEVEL 0
+
+#define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL)
+#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL)
diff --git a/drivers/media/video/ivtv/ivtv-video.c b/drivers/media/video/ivtv/ivtv-video.c
new file mode 100644 (file)
index 0000000..77e42d1
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+    saa7127 interface functions
+    Copyright (C) 2004-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-video.h"
+#include "ivtv-i2c.h"
+#include "ivtv-gpio.h"
+#include "ivtv-cards.h"
+#include <media/upd64031a.h>
+#include <media/upd64083.h>
+
+void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3,
+                 u8 vps4, u8 vps5)
+{
+       struct v4l2_sliced_vbi_data data;
+
+       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+               return;
+       data.id = V4L2_SLICED_VPS;
+       data.field = 0;
+       data.line = enabled ? 16 : 0;
+       data.data[4] = vps1;
+       data.data[10] = vps2;
+       data.data[11] = vps3;
+       data.data[12] = vps4;
+       data.data[13] = vps5;
+       ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+}
+
+void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4)
+{
+       struct v4l2_sliced_vbi_data data;
+
+       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+               return;
+       data.id = V4L2_SLICED_CAPTION_525;
+       data.field = 0;
+       data.line = (mode & 1) ? 21 : 0;
+       data.data[0] = cc1;
+       data.data[1] = cc2;
+       ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+       data.field = 1;
+       data.line = (mode & 2) ? 21 : 0;
+       data.data[0] = cc3;
+       data.data[1] = cc4;
+       ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+}
+
+void ivtv_set_wss(struct ivtv *itv, int enabled, int mode)
+{
+       struct v4l2_sliced_vbi_data data;
+
+       if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+               return;
+       /* When using a 50 Hz system, always turn on the
+          wide screen signal with 4x3 ratio as the default.
+          Turning this signal on and off can confuse certain
+          TVs. As far as I can tell there is no reason not to
+          transmit this signal. */
+       if ((itv->std & V4L2_STD_625_50) && !enabled) {
+               enabled = 1;
+               mode = 0x08;  /* 4x3 full format */
+       }
+       data.id = V4L2_SLICED_WSS_625;
+       data.field = 0;
+       data.line = enabled ? 23 : 0;
+       data.data[0] = mode & 0xff;
+       data.data[1] = (mode >> 8) & 0xff;
+       ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+}
+
+void ivtv_encoder_enable(struct ivtv *itv, int enabled)
+{
+       if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+               ivtv_saa7127(itv, enabled ? VIDIOC_STREAMON : VIDIOC_STREAMOFF,
+                               &enabled);
+       }
+}
+
+void ivtv_video_set_io(struct ivtv *itv)
+{
+       struct v4l2_routing route;
+       int inp = itv->active_input;
+       u32 type;
+
+       route.input = itv->card->video_inputs[inp].video_input;
+       route.output = 0;
+       itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+
+       type = itv->card->video_inputs[inp].video_type;
+
+       if (type == IVTV_CARD_INPUT_VID_TUNER) {
+               route.input = 0;  /* Tuner */
+       } else if (type < IVTV_CARD_INPUT_COMPOSITE1) {
+               route.input = 2;  /* S-Video */
+       } else {
+               route.input = 1;  /* Composite */
+       }
+
+       if (itv->card->hw_video & IVTV_HW_GPIO)
+               ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+
+       if (itv->card->hw_video & IVTV_HW_UPD64031A) {
+               if (type == IVTV_CARD_INPUT_VID_TUNER ||
+                   type >= IVTV_CARD_INPUT_COMPOSITE1) {
+                       /* Composite: GR on, connect to 3DYCS */
+                       route.input = UPD64031A_GR_ON | UPD64031A_3DYCS_COMPOSITE;
+               } else {
+                       /* S-Video: GR bypassed, turn it off */
+                       route.input = UPD64031A_GR_OFF | UPD64031A_3DYCS_DISABLE;
+               }
+               route.input |= itv->card->gr_config;
+
+               ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+       }
+
+       if (itv->card->hw_video & IVTV_HW_UPD6408X) {
+               route.input = UPD64083_YCS_MODE;
+               if (type > IVTV_CARD_INPUT_VID_TUNER &&
+                   type < IVTV_CARD_INPUT_COMPOSITE1) {
+                       /* S-Video uses YCNR mode and internal Y-ADC, the upd64031a
+                          is not used. */
+                       route.input |= UPD64083_YCNR_MODE;
+               }
+               else if (itv->card->hw_video & IVTV_HW_UPD64031A) {
+                 /* Use upd64031a output for tuner and composite(CX23416GYC only) inputs */
+                 if ((type == IVTV_CARD_INPUT_VID_TUNER)||
+                     (itv->card->type == IVTV_CARD_CX23416GYC)) {
+                   route.input |= UPD64083_EXT_Y_ADC;
+                 }
+               }
+               ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+       }
+}
diff --git a/drivers/media/video/ivtv/ivtv-video.h b/drivers/media/video/ivtv/ivtv-video.h
new file mode 100644 (file)
index 0000000..5efedeb
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+    saa7127 interface functions
+    Copyright (C) 2004-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
+ */
+
+void ivtv_set_wss(struct ivtv *itv, int enabled, int mode);
+void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4);
+void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3,
+                 u8 vps4, u8 vps5);
+void ivtv_encoder_enable(struct ivtv *itv, int enabled);
+void ivtv_video_set_io(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c
new file mode 100644 (file)
index 0000000..e49ecef
--- /dev/null
@@ -0,0 +1,1129 @@
+/*
+    yuv support
+
+    Copyright (C) 2007  Ian Armstrong <ian@iarmst.demon.co.uk>
+
+    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-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+
+static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
+                                struct ivtv_dma_frame *args)
+{
+       struct ivtv_dma_page_info y_dma;
+       struct ivtv_dma_page_info uv_dma;
+
+       int i;
+       int y_pages, uv_pages;
+
+       unsigned long y_buffer_offset, uv_buffer_offset;
+       int y_decode_height, uv_decode_height, y_size;
+       int frame = atomic_read(&itv->yuv_info.next_fill_frame);
+
+       y_buffer_offset = IVTV_DEC_MEM_START + yuv_offset[frame];
+       uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET;
+
+       y_decode_height = uv_decode_height = args->src.height + args->src.top;
+
+       if (y_decode_height < 512-16)
+               y_buffer_offset += 720 * 16;
+
+       if (y_decode_height & 15)
+               y_decode_height = (y_decode_height + 16) & ~15;
+
+       if (uv_decode_height & 31)
+               uv_decode_height = (uv_decode_height + 32) & ~31;
+
+       y_size = 720 * y_decode_height;
+
+       /* Still in USE */
+       if (dma->SG_length || dma->page_count) {
+               IVTV_DEBUG_WARN("prep_user_dma: SG_length %d page_count %d still full?\n",
+                               dma->SG_length, dma->page_count);
+               return -EBUSY;
+       }
+
+       ivtv_udma_get_page_info (&y_dma, (unsigned long)args->y_source, 720 * y_decode_height);
+       ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height);
+
+       /* Get user pages for DMA Xfer */
+       down_read(&current->mm->mmap_sem);
+       y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL);
+       uv_pages = get_user_pages(current, current->mm, uv_dma.uaddr, uv_dma.page_count, 0, 1, &dma->map[y_pages], NULL);
+       up_read(&current->mm->mmap_sem);
+
+       dma->page_count = y_dma.page_count + uv_dma.page_count;
+
+       if (y_pages + uv_pages != dma->page_count) {
+               IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
+                               y_pages + uv_pages, dma->page_count);
+
+               for (i = 0; i < dma->page_count; i++) {
+                       put_page(dma->map[i]);
+               }
+               dma->page_count = 0;
+               return -EINVAL;
+       }
+
+       /* Fill & map SG List */
+       ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0));
+       dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+
+       /* Fill SG Array with new values */
+       ivtv_udma_fill_sg_array (dma, y_buffer_offset, uv_buffer_offset, y_size);
+
+       /* If we've offset the y plane, ensure top area is blanked */
+       if (args->src.height + args->src.top < 512-16) {
+               if (itv->yuv_info.blanking_dmaptr) {
+                       dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16);
+                       dma->SGarray[dma->SG_length].src = cpu_to_le32(itv->yuv_info.blanking_dmaptr);
+                       dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DEC_MEM_START + yuv_offset[frame]);
+                       dma->SG_length++;
+               }
+       }
+
+       /* Tag SG Array with Interrupt Bit */
+       dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+       ivtv_udma_sync_for_device(itv);
+       return 0;
+}
+
+/* We rely on a table held in the firmware - Quick check. */
+int ivtv_yuv_filter_check(struct ivtv *itv)
+{
+       int i, offset_y, offset_uv;
+
+       for (i=0, offset_y = 16, offset_uv = 4; i<16; i++, offset_y += 24, offset_uv += 12) {
+               if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + offset_y) != i << 16) ||
+                   (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + offset_uv) != i << 16)) {
+                       IVTV_WARN ("YUV filter table not found in firmware.\n");
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2)
+{
+       int filter_index, filter_line;
+
+       /* If any filter is -1, then don't update it */
+       if (h_filter > -1) {
+               if (h_filter > 4) h_filter = 4;
+               filter_index = h_filter * 384;
+               filter_line = 0;
+               while (filter_line < 16) {
+                       write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02804);
+                       write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0281c);
+                       filter_index += 4;
+                       write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02808);
+                       write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02820);
+                       filter_index += 4;
+                       write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0280c);
+                       write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02824);
+                       filter_index += 4;
+                       write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02810);
+                       write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02828);
+                       filter_index += 4;
+                       write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02814);
+                       write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0282c);
+                       filter_index += 8;
+                       write_reg(0, 0x02818);
+                       write_reg(0, 0x02830);
+                       filter_line ++;
+               }
+               IVTV_DEBUG_YUV("h_filter -> %d\n",h_filter);
+       }
+
+       if (v_filter_1 > -1) {
+               if (v_filter_1 > 4) v_filter_1 = 4;
+               filter_index = v_filter_1 * 192;
+               filter_line = 0;
+               while (filter_line < 16) {
+                       write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02900);
+                       filter_index += 4;
+                       write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02904);
+                       filter_index += 8;
+                       write_reg(0, 0x02908);
+                       filter_line ++;
+               }
+               IVTV_DEBUG_YUV("v_filter_1 -> %d\n",v_filter_1);
+       }
+
+       if (v_filter_2 > -1) {
+               if (v_filter_2 > 4) v_filter_2 = 4;
+               filter_index = v_filter_2 * 192;
+               filter_line = 0;
+               while (filter_line < 16) {
+                       write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x0290c);
+                       filter_index += 4;
+                       write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02910);
+                       filter_index += 8;
+                       write_reg(0, 0x02914);
+                       filter_line ++;
+               }
+               IVTV_DEBUG_YUV("v_filter_2 -> %d\n",v_filter_2);
+       }
+}
+
+static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *window)
+{
+       u32 reg_2834, reg_2838, reg_283c;
+       u32 reg_2844, reg_2854, reg_285c;
+       u32 reg_2864, reg_2874, reg_2890;
+       u32 reg_2870, reg_2870_base, reg_2870_offset;
+       int x_cutoff;
+       int h_filter;
+       u32 master_width;
+
+       IVTV_DEBUG_WARN( "Need to adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n",
+                        window->tru_w, window->src_w, window->dst_w,window->src_x, window->dst_x);
+
+       /* How wide is the src image */
+       x_cutoff  = window->src_w + window->src_x;
+
+       /* Set the display width */
+       reg_2834 = window->dst_w;
+       reg_2838 = reg_2834;
+
+       /* Set the display position */
+       reg_2890 = window->dst_x;
+
+       /* Index into the image horizontally */
+       reg_2870 = 0;
+
+       /* 2870 is normally fudged to align video coords with osd coords.
+          If running full screen, it causes an unwanted left shift
+          Remove the fudge if we almost fill the screen.
+          Gradually adjust the offset to avoid the video 'snapping'
+          left/right if it gets dragged through this region.
+          Only do this if osd is full width. */
+       if (window->vis_w == 720) {
+               if ((window->tru_x - window->pan_x > -1) && (window->tru_x - window->pan_x <= 40) && (window->dst_w >= 680)){
+                       reg_2870 = 10 - (window->tru_x - window->pan_x) / 4;
+               }
+               else if ((window->tru_x - window->pan_x < 0) && (window->tru_x - window->pan_x >= -20) && (window->dst_w >= 660)) {
+                       reg_2870 = (10 + (window->tru_x - window->pan_x) / 2);
+               }
+
+               if (window->dst_w >= window->src_w)
+                       reg_2870 = reg_2870 << 16 | reg_2870;
+               else
+                       reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1);
+       }
+
+       if (window->dst_w < window->src_w)
+               reg_2870 = 0x000d000e - reg_2870;
+       else
+               reg_2870 = 0x0012000e - reg_2870;
+
+       /* We're also using 2870 to shift the image left (src_x & negative dst_x) */
+       reg_2870_offset = (window->src_x*((window->dst_w << 21)/window->src_w))>>19;
+
+       if (window->dst_w >= window->src_w) {
+               x_cutoff &= ~1;
+               master_width = (window->src_w * 0x00200000) / (window->dst_w);
+               if (master_width * window->dst_w != window->src_w * 0x00200000) master_width ++;
+               reg_2834 = (reg_2834 << 16) | x_cutoff;
+               reg_2838 = (reg_2838 << 16) | x_cutoff;
+               reg_283c = master_width >> 2;
+               reg_2844 = master_width >> 2;
+               reg_2854 = master_width;
+               reg_285c = master_width >> 1;
+               reg_2864 = master_width >> 1;
+
+               /* We also need to factor in the scaling
+                  (src_w - dst_w) / (src_w / 4) */
+               if (window->dst_w > window->src_w)
+                       reg_2870_base = ((window->dst_w - window->src_w)<<16) / (window->src_w <<14);
+               else
+                       reg_2870_base = 0;
+
+               reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 2) + (reg_2870_base << 17 | reg_2870_base);
+               reg_2874 = 0;
+       }
+       else if (window->dst_w < window->src_w / 2) {
+               master_width = (window->src_w * 0x00080000) / window->dst_w;
+               if (master_width * window->dst_w != window->src_w * 0x00080000) master_width ++;
+               reg_2834 = (reg_2834 << 16) | x_cutoff;
+               reg_2838 = (reg_2838 << 16) | x_cutoff;
+               reg_283c = master_width >> 2;
+               reg_2844 = master_width >> 1;
+               reg_2854 = master_width;
+               reg_285c = master_width >> 1;
+               reg_2864 = master_width >> 1;
+               reg_2870 += (((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset);
+               reg_2870 += (5 - (((window->src_w + window->src_w / 2) - 1) / window->dst_w)) << 16;
+               reg_2874 = 0x00000012;
+       }
+       else {
+               master_width = (window->src_w * 0x00100000) / window->dst_w;
+               if (master_width * window->dst_w != window->src_w * 0x00100000) master_width ++;
+               reg_2834 = (reg_2834 << 16) | x_cutoff;
+               reg_2838 = (reg_2838 << 16) | x_cutoff;
+               reg_283c = master_width >> 2;
+               reg_2844 = master_width >> 1;
+               reg_2854 = master_width;
+               reg_285c = master_width >> 1;
+               reg_2864 = master_width >> 1;
+               reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1);
+               reg_2870 += (5 - (((window->src_w * 3) - 1) / window->dst_w)) << 16;
+               reg_2874 = 0x00000001;
+       }
+
+       /* Select the horizontal filter */
+       if (window->src_w == window->dst_w) {
+               /* An exact size match uses filter 0 */
+               h_filter = 0;
+       }
+       else {
+               /* Figure out which filter to use */
+               h_filter = ((window->src_w << 16) / window->dst_w) >> 15;
+               h_filter = (h_filter >> 1) + (h_filter & 1);
+               /* Only an exact size match can use filter 0 */
+               if (h_filter == 0) h_filter = 1;
+       }
+
+       write_reg(reg_2834, 0x02834);
+       write_reg(reg_2838, 0x02838);
+       IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",itv->yuv_info.reg_2834, reg_2834, itv->yuv_info.reg_2838, reg_2838);
+
+       write_reg(reg_283c, 0x0283c);
+       write_reg(reg_2844, 0x02844);
+
+       IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",itv->yuv_info.reg_283c, reg_283c, itv->yuv_info.reg_2844, reg_2844);
+
+       write_reg(0x00080514, 0x02840);
+       write_reg(0x00100514, 0x02848);
+       IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",itv->yuv_info.reg_2840, 0x00080514, itv->yuv_info.reg_2848, 0x00100514);
+
+       write_reg(reg_2854, 0x02854);
+       IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",itv->yuv_info.reg_2854, reg_2854);
+
+       write_reg(reg_285c, 0x0285c);
+       write_reg(reg_2864, 0x02864);
+       IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",itv->yuv_info.reg_285c, reg_285c, itv->yuv_info.reg_2864, reg_2864);
+
+       write_reg(reg_2874, 0x02874);
+       IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",itv->yuv_info.reg_2874, reg_2874);
+
+       write_reg(reg_2870, 0x02870);
+       IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",itv->yuv_info.reg_2870, reg_2870);
+
+       write_reg( reg_2890,0x02890);
+       IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",itv->yuv_info.reg_2890, reg_2890);
+
+       /* Only update the filter if we really need to */
+       if (h_filter != itv->yuv_info.h_filter) {
+               ivtv_yuv_filter (itv,h_filter,-1,-1);
+               itv->yuv_info.h_filter = h_filter;
+       }
+}
+
+static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *window)
+{
+       u32 master_height;
+       u32 reg_2918, reg_291c, reg_2920, reg_2928;
+       u32 reg_2930, reg_2934, reg_293c;
+       u32 reg_2940, reg_2944, reg_294c;
+       u32 reg_2950, reg_2954, reg_2958, reg_295c;
+       u32 reg_2960, reg_2964, reg_2968, reg_296c;
+       u32 reg_289c;
+       u32 src_y_major_y, src_y_minor_y;
+       u32 src_y_major_uv, src_y_minor_uv;
+       u32 reg_2964_base, reg_2968_base;
+       int v_filter_1, v_filter_2;
+
+       IVTV_DEBUG_WARN("Need to adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n",
+               window->tru_h, window->src_h, window->dst_h,window->src_y, window->dst_y);
+
+       /* What scaling mode is being used... */
+       if (window->interlaced_y) {
+               IVTV_DEBUG_YUV("Scaling mode Y: Interlaced\n");
+       }
+       else {
+               IVTV_DEBUG_YUV("Scaling mode Y: Progressive\n");
+       }
+
+       if (window->interlaced_uv) {
+               IVTV_DEBUG_YUV("Scaling mode UV: Interlaced\n");
+       }
+       else {
+               IVTV_DEBUG_YUV("Scaling mode UV: Progressive\n");
+       }
+
+       /* What is the source video being treated as... */
+       if (itv->yuv_info.frame_interlaced) {
+               IVTV_DEBUG_WARN("Source video: Interlaced\n");
+       }
+       else {
+               IVTV_DEBUG_WARN("Source video: Non-interlaced\n");
+       }
+
+       /* We offset into the image using two different index methods, so split
+          the y source coord into two parts. */
+       if (window->src_y < 8) {
+               src_y_minor_uv = window->src_y;
+               src_y_major_uv = 0;
+       }
+       else {
+               src_y_minor_uv = 8;
+               src_y_major_uv = window->src_y - 8;
+       }
+
+       src_y_minor_y = src_y_minor_uv;
+       src_y_major_y = src_y_major_uv;
+
+       if (window->offset_y) src_y_minor_y += 16;
+
+       if (window->interlaced_y)
+               reg_2918 = (window->dst_h << 16) | (window->src_h + src_y_minor_y);
+       else
+               reg_2918 = (window->dst_h << 16) | ((window->src_h + src_y_minor_y) << 1);
+
+       if (window->interlaced_uv)
+               reg_291c = (window->dst_h << 16) | ((window->src_h + src_y_minor_uv) >> 1);
+       else
+               reg_291c = (window->dst_h << 16) | (window->src_h + src_y_minor_uv);
+
+       reg_2964_base = (src_y_minor_y * ((window->dst_h << 16)/window->src_h)) >> 14;
+       reg_2968_base = (src_y_minor_uv * ((window->dst_h << 16)/window->src_h)) >> 14;
+
+       if (window->dst_h / 2 >= window->src_h && !window->interlaced_y) {
+               master_height = (window->src_h * 0x00400000) / window->dst_h;
+               if ((window->src_h * 0x00400000) - (master_height * window->dst_h) >= window->dst_h / 2) master_height ++;
+               reg_2920 = master_height >> 2;
+               reg_2928 = master_height >> 3;
+               reg_2930 = master_height;
+               reg_2940 = master_height >> 1;
+               reg_2964_base >>= 3;
+               reg_2968_base >>= 3;
+               reg_296c = 0x00000000;
+       }
+       else if (window->dst_h >= window->src_h) {
+               master_height = (window->src_h * 0x00400000) / window->dst_h;
+               master_height = (master_height >> 1) + (master_height & 1);
+               reg_2920 = master_height >> 2;
+               reg_2928 = master_height >> 2;
+               reg_2930 = master_height;
+               reg_2940 = master_height >> 1;
+               reg_296c = 0x00000000;
+               if (window->interlaced_y) {
+                       reg_2964_base >>= 3;
+               }
+               else {
+                       reg_296c ++;
+                       reg_2964_base >>= 2;
+               }
+               if (window->interlaced_uv) reg_2928 >>= 1;
+               reg_2968_base >>= 3;
+       }
+       else if (window->dst_h >= window->src_h / 2) {
+               master_height = (window->src_h * 0x00200000) / window->dst_h;
+               master_height = (master_height >> 1) + (master_height & 1);
+               reg_2920 = master_height >> 2;
+               reg_2928 = master_height >> 2;
+               reg_2930 = master_height;
+               reg_2940 = master_height;
+               reg_296c = 0x00000101;
+               if (window->interlaced_y) {
+                       reg_2964_base >>= 2;
+               }
+               else {
+                       reg_296c ++;
+                       reg_2964_base >>= 1;
+               }
+               if (window->interlaced_uv) reg_2928 >>= 1;
+               reg_2968_base >>= 2;
+       }
+       else {
+               master_height = (window->src_h * 0x00100000) / window->dst_h;
+               master_height = (master_height >> 1) + (master_height & 1);
+               reg_2920 = master_height >> 2;
+               reg_2928 = master_height >> 2;
+               reg_2930 = master_height;
+               reg_2940 = master_height;
+               reg_2964_base >>= 1;
+               reg_2968_base >>= 2;
+               reg_296c = 0x00000102;
+       }
+
+       /* FIXME These registers change depending on scaled / unscaled output
+          We really need to work out what they should be */
+       if (window->src_h == window->dst_h){
+               reg_2934 = 0x00020000;
+               reg_293c = 0x00100000;
+               reg_2944 = 0x00040000;
+               reg_294c = 0x000b0000;
+       }
+       else {
+               reg_2934 = 0x00000FF0;
+               reg_293c = 0x00000FF0;
+               reg_2944 = 0x00000FF0;
+               reg_294c = 0x00000FF0;
+       }
+
+       /* The first line to be displayed */
+       reg_2950 = 0x00010000 + src_y_major_y;
+       if (window->interlaced_y) reg_2950 += 0x00010000;
+       reg_2954 = reg_2950 + 1;
+
+       reg_2958 = 0x00010000 + (src_y_major_y >> 1);
+       if (window->interlaced_uv) reg_2958 += 0x00010000;
+       reg_295c = reg_2958 + 1;
+
+       if (itv->yuv_info.decode_height == 480)
+               reg_289c = 0x011e0017;
+       else
+               reg_289c = 0x01500017;
+
+       if (window->dst_y < 0)
+               reg_289c = (reg_289c - ((window->dst_y & ~1)<<15))-(window->dst_y >>1);
+       else
+               reg_289c = (reg_289c + ((window->dst_y & ~1)<<15))+(window->dst_y >>1);
+
+       /* How much of the source to decode.
+          Take into account the source offset */
+       reg_2960 = ((src_y_minor_y + window->src_h + src_y_major_y) - 1 ) |
+                       ((((src_y_minor_uv + window->src_h + src_y_major_uv) - 1) & ~1) << 15);
+
+       /* Calculate correct value for register 2964 */
+       if (window->src_h == window->dst_h)
+               reg_2964 = 1;
+       else {
+               reg_2964 = 2 + ((window->dst_h << 1) / window->src_h);
+               reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1);
+       }
+       reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1);
+       reg_2964 = (reg_2964 << 16) + reg_2964 + (reg_2964 * 46 / 94);
+
+       /* Okay, we've wasted time working out the correct value,
+          but if we use it, it fouls the the window alignment.
+          Fudge it to what we want... */
+       reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16));
+       reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16));
+
+       /* Deviate further from what it should be. I find the flicker headache
+          inducing so try to reduce it slightly. Leave 2968 as-is otherwise
+          colours foul. */
+       if ((reg_2964 != 0x00010001) && (window->dst_h / 2 <= window->src_h))
+               reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF)/2);
+
+       if (!window->interlaced_y) reg_2964 -= 0x00010001;
+       if (!window->interlaced_uv) reg_2968 -= 0x00010001;
+
+       reg_2964 += ((reg_2964_base << 16) | reg_2964_base);
+       reg_2968 += ((reg_2968_base << 16) | reg_2968_base);
+
+       /* Select the vertical filter */
+       if (window->src_h == window->dst_h) {
+               /* An exact size match uses filter 0/1 */
+               v_filter_1 = 0;
+               v_filter_2 = 1;
+       }
+       else {
+               /* Figure out which filter to use */
+               v_filter_1 = ((window->src_h << 16) / window->dst_h) >> 15;
+               v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
+               /* Only an exact size match can use filter 0 */
+               if (v_filter_1 == 0) v_filter_1 = 1;
+               v_filter_2 = v_filter_1;
+       }
+
+       write_reg(reg_2934, 0x02934);
+       write_reg(reg_293c, 0x0293c);
+       IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",itv->yuv_info.reg_2934, reg_2934, itv->yuv_info.reg_293c, reg_293c);
+       write_reg(reg_2944, 0x02944);
+       write_reg(reg_294c, 0x0294c);
+       IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",itv->yuv_info.reg_2944, reg_2944, itv->yuv_info.reg_294c, reg_294c);
+
+       /* Ensure 2970 is 0 (does it ever change ?) */
+/*     write_reg(0,0x02970); */
+/*     IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n",itv->yuv_info.reg_2970, 0); */
+
+       write_reg(reg_2930, 0x02938);
+       write_reg(reg_2930, 0x02930);
+       IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",itv->yuv_info.reg_2930, reg_2930, itv->yuv_info.reg_2938, reg_2930);
+
+       write_reg(reg_2928, 0x02928);
+       write_reg(reg_2928+0x514, 0x0292C);
+       IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",itv->yuv_info.reg_2928, reg_2928, itv->yuv_info.reg_292c, reg_2928+0x514);
+
+       write_reg(reg_2920, 0x02920);
+       write_reg(reg_2920+0x514, 0x02924);
+       IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",itv->yuv_info.reg_2920, reg_2920, itv->yuv_info.reg_2924, 0x514+reg_2920);
+
+       write_reg (reg_2918,0x02918);
+       write_reg (reg_291c,0x0291C);
+       IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",itv->yuv_info.reg_2918,reg_2918,itv->yuv_info.reg_291c,reg_291c);
+
+       write_reg(reg_296c, 0x0296c);
+       IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",itv->yuv_info.reg_296c, reg_296c);
+
+       write_reg(reg_2940, 0x02948);
+       write_reg(reg_2940, 0x02940);
+       IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",itv->yuv_info.reg_2940, reg_2940, itv->yuv_info.reg_2948, reg_2940);
+
+       write_reg(reg_2950, 0x02950);
+       write_reg(reg_2954, 0x02954);
+       IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",itv->yuv_info.reg_2950, reg_2950, itv->yuv_info.reg_2954, reg_2954);
+
+       write_reg(reg_2958, 0x02958);
+       write_reg(reg_295c, 0x0295C);
+       IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",itv->yuv_info.reg_2958, reg_2958, itv->yuv_info.reg_295c, reg_295c);
+
+       write_reg(reg_2960, 0x02960);
+       IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",itv->yuv_info.reg_2960, reg_2960);
+
+       write_reg(reg_2964, 0x02964);
+       write_reg(reg_2968, 0x02968);
+       IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",itv->yuv_info.reg_2964, reg_2964, itv->yuv_info.reg_2968, reg_2968);
+
+       write_reg( reg_289c,0x0289c);
+       IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",itv->yuv_info.reg_289c, reg_289c);
+
+       /* Only update filter 1 if we really need to */
+       if (v_filter_1 != itv->yuv_info.v_filter_1) {
+               ivtv_yuv_filter (itv,-1,v_filter_1,-1);
+               itv->yuv_info.v_filter_1 = v_filter_1;
+       }
+
+       /* Only update filter 2 if we really need to */
+       if (v_filter_2 != itv->yuv_info.v_filter_2) {
+               ivtv_yuv_filter (itv,-1,-1,v_filter_2);
+               itv->yuv_info.v_filter_2 = v_filter_2;
+       }
+
+       itv->yuv_info.frame_interlaced_last = itv->yuv_info.frame_interlaced;
+       itv->yuv_info.lace_threshold_last = itv->yuv_info.lace_threshold;
+}
+
+/* Modify the supplied coordinate information to fit the visible osd area */
+static u32 ivtv_yuv_window_setup (struct ivtv *itv, struct yuv_frame_info *window)
+{
+       int osd_crop;
+       u32 osd_scale;
+       u32 yuv_update = 0;
+
+       /* Work out the lace settings */
+       switch (itv->yuv_info.lace_mode) {
+               case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */
+                       itv->yuv_info.frame_interlaced = 0;
+                       if (window->tru_h < 512 || (window->tru_h > 576 && window->tru_h < 1021))
+                               window->interlaced_y = 0;
+                       else
+                               window->interlaced_y = 1;
+
+                       if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2))
+                               window->interlaced_uv = 0;
+                       else
+                               window->interlaced_uv = 1;
+                       break;
+
+               case IVTV_YUV_MODE_AUTO:
+                       if (window->tru_h <= itv->yuv_info.lace_threshold || window->tru_h > 576 || window->tru_w > 720){
+                               itv->yuv_info.frame_interlaced = 0;
+                               if ((window->tru_h < 512) ||
+                                 (window->tru_h > 576 && window->tru_h < 1021) ||
+                                 (window->tru_w > 720 && window->tru_h < 1021))
+                                       window->interlaced_y = 0;
+                               else
+                                       window->interlaced_y = 1;
+
+                               if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2))
+                                       window->interlaced_uv = 0;
+                               else
+                                       window->interlaced_uv = 1;
+                       }
+                       else {
+                               itv->yuv_info.frame_interlaced = 1;
+                               window->interlaced_y = 1;
+                               window->interlaced_uv = 1;
+                       }
+                       break;
+
+                       case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */
+               default:
+                       itv->yuv_info.frame_interlaced = 1;
+                       window->interlaced_y = 1;
+                       window->interlaced_uv = 1;
+                       break;
+       }
+
+       /* Sorry, but no negative coords for src */
+       if (window->src_x < 0) window->src_x = 0;
+       if (window->src_y < 0) window->src_y = 0;
+
+       /* Can only reduce width down to 1/4 original size */
+       if ((osd_crop = window->src_w - ( 4 * window->dst_w )) > 0) {
+               window->src_x += osd_crop / 2;
+               window->src_w = (window->src_w - osd_crop) & ~3;
+               window->dst_w = window->src_w / 4;
+               window->dst_w += window->dst_w & 1;
+       }
+
+       /* Can only reduce height down to 1/4 original size */
+       if (window->src_h / window->dst_h >= 2) {
+               /* Overflow may be because we're running progressive, so force mode switch */
+               window->interlaced_y = 1;
+               /* Make sure we're still within limits for interlace */
+               if ((osd_crop = window->src_h - ( 4 * window->dst_h )) > 0) {
+                       /* If we reach here we'll have to force the height. */
+                       window->src_y += osd_crop / 2;
+                       window->src_h = (window->src_h - osd_crop) & ~3;
+                       window->dst_h = window->src_h / 4;
+                       window->dst_h += window->dst_h & 1;
+               }
+       }
+
+       /* If there's nothing to safe to display, we may as well stop now */
+       if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) {
+               return 0;
+       }
+
+       /* Ensure video remains inside OSD area */
+       osd_scale = (window->src_h << 16) / window->dst_h;
+
+       if ((osd_crop = window->pan_y - window->dst_y) > 0) {
+               /* Falls off the upper edge - crop */
+               window->src_y += (osd_scale * osd_crop) >> 16;
+               window->src_h -= (osd_scale * osd_crop) >> 16;
+               window->dst_h -= osd_crop;
+               window->dst_y = 0;
+       }
+       else {
+               window->dst_y -= window->pan_y;
+       }
+
+       if ((osd_crop = window->dst_h + window->dst_y - window->vis_h) > 0) {
+               /* Falls off the lower edge - crop */
+               window->dst_h -= osd_crop;
+               window->src_h -= (osd_scale * osd_crop) >> 16;
+       }
+
+       osd_scale = (window->src_w << 16) / window->dst_w;
+
+       if ((osd_crop = window->pan_x - window->dst_x) > 0) {
+               /* Fall off the left edge - crop */
+               window->src_x += (osd_scale * osd_crop) >> 16;
+               window->src_w -= (osd_scale * osd_crop) >> 16;
+               window->dst_w -= osd_crop;
+               window->dst_x = 0;
+       }
+       else {
+               window->dst_x -= window->pan_x;
+       }
+
+       if ((osd_crop = window->dst_w + window->dst_x - window->vis_w) > 0) {
+               /* Falls off the right edge - crop */
+               window->dst_w -= osd_crop;
+               window->src_w -= (osd_scale * osd_crop) >> 16;
+       }
+
+       /* The OSD can be moved. Track to it */
+       window->dst_x += itv->yuv_info.osd_x_offset;
+       window->dst_y += itv->yuv_info.osd_y_offset;
+
+       /* Width & height for both src & dst must be even.
+          Same for coordinates. */
+       window->dst_w &= ~1;
+       window->dst_x &= ~1;
+
+       window->src_w += window->src_x & 1;
+       window->src_x &= ~1;
+
+       window->src_w &= ~1;
+       window->dst_w &= ~1;
+
+       window->dst_h &= ~1;
+       window->dst_y &= ~1;
+
+       window->src_h += window->src_y & 1;
+       window->src_y &= ~1;
+
+       window->src_h &= ~1;
+       window->dst_h &= ~1;
+
+       /* Due to rounding, we may have reduced the output size to <1/4 of the source
+          Check again, but this time just resize. Don't change source coordinates */
+       if (window->dst_w < window->src_w / 4) {
+               window->src_w &= ~3;
+               window->dst_w = window->src_w / 4;
+               window->dst_w += window->dst_w & 1;
+       }
+       if (window->dst_h < window->src_h / 4) {
+               window->src_h &= ~3;
+               window->dst_h = window->src_h / 4;
+               window->dst_h += window->dst_h & 1;
+       }
+
+       /* Check again. If there's nothing to safe to display, stop now */
+       if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) {
+               return 0;
+       }
+
+       /* Both x offset & width are linked, so they have to be done together */
+       if ((itv->yuv_info.old_frame_info.dst_w != window->dst_w) ||
+           (itv->yuv_info.old_frame_info.src_w != window->src_w) ||
+           (itv->yuv_info.old_frame_info.dst_x != window->dst_x) ||
+           (itv->yuv_info.old_frame_info.src_x != window->src_x) ||
+           (itv->yuv_info.old_frame_info.pan_x != window->pan_x) ||
+           (itv->yuv_info.old_frame_info.vis_w != window->vis_w)) {
+               yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL;
+       }
+
+       if ((itv->yuv_info.old_frame_info.src_h != window->src_h) ||
+           (itv->yuv_info.old_frame_info.dst_h != window->dst_h) ||
+           (itv->yuv_info.old_frame_info.dst_y != window->dst_y) ||
+           (itv->yuv_info.old_frame_info.src_y != window->src_y) ||
+           (itv->yuv_info.old_frame_info.pan_y != window->pan_y) ||
+           (itv->yuv_info.old_frame_info.vis_h != window->vis_h) ||
+           (itv->yuv_info.old_frame_info.interlaced_y != window->interlaced_y) ||
+           (itv->yuv_info.old_frame_info.interlaced_uv != window->interlaced_uv)) {
+               yuv_update |= IVTV_YUV_UPDATE_VERTICAL;
+       }
+
+       return yuv_update;
+}
+
+/* Update the scaling register to the requested value */
+void ivtv_yuv_work_handler (struct work_struct *work)
+{
+       struct yuv_playback_info *info = container_of(work, struct yuv_playback_info, work_queue);
+       struct ivtv *itv = container_of(info, struct ivtv, yuv_info);
+       DEFINE_WAIT(wait);
+
+       struct yuv_frame_info window;
+       u32 yuv_update;
+
+       int frame = itv->yuv_info.update_frame;
+
+/*     IVTV_DEBUG_YUV("Update yuv registers for frame %d\n",frame); */
+       memcpy(&window, &itv->yuv_info.new_frame_info[frame], sizeof (window));
+
+       /* Update the osd pan info */
+       window.pan_x = itv->yuv_info.osd_x_pan;
+       window.pan_y = itv->yuv_info.osd_y_pan;
+       window.vis_w = itv->yuv_info.osd_vis_w;
+       window.vis_h = itv->yuv_info.osd_vis_h;
+
+       /* Calculate the display window coordinates. Exit if nothing left */
+       if (!(yuv_update = ivtv_yuv_window_setup (itv, &window)))
+               return;
+
+       /* Update horizontal settings */
+       if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL)
+               ivtv_yuv_handle_horizontal(itv, &window);
+
+       if (yuv_update & IVTV_YUV_UPDATE_VERTICAL)
+               ivtv_yuv_handle_vertical(itv, &window);
+
+       memcpy(&itv->yuv_info.old_frame_info, &window, sizeof (itv->yuv_info.old_frame_info));
+}
+
+static void ivtv_yuv_init (struct ivtv *itv)
+{
+       IVTV_DEBUG_YUV("ivtv_yuv_init\n");
+
+       /* Take a snapshot of the current register settings */
+       itv->yuv_info.reg_2834 = read_reg(0x02834);
+       itv->yuv_info.reg_2838 = read_reg(0x02838);
+       itv->yuv_info.reg_283c = read_reg(0x0283c);
+       itv->yuv_info.reg_2840 = read_reg(0x02840);
+       itv->yuv_info.reg_2844 = read_reg(0x02844);
+       itv->yuv_info.reg_2848 = read_reg(0x02848);
+       itv->yuv_info.reg_2854 = read_reg(0x02854);
+       itv->yuv_info.reg_285c = read_reg(0x0285c);
+       itv->yuv_info.reg_2864 = read_reg(0x02864);
+       itv->yuv_info.reg_2870 = read_reg(0x02870);
+       itv->yuv_info.reg_2874 = read_reg(0x02874);
+       itv->yuv_info.reg_2898 = read_reg(0x02898);
+       itv->yuv_info.reg_2890 = read_reg(0x02890);
+
+       itv->yuv_info.reg_289c = read_reg(0x0289c);
+       itv->yuv_info.reg_2918 = read_reg(0x02918);
+       itv->yuv_info.reg_291c = read_reg(0x0291c);
+       itv->yuv_info.reg_2920 = read_reg(0x02920);
+       itv->yuv_info.reg_2924 = read_reg(0x02924);
+       itv->yuv_info.reg_2928 = read_reg(0x02928);
+       itv->yuv_info.reg_292c = read_reg(0x0292c);
+       itv->yuv_info.reg_2930 = read_reg(0x02930);
+       itv->yuv_info.reg_2934 = read_reg(0x02934);
+       itv->yuv_info.reg_2938 = read_reg(0x02938);
+       itv->yuv_info.reg_293c = read_reg(0x0293c);
+       itv->yuv_info.reg_2940 = read_reg(0x02940);
+       itv->yuv_info.reg_2944 = read_reg(0x02944);
+       itv->yuv_info.reg_2948 = read_reg(0x02948);
+       itv->yuv_info.reg_294c = read_reg(0x0294c);
+       itv->yuv_info.reg_2950 = read_reg(0x02950);
+       itv->yuv_info.reg_2954 = read_reg(0x02954);
+       itv->yuv_info.reg_2958 = read_reg(0x02958);
+       itv->yuv_info.reg_295c = read_reg(0x0295c);
+       itv->yuv_info.reg_2960 = read_reg(0x02960);
+       itv->yuv_info.reg_2964 = read_reg(0x02964);
+       itv->yuv_info.reg_2968 = read_reg(0x02968);
+       itv->yuv_info.reg_296c = read_reg(0x0296c);
+       itv->yuv_info.reg_2970 = read_reg(0x02970);
+
+       itv->yuv_info.v_filter_1 = -1;
+       itv->yuv_info.v_filter_2 = -1;
+       itv->yuv_info.h_filter = -1;
+
+       /* Set some valid size info */
+       itv->yuv_info.osd_x_offset = read_reg(0x02a04) & 0x00000FFF;
+       itv->yuv_info.osd_y_offset = (read_reg(0x02a04) >> 16) & 0x00000FFF;
+
+       /* Bit 2 of reg 2878 indicates current decoder output format
+          0 : NTSC    1 : PAL */
+       if (read_reg(0x2878) & 4)
+               itv->yuv_info.decode_height = 576;
+       else
+               itv->yuv_info.decode_height = 480;
+
+       /* If no visible size set, assume full size */
+       if (!itv->yuv_info.osd_vis_w) itv->yuv_info.osd_vis_w = 720 - itv->yuv_info.osd_x_offset;
+       if (!itv->yuv_info.osd_vis_h) itv->yuv_info.osd_vis_h = itv->yuv_info.decode_height - itv->yuv_info.osd_y_offset;
+
+       /* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */
+       itv->yuv_info.blanking_ptr = kzalloc(720*16,GFP_KERNEL);
+       if (itv->yuv_info.blanking_ptr) {
+               itv->yuv_info.blanking_dmaptr = pci_map_single(itv->dev, itv->yuv_info.blanking_ptr, 720*16, PCI_DMA_TODEVICE);
+       }
+       else {
+               itv->yuv_info.blanking_dmaptr = 0;
+               IVTV_DEBUG_WARN ("Failed to allocate yuv blanking buffer\n");
+       }
+
+       IVTV_DEBUG_WARN("Enable video output\n");
+       write_reg_sync(0x00108080, 0x2898);
+
+       /* Enable YUV decoder output */
+       write_reg_sync(0x01, IVTV_REG_VDM);
+
+       set_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
+       atomic_set(&itv->yuv_info.next_dma_frame,0);
+}
+
+int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+       DEFINE_WAIT(wait);
+       int rc = 0;
+       int got_sig = 0;
+       int frame, next_fill_frame, last_fill_frame;
+
+       IVTV_DEBUG_INFO("yuv_prep_frame\n");
+
+       if (atomic_read(&itv->yuv_info.next_dma_frame) == -1) ivtv_yuv_init(itv);
+
+       frame = atomic_read(&itv->yuv_info.next_fill_frame);
+       next_fill_frame = (frame + 1) & 0x3;
+       last_fill_frame = (atomic_read(&itv->yuv_info.next_dma_frame)+1) & 0x3;
+
+       if (next_fill_frame != last_fill_frame && last_fill_frame != frame) {
+               /* Buffers are full - Overwrite the last frame */
+               next_fill_frame = frame;
+               frame = (frame - 1) & 3;
+       }
+
+       /* Take a snapshot of the yuv coordinate information */
+       itv->yuv_info.new_frame_info[frame].src_x = args->src.left;
+       itv->yuv_info.new_frame_info[frame].src_y = args->src.top;
+       itv->yuv_info.new_frame_info[frame].src_w = args->src.width;
+       itv->yuv_info.new_frame_info[frame].src_h = args->src.height;
+       itv->yuv_info.new_frame_info[frame].dst_x = args->dst.left;
+       itv->yuv_info.new_frame_info[frame].dst_y = args->dst.top;
+       itv->yuv_info.new_frame_info[frame].dst_w = args->dst.width;
+       itv->yuv_info.new_frame_info[frame].dst_h = args->dst.height;
+       itv->yuv_info.new_frame_info[frame].tru_x = args->dst.left;
+       itv->yuv_info.new_frame_info[frame].tru_w = args->src_width;
+       itv->yuv_info.new_frame_info[frame].tru_h = args->src_height;
+
+       /* Are we going to offset the Y plane */
+       if (args->src.height + args->src.top < 512-16)
+               itv->yuv_info.new_frame_info[frame].offset_y = 1;
+       else
+               itv->yuv_info.new_frame_info[frame].offset_y = 0;
+
+       /* Snapshot the osd pan info */
+       itv->yuv_info.new_frame_info[frame].pan_x = itv->yuv_info.osd_x_pan;
+       itv->yuv_info.new_frame_info[frame].pan_y = itv->yuv_info.osd_y_pan;
+       itv->yuv_info.new_frame_info[frame].vis_w = itv->yuv_info.osd_vis_w;
+       itv->yuv_info.new_frame_info[frame].vis_h = itv->yuv_info.osd_vis_h;
+
+       itv->yuv_info.new_frame_info[frame].update = 0;
+       itv->yuv_info.new_frame_info[frame].interlaced_y = 0;
+       itv->yuv_info.new_frame_info[frame].interlaced_uv = 0;
+
+       if (memcmp (&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame],
+           sizeof (itv->yuv_info.new_frame_info[frame]))) {
+               memcpy(&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], sizeof (itv->yuv_info.old_frame_info_args));
+               itv->yuv_info.new_frame_info[frame].update = 1;
+/*             IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */
+       }
+
+       /* DMA the frame */
+       mutex_lock(&itv->udma.lock);
+
+       if ((rc = ivtv_yuv_prep_user_dma(itv, &itv->udma, args)) != 0) {
+               mutex_unlock(&itv->udma.lock);
+               return rc;
+       }
+
+       ivtv_udma_prepare(itv);
+       prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+       /* if no UDMA is pending and no UDMA is in progress, then the DMA
+       is finished */
+       while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
+               /* don't interrupt if the DMA is in progress but break off
+               a still pending DMA. */
+               got_sig = signal_pending(current);
+               if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
+                       break;
+               got_sig = 0;
+               schedule();
+       }
+       finish_wait(&itv->dma_waitq, &wait);
+
+       /* Unmap Last DMA Xfer */
+       ivtv_udma_unmap(itv);
+
+       if (got_sig) {
+               IVTV_DEBUG_INFO("User stopped YUV UDMA\n");
+               mutex_unlock(&itv->udma.lock);
+               return -EINTR;
+       }
+
+       atomic_set(&itv->yuv_info.next_fill_frame, next_fill_frame);
+
+       mutex_unlock(&itv->udma.lock);
+       return rc;
+}
+
+void ivtv_yuv_close(struct ivtv *itv)
+{
+       int h_filter, v_filter_1, v_filter_2;
+
+       IVTV_DEBUG_YUV("ivtv_yuv_close\n");
+       ivtv_waitq(&itv->vsync_waitq);
+
+       atomic_set(&itv->yuv_info.next_dma_frame, -1);
+       atomic_set(&itv->yuv_info.next_fill_frame, 0);
+
+       /* Reset registers we have changed so mpeg playback works */
+
+       /* If we fully restore this register, the display may remain active.
+          Restore, but set one bit to blank the video. Firmware will always
+          clear this bit when needed, so not a problem. */
+       write_reg(itv->yuv_info.reg_2898 | 0x01000000, 0x2898);
+
+       write_reg(itv->yuv_info.reg_2834, 0x02834);
+       write_reg(itv->yuv_info.reg_2838, 0x02838);
+       write_reg(itv->yuv_info.reg_283c, 0x0283c);
+       write_reg(itv->yuv_info.reg_2840, 0x02840);
+       write_reg(itv->yuv_info.reg_2844, 0x02844);
+       write_reg(itv->yuv_info.reg_2848, 0x02848);
+       write_reg(itv->yuv_info.reg_2854, 0x02854);
+       write_reg(itv->yuv_info.reg_285c, 0x0285c);
+       write_reg(itv->yuv_info.reg_2864, 0x02864);
+       write_reg(itv->yuv_info.reg_2870, 0x02870);
+       write_reg(itv->yuv_info.reg_2874, 0x02874);
+       write_reg(itv->yuv_info.reg_2890, 0x02890);
+       write_reg(itv->yuv_info.reg_289c, 0x0289c);
+
+       write_reg(itv->yuv_info.reg_2918, 0x02918);
+       write_reg(itv->yuv_info.reg_291c, 0x0291c);
+       write_reg(itv->yuv_info.reg_2920, 0x02920);
+       write_reg(itv->yuv_info.reg_2924, 0x02924);
+       write_reg(itv->yuv_info.reg_2928, 0x02928);
+       write_reg(itv->yuv_info.reg_292c, 0x0292c);
+       write_reg(itv->yuv_info.reg_2930, 0x02930);
+       write_reg(itv->yuv_info.reg_2934, 0x02934);
+       write_reg(itv->yuv_info.reg_2938, 0x02938);
+       write_reg(itv->yuv_info.reg_293c, 0x0293c);
+       write_reg(itv->yuv_info.reg_2940, 0x02940);
+       write_reg(itv->yuv_info.reg_2944, 0x02944);
+       write_reg(itv->yuv_info.reg_2948, 0x02948);
+       write_reg(itv->yuv_info.reg_294c, 0x0294c);
+       write_reg(itv->yuv_info.reg_2950, 0x02950);
+       write_reg(itv->yuv_info.reg_2954, 0x02954);
+       write_reg(itv->yuv_info.reg_2958, 0x02958);
+       write_reg(itv->yuv_info.reg_295c, 0x0295c);
+       write_reg(itv->yuv_info.reg_2960, 0x02960);
+       write_reg(itv->yuv_info.reg_2964, 0x02964);
+       write_reg(itv->yuv_info.reg_2968, 0x02968);
+       write_reg(itv->yuv_info.reg_296c, 0x0296c);
+       write_reg(itv->yuv_info.reg_2970, 0x02970);
+
+       /* Prepare to restore filters */
+
+       /* First the horizontal filter */
+       if ((itv->yuv_info.reg_2834 & 0x0000FFFF) == (itv->yuv_info.reg_2834 >> 16)) {
+               /* An exact size match uses filter 0 */
+               h_filter = 0;
+       }
+       else {
+               /* Figure out which filter to use */
+               h_filter = ((itv->yuv_info.reg_2834 << 16) / (itv->yuv_info.reg_2834 >> 16)) >> 15;
+               h_filter = (h_filter >> 1) + (h_filter & 1);
+               /* Only an exact size match can use filter 0. */
+               if (h_filter < 1) h_filter = 1;
+       }
+
+       /* Now the vertical filter */
+       if ((itv->yuv_info.reg_2918 & 0x0000FFFF) == (itv->yuv_info.reg_2918 >> 16)) {
+               /* An exact size match uses filter 0/1 */
+               v_filter_1 = 0;
+               v_filter_2 = 1;
+       }
+       else {
+               /* Figure out which filter to use */
+               v_filter_1 = ((itv->yuv_info.reg_2918 << 16) / (itv->yuv_info.reg_2918 >> 16)) >> 15;
+               v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
+               /* Only an exact size match can use filter 0 */
+               if (v_filter_1 == 0) v_filter_1 = 1;
+               v_filter_2 = v_filter_1;
+       }
+
+       /* Now restore the filters */
+       ivtv_yuv_filter (itv,h_filter,v_filter_1,v_filter_2);
+
+       /* and clear a few registers */
+       write_reg(0, 0x02814);
+       write_reg(0, 0x0282c);
+       write_reg(0, 0x02904);
+       write_reg(0, 0x02910);
+
+       /* Release the blanking buffer */
+       if (itv->yuv_info.blanking_ptr) {
+               kfree (itv->yuv_info.blanking_ptr);
+               itv->yuv_info.blanking_ptr = NULL;
+               pci_unmap_single(itv->dev, itv->yuv_info.blanking_dmaptr, 720*16, PCI_DMA_TODEVICE);
+       }
+
+       /* Invalidate the old dimension information */
+       itv->yuv_info.old_frame_info.src_w = 0;
+       itv->yuv_info.old_frame_info.src_h = 0;
+       itv->yuv_info.old_frame_info_args.src_w = 0;
+       itv->yuv_info.old_frame_info_args.src_h = 0;
+
+       /* All done. */
+       clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
+}
+
diff --git a/drivers/media/video/ivtv/ivtv-yuv.h b/drivers/media/video/ivtv/ivtv-yuv.h
new file mode 100644 (file)
index 0000000..3112873
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+    yuv support
+
+    Copyright (C) 2007  Ian Armstrong <ian@iarmst.demon.co.uk>
+
+    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_yuv_filter_check(struct ivtv *itv);
+int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args);
+void ivtv_yuv_close(struct ivtv *itv);
+void ivtv_yuv_work_handler (struct work_struct *work);
diff --git a/include/media/ivtv.h b/include/media/ivtv.h
new file mode 100644 (file)
index 0000000..412b48e
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+    Public ivtv API header
+    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
+    Copyright (C) 2004-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 _LINUX_IVTV_H
+#define _LINUX_IVTV_H
+
+/* ivtv knows several distinct output modes: MPEG streaming,
+   YUV streaming, YUV updates through user DMA and the passthrough
+   mode.
+
+   In order to clearly tell the driver that we are in user DMA
+   YUV mode you need to call IVTV_IOC_DMA_FRAME with y_source == NULL
+   first (althrough if you don't then the first time
+   DMA_FRAME is called the mode switch is done automatically).
+
+   When you close the file handle the user DMA mode is exited again.
+
+   While in one mode, you cannot use another mode (EBUSY is returned).
+
+   All this means that if you want to change the YUV interlacing
+   for the user DMA YUV mode you first need to do call IVTV_IOC_DMA_FRAME
+   with y_source == NULL before you can set the correct format using
+   VIDIOC_S_FMT.
+
+   Eventually all this should be replaced with a proper V4L2 API,
+   but for now we have to do it this way. */
+
+struct ivtv_dma_frame {
+       enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_OUTPUT */
+       __u32 pixelformat;       /* 0 == same as destination */
+       void __user *y_source;   /* if NULL and type == V4L2_BUF_TYPE_VIDEO_OUTPUT,
+                                   then just switch to user DMA YUV output mode */
+       void __user *uv_source;  /* Unused for RGB pixelformats */
+       struct v4l2_rect src;
+       struct v4l2_rect dst;
+       __u32 src_width;
+       __u32 src_height;
+};
+
+#define IVTV_IOC_DMA_FRAME  _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtv_dma_frame)
+
+/* These are the VBI types as they appear in the embedded VBI private packets. */
+#define IVTV_SLICED_TYPE_TELETEXT_B     (1)
+#define IVTV_SLICED_TYPE_CAPTION_525    (4)
+#define IVTV_SLICED_TYPE_WSS_625        (5)
+#define IVTV_SLICED_TYPE_VPS            (7)
+
+#endif /* _LINUX_IVTV_H */