Merge git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb

* git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb: (28 commits)
  V4L-DVB(7789a): cx18: fix symbol conflict with ivtv driver
  V4L/DVB (7789): tuner: remove static dependencies on analog tuner sub-modules
  V4L/DVB (7785): [2.6 patch] make mt9{m001,v022}_controls[] static
  V4L/DVB (7786): cx18: new driver for the Conexant CX23418 MPEG encoder chip
  V4L/DVB (7783): drivers/media/dvb/frontends/s5h1420.c: printk fix
  V4L/DVB (7782): pvrusb2: Driver is no longer experimental
  V4L/DVB (7781): pvrusb2-dvb: include dvb support by default and update Kconfig help text
  V4L/DVB (7780): pvrusb2: always enable support for OnAir Creator / HDTV USB2
  V4L/DVB (7779): pvrusb2-dvb: quiet down noise in kernel log for feed debug
  Rename common tuner Kconfig names to use the same
  Fix V4L/DVB core help messages
  V4L/DVB (7769): Move other terrestrial tuners to common/tuners
  V4L/DVB (7768): reorganize some DVB-S Kconfig items
  V4L/DVB(7767): Move tuners to common/tuners
  V4L/DVB (7766): saa7134: add another PCI ID for Beholder M6
  V4L/DVB (7765): Add support for Beholder BeholdTV H6
  V4L/DVB (7763): ivtv: add tuner support for the AverMedia M116
  V4L/DVB (7762): ivtv: fix tuner detection for PAL-N/Nc
  V4L/DVB (7761): ivtv: increase the DMA timeout from 100 to 300 ms
  V4L/DVB (7759): ivtv: increase version number to 1.2.1
  ...
diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134
index 44d84dd..67937df 100644
--- a/Documentation/video4linux/CARDLIST.saa7134
+++ b/Documentation/video4linux/CARDLIST.saa7134
@@ -128,7 +128,7 @@
 127 -> Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM [0000:5071,0000:507B,5ace:5070,5ace:5090]
 128 -> Beholder BeholdTV Columbus TVFM          [0000:5201]
 129 -> Beholder BeholdTV 607 / BeholdTV 609     [5ace:6070,5ace:6071,5ace:6072,5ace:6073,5ace:6090,5ace:6091,5ace:6092,5ace:6093]
-130 -> Beholder BeholdTV M6 / BeholdTV M6 Extra [5ace:6190,5ace:6193]
+130 -> Beholder BeholdTV M6 / BeholdTV M6 Extra [5ace:6190,5ace:6193,5ace:6191]
 131 -> Twinhan Hybrid DTV-DVB 3056 PCI          [1822:0022]
 132 -> Genius TVGO AM11MCE
 133 -> NXP Snake DVB-S reference design
@@ -140,3 +140,4 @@
 139 -> Compro VideoMate T750                    [185b:c900]
 140 -> Avermedia DVB-S Pro A700                 [1461:a7a1]
 141 -> Avermedia DVB-S Hybrid+FM A700           [1461:a7a2]
+142 -> Beholder BeholdTV H6                     [5ace:6290]
diff --git a/Documentation/video4linux/cx18.txt b/Documentation/video4linux/cx18.txt
new file mode 100644
index 0000000..077d56e
--- /dev/null
+++ b/Documentation/video4linux/cx18.txt
@@ -0,0 +1,34 @@
+Some notes regarding the cx18 driver for the Conexant CX23418 MPEG
+encoder chip:
+
+1) The only hardware currently supported is the Hauppauge HVR-1600.
+
+2) Some people have problems getting the i2c bus to work. Cause unknown.
+   The symptom is that the eeprom cannot be read and the card is
+   unusable.
+
+3) The audio from the analog tuner is mono only. Probably caused by
+   incorrect audio register information in the datasheet. We are
+   waiting for updated information from Conexant.
+
+4) VBI (raw or sliced) has not yet been implemented.
+
+5) MPEG indexing is not yet implemented.
+
+6) The driver is still a bit rough around the edges, this should
+   improve over time.
+
+
+Firmware:
+
+The firmware needs to be extracted from the Windows Hauppauge HVR-1600
+driver, available here:
+
+http://hauppauge.lightpath.net/software/install_cd/hauppauge_cd_3.4d1.zip
+
+Unzip, then copy the following files to the firmware directory
+and rename them as follows:
+
+Drivers/Driver18/hcw18apu.rom -> v4l-cx23418-apu.fw
+Drivers/Driver18/hcw18enc.rom -> v4l-cx23418-cpu.fw
+Drivers/Driver18/hcw18mlC.rom -> v4l-cx23418-dig.fw
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 128bb9c..ddf57e1 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -5,16 +5,20 @@
 menu "Multimedia devices"
 	depends on HAS_IOMEM
 
+comment "Multimedia core support"
+
+#
+# V4L core and enabled API's
+#
+
 config VIDEO_DEV
 	tristate "Video For Linux"
 	---help---
-	  Support for audio/video capture and overlay devices and FM radio
-	  cards. The exact capabilities of each device vary.
+	  V4L core support for video capture and overlay devices, webcams and
+	  AM/FM radio cards.
 
 	  This kernel includes support for the new Video for Linux Two API,
-	  (V4L2) as well as the original system. Drivers and applications
-	  need to be rewritten to use V4L2, but drivers for popular cards
-	  and applications for most video capture functions already exist.
+	  (V4L2).
 
 	  Additional info and docs are available on the web at
 	  <http://linuxtv.org>
@@ -36,8 +40,11 @@
 	default VIDEO_DEV && VIDEO_V4L2_COMMON
 	select VIDEO_V4L1_COMPAT
 	---help---
-	  Enables a compatibility API used by most V4L2 devices to allow
-	  its usage with legacy applications that supports only V4L1 api.
+	  Enables drivers based on the legacy V4L1 API.
+
+	  This api were developed to be used at Kernel 2.2 and 2.4, but
+	  lacks support for several video standards. There are several
+	  drivers at kernel that still depends on it.
 
 	  If you are unsure as to whether this is required, answer Y.
 
@@ -46,9 +53,8 @@
 	depends on VIDEO_DEV
 	default VIDEO_DEV
 	---help---
-	  This api were developed to be used at Kernel 2.2 and 2.4, but
-	  lacks support for several video standards. There are several
-	  drivers at kernel that still depends on it.
+	  Enables a compatibility API used by most V4L2 devices to allow
+	  its usage with legacy applications that supports only V4L1 api.
 
 	  Documentation for the original API is included in the file
 	  <Documentation/video4linux/API.html>.
@@ -58,136 +64,58 @@
 
 	  If you are unsure as to whether this is required, answer Y.
 
-config VIDEO_V4L2
-	tristate
-	depends on VIDEO_DEV && VIDEO_V4L2_COMMON
-	default VIDEO_DEV && VIDEO_V4L2_COMMON
+#
+# DVB Core
+#
 
-config VIDEO_V4L1
+config DVB_CORE
+	tristate "DVB for Linux"
+	depends on NET && INET
+	select CRC32
+	help
+	  DVB core utility functions for device handling, software fallbacks etc.
+
+	  Enable this if you own a DVB/ATSC adapter and want to use it or if
+	  you compile Linux for a digital SetTopBox.
+
+	  Say Y when you have a DVB or an ATSC card and want to use it.
+
+	  API specs and user tools are available from <http://www.linuxtv.org/>.
+
+	  Please report problems regarding this support to the LinuxDVB
+	  mailing list.
+
+	  If unsure say N.
+
+config VIDEO_MEDIA
 	tristate
-	depends on VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1
-	default VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1
+	default DVB_CORE || VIDEO_DEV
+	depends on DVB_CORE || VIDEO_DEV
+
+comment "Multimedia drivers"
+
+source "drivers/media/common/Kconfig"
+
+#
+# Tuner drivers for DVB and V4L
+#
+
+source "drivers/media/common/tuners/Kconfig"
+
+#
+# Video/Radio/Hybrid adapters
+#
 
 source "drivers/media/video/Kconfig"
 
 source "drivers/media/radio/Kconfig"
 
+#
+# DVB adapters
+#
+
 source "drivers/media/dvb/Kconfig"
 
-source "drivers/media/common/Kconfig"
-
-config VIDEO_TUNER
-	tristate
-	depends on I2C
-	select TUNER_XC2028 if !VIDEO_TUNER_CUSTOMIZE
-	select TUNER_MT20XX if !VIDEO_TUNER_CUSTOMIZE
-	select TUNER_TDA8290 if !VIDEO_TUNER_CUSTOMIZE
-	select TUNER_TEA5761 if !VIDEO_TUNER_CUSTOMIZE
-	select TUNER_TEA5767 if !VIDEO_TUNER_CUSTOMIZE
-	select TUNER_SIMPLE if !VIDEO_TUNER_CUSTOMIZE
-	select TUNER_TDA9887 if !VIDEO_TUNER_CUSTOMIZE
-
-menuconfig VIDEO_TUNER_CUSTOMIZE
-	bool "Customize analog tuner modules to build"
-	depends on VIDEO_TUNER
-	help
-	  This allows the user to deselect tuner drivers unnecessary
-	  for their hardware from the build. Use this option with care
-	  as deselecting tuner drivers which are in fact necessary will
-	  result in V4L devices which cannot be tuned due to lack of
-	  driver support
-
-	  If unsure say N.
-
-if VIDEO_TUNER_CUSTOMIZE
-
-config TUNER_XC2028
-	tristate "XCeive xc2028/xc3028 tuners"
-	depends on I2C && FW_LOADER
-	default m if VIDEO_TUNER_CUSTOMIZE
-	help
-	  Say Y here to include support for the xc2028/xc3028 tuners.
-
-config TUNER_MT20XX
-	tristate "Microtune 2032 / 2050 tuners"
-	depends on I2C
-	default m if VIDEO_TUNER_CUSTOMIZE
-	help
-	  Say Y here to include support for the MT2032 / MT2050 tuner.
-
-config TUNER_TDA8290
-	tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
-	depends on I2C
-	select DVB_TDA827X
-	select DVB_TDA18271
-	default m if VIDEO_TUNER_CUSTOMIZE
-	help
-	  Say Y here to include support for Philips TDA8290+8275(a) tuner.
-
-config TUNER_TEA5761
-	tristate "TEA 5761 radio tuner (EXPERIMENTAL)"
-	depends on I2C && EXPERIMENTAL
-	default m if VIDEO_TUNER_CUSTOMIZE
-	help
-	  Say Y here to include support for the Philips TEA5761 radio tuner.
-
-config TUNER_TEA5767
-	tristate "TEA 5767 radio tuner"
-	depends on I2C
-	default m if VIDEO_TUNER_CUSTOMIZE
-	help
-	  Say Y here to include support for the Philips TEA5767 radio tuner.
-
-config TUNER_SIMPLE
-	tristate "Simple tuner support"
-	depends on I2C
-	select TUNER_TDA9887
-	default m if VIDEO_TUNER_CUSTOMIZE
-	help
-	  Say Y here to include support for various simple tuners.
-
-config TUNER_TDA9887
-	tristate "TDA 9885/6/7 analog IF demodulator"
-	depends on I2C
-	default m if VIDEO_TUNER_CUSTOMIZE
-	help
-	  Say Y here to include support for Philips TDA9885/6/7
-	  analog IF demodulator.
-
-endif # VIDEO_TUNER_CUSTOMIZE
-
-config VIDEOBUF_GEN
-	tristate
-
-config VIDEOBUF_DMA_SG
-	depends on HAS_DMA
-	select VIDEOBUF_GEN
-	tristate
-
-config VIDEOBUF_VMALLOC
-	select VIDEOBUF_GEN
-	tristate
-
-config VIDEOBUF_DVB
-	tristate
-	select VIDEOBUF_GEN
-	select VIDEOBUF_DMA_SG
-
-config VIDEO_BTCX
-	tristate
-
-config VIDEO_IR_I2C
-	tristate
-
-config VIDEO_IR
-	tristate
-	depends on INPUT
-	select VIDEO_IR_I2C if I2C
-
-config VIDEO_TVEEPROM
-	tristate
-	depends on I2C
-
 config DAB
 	boolean "DAB adapters"
 	---help---
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index 7b8bb69..73f742c 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -2,10 +2,10 @@
 # Makefile for the kernel multimedia device drivers.
 #
 
-obj-y := common/
-obj-y += video/
+obj-$(CONFIG_VIDEO_MEDIA) += common/
+
+# Since hybrid devices are here, should be compiled if DVB and/or V4L
+obj-$(CONFIG_VIDEO_MEDIA) += video/
+
 obj-$(CONFIG_VIDEO_DEV) += radio/
 obj-$(CONFIG_DVB_CORE)  += dvb/
-ifeq ($(CONFIG_DVB_CORE),)
-  obj-$(CONFIG_VIDEO_TUNER)  += dvb/frontends/
-endif
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
new file mode 100644
index 0000000..7b379e1
--- /dev/null
+++ b/drivers/media/common/tuners/Kconfig
@@ -0,0 +1,146 @@
+config MEDIA_ATTACH
+	bool "Load and attach frontend driver modules as needed"
+	depends on DVB_CORE
+	depends on MODULES
+	help
+	  Remove the static dependency of DVB card drivers on all
+	  frontend modules for all possible card variants. Instead,
+	  allow the card drivers to only load the frontend modules
+	  they require. This saves several KBytes of memory.
+
+	  Note: You will need module-init-tools v3.2 or later for this feature.
+
+	  If unsure say Y.
+
+config MEDIA_TUNER
+	tristate
+	default DVB_CORE || VIDEO_DEV
+	depends on DVB_CORE || VIDEO_DEV
+	select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE
+	select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE
+	select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMIZE
+	select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMIZE
+	select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMIZE
+	select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMIZE
+	select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
+	select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMIZE
+
+menuconfig MEDIA_TUNER_CUSTOMIZE
+	bool "Customize analog and hybrid tuner modules to build"
+	depends on MEDIA_TUNER
+	help
+	  This allows the user to deselect tuner drivers unnecessary
+	  for their hardware from the build. Use this option with care
+	  as deselecting tuner drivers which are in fact necessary will
+	  result in V4L/DVB devices which cannot be tuned due to lack of
+	  driver support
+
+	  If unsure say N.
+
+if MEDIA_TUNER_CUSTOMIZE
+
+config MEDIA_TUNER_SIMPLE
+	tristate "Simple tuner support"
+	depends on I2C
+	select MEDIA_TUNER_TDA9887
+	default m if MEDIA_TUNER_CUSTOMIZE
+	help
+	  Say Y here to include support for various simple tuners.
+
+config MEDIA_TUNER_TDA8290
+	tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
+	depends on I2C
+	select MEDIA_TUNER_TDA827X
+	select MEDIA_TUNER_TDA18271
+	default m if MEDIA_TUNER_CUSTOMIZE
+	help
+	  Say Y here to include support for Philips TDA8290+8275(a) tuner.
+
+config MEDIA_TUNER_TDA827X
+	tristate "Philips TDA827X silicon tuner"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-T silicon tuner module. Say Y when you want to support this tuner.
+
+config MEDIA_TUNER_TDA18271
+	tristate "NXP TDA18271 silicon tuner"
+	depends on I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A silicon tuner module. Say Y when you want to support this tuner.
+
+config MEDIA_TUNER_TDA9887
+	tristate "TDA 9885/6/7 analog IF demodulator"
+	depends on I2C
+	default m if MEDIA_TUNER_CUSTOMIZE
+	help
+	  Say Y here to include support for Philips TDA9885/6/7
+	  analog IF demodulator.
+
+config MEDIA_TUNER_TEA5761
+	tristate "TEA 5761 radio tuner (EXPERIMENTAL)"
+	depends on I2C && EXPERIMENTAL
+	default m if MEDIA_TUNER_CUSTOMIZE
+	help
+	  Say Y here to include support for the Philips TEA5761 radio tuner.
+
+config MEDIA_TUNER_TEA5767
+	tristate "TEA 5767 radio tuner"
+	depends on I2C
+	default m if MEDIA_TUNER_CUSTOMIZE
+	help
+	  Say Y here to include support for the Philips TEA5767 radio tuner.
+
+config MEDIA_TUNER_MT20XX
+	tristate "Microtune 2032 / 2050 tuners"
+	depends on I2C
+	default m if MEDIA_TUNER_CUSTOMIZE
+	help
+	  Say Y here to include support for the MT2032 / MT2050 tuner.
+
+config MEDIA_TUNER_MT2060
+	tristate "Microtune MT2060 silicon IF tuner"
+	depends on I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A driver for the silicon IF tuner MT2060 from Microtune.
+
+config MEDIA_TUNER_MT2266
+	tristate "Microtune MT2266 silicon tuner"
+	depends on I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A driver for the silicon baseband tuner MT2266 from Microtune.
+
+config MEDIA_TUNER_MT2131
+	tristate "Microtune MT2131 silicon tuner"
+	depends on I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A driver for the silicon baseband tuner MT2131 from Microtune.
+
+config MEDIA_TUNER_QT1010
+	tristate "Quantek QT1010 silicon tuner"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A driver for the silicon tuner QT1010 from Quantek.
+
+config MEDIA_TUNER_XC2028
+	tristate "XCeive xc2028/xc3028 tuners"
+	depends on I2C && FW_LOADER
+	default m if MEDIA_TUNER_CUSTOMIZE
+	help
+	  Say Y here to include support for the xc2028/xc3028 tuners.
+
+config MEDIA_TUNER_XC5000
+	tristate "Xceive XC5000 silicon tuner"
+	depends on I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A driver for the silicon tuner XC5000 from Xceive.
+	  This device is only used inside a SiP called togther with a
+	  demodulator for now.
+
+endif # MEDIA_TUNER_CUSTOMIZE
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
new file mode 100644
index 0000000..236d993
--- /dev/null
+++ b/drivers/media/common/tuners/Makefile
@@ -0,0 +1,25 @@
+#
+# Makefile for common V4L/DVB tuners
+#
+
+tda18271-objs := tda18271-maps.o tda18271-common.o tda18271-fe.o
+
+obj-$(CONFIG_MEDIA_TUNER_XC2028) += tuner-xc2028.o
+obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-simple.o
+# tuner-types will be merged into tuner-simple, in the future
+obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-types.o
+obj-$(CONFIG_MEDIA_TUNER_MT20XX) += mt20xx.o
+obj-$(CONFIG_MEDIA_TUNER_TDA8290) += tda8290.o
+obj-$(CONFIG_MEDIA_TUNER_TEA5767) += tea5767.o
+obj-$(CONFIG_MEDIA_TUNER_TEA5761) += tea5761.o
+obj-$(CONFIG_MEDIA_TUNER_TDA9887) += tda9887.o
+obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o
+obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o
+obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o
+obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o
+obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o
+obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o
+obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/dvb/frontends/mt2060.c b/drivers/media/common/tuners/mt2060.c
similarity index 100%
rename from drivers/media/dvb/frontends/mt2060.c
rename to drivers/media/common/tuners/mt2060.c
diff --git a/drivers/media/dvb/frontends/mt2060.h b/drivers/media/common/tuners/mt2060.h
similarity index 90%
rename from drivers/media/dvb/frontends/mt2060.h
rename to drivers/media/common/tuners/mt2060.h
index acba005..cb60caf 100644
--- a/drivers/media/dvb/frontends/mt2060.h
+++ b/drivers/media/common/tuners/mt2060.h
@@ -30,7 +30,7 @@
 	u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */
 };
 
-#if defined(CONFIG_DVB_TUNER_MT2060) || (defined(CONFIG_DVB_TUNER_MT2060_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_MT2060) || (defined(CONFIG_MEDIA_TUNER_MT2060_MODULE) && defined(MODULE))
 extern struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1);
 #else
 static inline struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1)
@@ -38,6 +38,6 @@
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;
 }
-#endif // CONFIG_DVB_TUNER_MT2060
+#endif // CONFIG_MEDIA_TUNER_MT2060
 
 #endif
diff --git a/drivers/media/dvb/frontends/mt2060_priv.h b/drivers/media/common/tuners/mt2060_priv.h
similarity index 100%
rename from drivers/media/dvb/frontends/mt2060_priv.h
rename to drivers/media/common/tuners/mt2060_priv.h
diff --git a/drivers/media/video/mt20xx.c b/drivers/media/common/tuners/mt20xx.c
similarity index 100%
rename from drivers/media/video/mt20xx.c
rename to drivers/media/common/tuners/mt20xx.c
diff --git a/drivers/media/video/mt20xx.h b/drivers/media/common/tuners/mt20xx.h
similarity index 91%
rename from drivers/media/video/mt20xx.h
rename to drivers/media/common/tuners/mt20xx.h
index aa848e1..259553a 100644
--- a/drivers/media/video/mt20xx.h
+++ b/drivers/media/common/tuners/mt20xx.h
@@ -20,7 +20,7 @@
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
-#if defined(CONFIG_TUNER_MT20XX) || (defined(CONFIG_TUNER_MT20XX_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_MT20XX) || (defined(CONFIG_MEDIA_TUNER_MT20XX_MODULE) && defined(MODULE))
 extern struct dvb_frontend *microtune_attach(struct dvb_frontend *fe,
 					     struct i2c_adapter* i2c_adap,
 					     u8 i2c_addr);
diff --git a/drivers/media/dvb/frontends/mt2131.c b/drivers/media/common/tuners/mt2131.c
similarity index 100%
rename from drivers/media/dvb/frontends/mt2131.c
rename to drivers/media/common/tuners/mt2131.c
diff --git a/drivers/media/dvb/frontends/mt2131.h b/drivers/media/common/tuners/mt2131.h
similarity index 91%
rename from drivers/media/dvb/frontends/mt2131.h
rename to drivers/media/common/tuners/mt2131.h
index 606d857..cd8376f 100644
--- a/drivers/media/dvb/frontends/mt2131.h
+++ b/drivers/media/common/tuners/mt2131.h
@@ -30,7 +30,7 @@
 	u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */
 };
 
-#if defined(CONFIG_DVB_TUNER_MT2131) || (defined(CONFIG_DVB_TUNER_MT2131_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_MT2131) || (defined(CONFIG_MEDIA_TUNER_MT2131_MODULE) && defined(MODULE))
 extern struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe,
 					  struct i2c_adapter *i2c,
 					  struct mt2131_config *cfg,
@@ -44,7 +44,7 @@
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;
 }
-#endif /* CONFIG_DVB_TUNER_MT2131 */
+#endif /* CONFIG_MEDIA_TUNER_MT2131 */
 
 #endif /* __MT2131_H__ */
 
diff --git a/drivers/media/dvb/frontends/mt2131_priv.h b/drivers/media/common/tuners/mt2131_priv.h
similarity index 100%
rename from drivers/media/dvb/frontends/mt2131_priv.h
rename to drivers/media/common/tuners/mt2131_priv.h
diff --git a/drivers/media/dvb/frontends/mt2266.c b/drivers/media/common/tuners/mt2266.c
similarity index 100%
rename from drivers/media/dvb/frontends/mt2266.c
rename to drivers/media/common/tuners/mt2266.c
diff --git a/drivers/media/dvb/frontends/mt2266.h b/drivers/media/common/tuners/mt2266.h
similarity index 88%
rename from drivers/media/dvb/frontends/mt2266.h
rename to drivers/media/common/tuners/mt2266.h
index c5113ef..4d08388 100644
--- a/drivers/media/dvb/frontends/mt2266.h
+++ b/drivers/media/common/tuners/mt2266.h
@@ -24,7 +24,7 @@
 	u8 i2c_address;
 };
 
-#if defined(CONFIG_DVB_TUNER_MT2266) || (defined(CONFIG_DVB_TUNER_MT2266_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_MT2266) || (defined(CONFIG_MEDIA_TUNER_MT2266_MODULE) && defined(MODULE))
 extern struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg);
 #else
 static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg)
@@ -32,6 +32,6 @@
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;
 }
-#endif // CONFIG_DVB_TUNER_MT2266
+#endif // CONFIG_MEDIA_TUNER_MT2266
 
 #endif
diff --git a/drivers/media/dvb/frontends/qt1010.c b/drivers/media/common/tuners/qt1010.c
similarity index 100%
rename from drivers/media/dvb/frontends/qt1010.c
rename to drivers/media/common/tuners/qt1010.c
diff --git a/drivers/media/dvb/frontends/qt1010.h b/drivers/media/common/tuners/qt1010.h
similarity index 91%
rename from drivers/media/dvb/frontends/qt1010.h
rename to drivers/media/common/tuners/qt1010.h
index cff6a7c..807fb7b 100644
--- a/drivers/media/dvb/frontends/qt1010.h
+++ b/drivers/media/common/tuners/qt1010.h
@@ -36,7 +36,7 @@
  * @param cfg  tuner hw based configuration
  * @return fe  pointer on success, NULL on failure
  */
-#if defined(CONFIG_DVB_TUNER_QT1010) || (defined(CONFIG_DVB_TUNER_QT1010_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_QT1010) || (defined(CONFIG_MEDIA_TUNER_QT1010_MODULE) && defined(MODULE))
 extern struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe,
 					  struct i2c_adapter *i2c,
 					  struct qt1010_config *cfg);
@@ -48,6 +48,6 @@
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;
 }
-#endif // CONFIG_DVB_TUNER_QT1010
+#endif // CONFIG_MEDIA_TUNER_QT1010
 
 #endif
diff --git a/drivers/media/dvb/frontends/qt1010_priv.h b/drivers/media/common/tuners/qt1010_priv.h
similarity index 100%
rename from drivers/media/dvb/frontends/qt1010_priv.h
rename to drivers/media/common/tuners/qt1010_priv.h
diff --git a/drivers/media/dvb/frontends/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c
similarity index 100%
rename from drivers/media/dvb/frontends/tda18271-common.c
rename to drivers/media/common/tuners/tda18271-common.c
diff --git a/drivers/media/dvb/frontends/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c
similarity index 100%
rename from drivers/media/dvb/frontends/tda18271-fe.c
rename to drivers/media/common/tuners/tda18271-fe.c
diff --git a/drivers/media/dvb/frontends/tda18271-tables.c b/drivers/media/common/tuners/tda18271-maps.c
similarity index 100%
rename from drivers/media/dvb/frontends/tda18271-tables.c
rename to drivers/media/common/tuners/tda18271-maps.c
diff --git a/drivers/media/dvb/frontends/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h
similarity index 100%
rename from drivers/media/dvb/frontends/tda18271-priv.h
rename to drivers/media/common/tuners/tda18271-priv.h
diff --git a/drivers/media/dvb/frontends/tda18271.h b/drivers/media/common/tuners/tda18271.h
similarity index 95%
rename from drivers/media/dvb/frontends/tda18271.h
rename to drivers/media/common/tuners/tda18271.h
index 0e7af8d05..7db9831 100644
--- a/drivers/media/dvb/frontends/tda18271.h
+++ b/drivers/media/common/tuners/tda18271.h
@@ -81,7 +81,7 @@
 	unsigned int small_i2c:1;
 };
 
-#if defined(CONFIG_DVB_TDA18271) || (defined(CONFIG_DVB_TDA18271_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_TDA18271) || (defined(CONFIG_MEDIA_TUNER_TDA18271_MODULE) && defined(MODULE))
 extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
 					    struct i2c_adapter *i2c,
 					    struct tda18271_config *cfg);
diff --git a/drivers/media/dvb/frontends/tda827x.c b/drivers/media/common/tuners/tda827x.c
similarity index 100%
rename from drivers/media/dvb/frontends/tda827x.c
rename to drivers/media/common/tuners/tda827x.c
diff --git a/drivers/media/dvb/frontends/tda827x.h b/drivers/media/common/tuners/tda827x.h
similarity index 92%
rename from drivers/media/dvb/frontends/tda827x.h
rename to drivers/media/common/tuners/tda827x.h
index b73c235..7850a9a 100644
--- a/drivers/media/dvb/frontends/tda827x.h
+++ b/drivers/media/common/tuners/tda827x.h
@@ -51,7 +51,7 @@
  * @param cfg optional callback function pointers.
  * @return FE pointer on success, NULL on failure.
  */
-#if defined(CONFIG_DVB_TDA827X) || (defined(CONFIG_DVB_TDA827X_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_TDA827X) || (defined(CONFIG_MEDIA_TUNER_TDA827X_MODULE) && defined(MODULE))
 extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr,
 					   struct i2c_adapter *i2c,
 					   struct tda827x_config *cfg);
@@ -64,6 +64,6 @@
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;
 }
-#endif // CONFIG_DVB_TDA827X
+#endif // CONFIG_MEDIA_TUNER_TDA827X
 
 #endif // __DVB_TDA827X_H__
diff --git a/drivers/media/video/tda8290.c b/drivers/media/common/tuners/tda8290.c
similarity index 98%
rename from drivers/media/video/tda8290.c
rename to drivers/media/common/tuners/tda8290.c
index 0ebb5b5..91204d3 100644
--- a/drivers/media/video/tda8290.c
+++ b/drivers/media/common/tuners/tda8290.c
@@ -578,16 +578,16 @@
 
 	if ((data == 0x83) || (data == 0x84)) {
 		priv->ver |= TDA18271;
-		tda18271_attach(fe, priv->tda827x_addr,
-				priv->i2c_props.adap,
-				&tda829x_tda18271_config);
+		dvb_attach(tda18271_attach, fe, priv->tda827x_addr,
+			   priv->i2c_props.adap, &tda829x_tda18271_config);
 	} else {
 		if ((data & 0x3c) == 0)
 			priv->ver |= TDA8275;
 		else
 			priv->ver |= TDA8275A;
 
-		tda827x_attach(fe, priv->tda827x_addr, priv->i2c_props.adap, &priv->cfg);
+		dvb_attach(tda827x_attach, fe, priv->tda827x_addr,
+			   priv->i2c_props.adap, &priv->cfg);
 		priv->cfg.switch_addr = priv->i2c_props.addr;
 	}
 	if (fe->ops.tuner_ops.init)
diff --git a/drivers/media/video/tda8290.h b/drivers/media/common/tuners/tda8290.h
similarity index 93%
rename from drivers/media/video/tda8290.h
rename to drivers/media/common/tuners/tda8290.h
index d3bbf27..aa074f3 100644
--- a/drivers/media/video/tda8290.h
+++ b/drivers/media/common/tuners/tda8290.h
@@ -29,7 +29,7 @@
 #define TDA829X_DONT_PROBE  1
 };
 
-#if defined(CONFIG_TUNER_TDA8290) || (defined(CONFIG_TUNER_TDA8290_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_TDA8290) || (defined(CONFIG_MEDIA_TUNER_TDA8290_MODULE) && defined(MODULE))
 extern int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr);
 
 extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/video/tda9887.c b/drivers/media/common/tuners/tda9887.c
similarity index 100%
rename from drivers/media/video/tda9887.c
rename to drivers/media/common/tuners/tda9887.c
diff --git a/drivers/media/video/tda9887.h b/drivers/media/common/tuners/tda9887.h
similarity index 91%
rename from drivers/media/video/tda9887.h
rename to drivers/media/common/tuners/tda9887.h
index be49dcb..acc419e 100644
--- a/drivers/media/video/tda9887.h
+++ b/drivers/media/common/tuners/tda9887.h
@@ -21,7 +21,7 @@
 #include "dvb_frontend.h"
 
 /* ------------------------------------------------------------------------ */
-#if defined(CONFIG_TUNER_TDA9887) || (defined(CONFIG_TUNER_TDA9887_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_TDA9887) || (defined(CONFIG_MEDIA_TUNER_TDA9887_MODULE) && defined(MODULE))
 extern struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
 					   struct i2c_adapter *i2c_adap,
 					   u8 i2c_addr);
diff --git a/drivers/media/video/tea5761.c b/drivers/media/common/tuners/tea5761.c
similarity index 100%
rename from drivers/media/video/tea5761.c
rename to drivers/media/common/tuners/tea5761.c
diff --git a/drivers/media/video/tea5761.h b/drivers/media/common/tuners/tea5761.h
similarity index 92%
rename from drivers/media/video/tea5761.h
rename to drivers/media/common/tuners/tea5761.h
index 8eb6272..2e2ff82 100644
--- a/drivers/media/video/tea5761.h
+++ b/drivers/media/common/tuners/tea5761.h
@@ -20,7 +20,7 @@
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
-#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE))
 extern int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
 
 extern struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/video/tea5767.c b/drivers/media/common/tuners/tea5767.c
similarity index 100%
rename from drivers/media/video/tea5767.c
rename to drivers/media/common/tuners/tea5767.c
diff --git a/drivers/media/video/tea5767.h b/drivers/media/common/tuners/tea5767.h
similarity index 94%
rename from drivers/media/video/tea5767.h
rename to drivers/media/common/tuners/tea5767.h
index 7b547c0..d30ab1b 100644
--- a/drivers/media/video/tea5767.h
+++ b/drivers/media/common/tuners/tea5767.h
@@ -39,7 +39,7 @@
 	enum tea5767_xtal	xtal_freq;
 };
 
-#if defined(CONFIG_TUNER_TEA5767) || (defined(CONFIG_TUNER_TEA5767_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_TEA5767) || (defined(CONFIG_MEDIA_TUNER_TEA5767_MODULE) && defined(MODULE))
 extern int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
 
 extern struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/video/tuner-i2c.h b/drivers/media/common/tuners/tuner-i2c.h
similarity index 100%
rename from drivers/media/video/tuner-i2c.h
rename to drivers/media/common/tuners/tuner-i2c.h
diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/common/tuners/tuner-simple.c
similarity index 100%
rename from drivers/media/video/tuner-simple.c
rename to drivers/media/common/tuners/tuner-simple.c
diff --git a/drivers/media/video/tuner-simple.h b/drivers/media/common/tuners/tuner-simple.h
similarity index 92%
rename from drivers/media/video/tuner-simple.h
rename to drivers/media/common/tuners/tuner-simple.h
index e46cf01..381fa5d 100644
--- a/drivers/media/video/tuner-simple.h
+++ b/drivers/media/common/tuners/tuner-simple.h
@@ -20,7 +20,7 @@
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
-#if defined(CONFIG_TUNER_SIMPLE) || (defined(CONFIG_TUNER_SIMPLE_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_SIMPLE) || (defined(CONFIG_MEDIA_TUNER_SIMPLE_MODULE) && defined(MODULE))
 extern struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
 						struct i2c_adapter *i2c_adap,
 						u8 i2c_addr,
diff --git a/drivers/media/video/tuner-types.c b/drivers/media/common/tuners/tuner-types.c
similarity index 100%
rename from drivers/media/video/tuner-types.c
rename to drivers/media/common/tuners/tuner-types.c
diff --git a/drivers/media/video/tuner-xc2028-types.h b/drivers/media/common/tuners/tuner-xc2028-types.h
similarity index 100%
rename from drivers/media/video/tuner-xc2028-types.h
rename to drivers/media/common/tuners/tuner-xc2028-types.h
diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
similarity index 100%
rename from drivers/media/video/tuner-xc2028.c
rename to drivers/media/common/tuners/tuner-xc2028.c
diff --git a/drivers/media/video/tuner-xc2028.h b/drivers/media/common/tuners/tuner-xc2028.h
similarity index 93%
rename from drivers/media/video/tuner-xc2028.h
rename to drivers/media/common/tuners/tuner-xc2028.h
index fc2f132..216025c 100644
--- a/drivers/media/video/tuner-xc2028.h
+++ b/drivers/media/common/tuners/tuner-xc2028.h
@@ -47,7 +47,7 @@
 #define XC2028_TUNER_RESET	0
 #define XC2028_RESET_CLK	1
 
-#if defined(CONFIG_TUNER_XC2028) || (defined(CONFIG_TUNER_XC2028_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_XC2028) || (defined(CONFIG_MEDIA_TUNER_XC2028_MODULE) && defined(MODULE))
 extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
 					  struct xc2028_config *cfg);
 #else
diff --git a/drivers/media/dvb/frontends/xc5000.c b/drivers/media/common/tuners/xc5000.c
similarity index 100%
rename from drivers/media/dvb/frontends/xc5000.c
rename to drivers/media/common/tuners/xc5000.c
diff --git a/drivers/media/dvb/frontends/xc5000.h b/drivers/media/common/tuners/xc5000.h
similarity index 92%
rename from drivers/media/dvb/frontends/xc5000.h
rename to drivers/media/common/tuners/xc5000.h
index b890883..0ee80f9 100644
--- a/drivers/media/dvb/frontends/xc5000.h
+++ b/drivers/media/common/tuners/xc5000.h
@@ -45,8 +45,8 @@
 /* xc5000 callback command */
 #define XC5000_TUNER_RESET		0
 
-#if defined(CONFIG_DVB_TUNER_XC5000) || \
-    (defined(CONFIG_DVB_TUNER_XC5000_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_XC5000) || \
+    (defined(CONFIG_MEDIA_TUNER_XC5000_MODULE) && defined(MODULE))
 extern struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe,
 					  struct i2c_adapter *i2c,
 					  struct xc5000_config *cfg);
@@ -58,6 +58,6 @@
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;
 }
-#endif // CONFIG_DVB_TUNER_XC5000
+#endif // CONFIG_MEDIA_TUNER_XC5000
 
 #endif // __XC5000_H__
diff --git a/drivers/media/dvb/frontends/xc5000_priv.h b/drivers/media/common/tuners/xc5000_priv.h
similarity index 100%
rename from drivers/media/dvb/frontends/xc5000_priv.h
rename to drivers/media/common/tuners/xc5000_priv.h
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index 03ef88a..7b21b49 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -1,9 +1,7 @@
 #
-# Multimedia device configuration
+# DVB device configuration
 #
 
-source "drivers/media/dvb/dvb-core/Kconfig"
-
 menuconfig DVB_CAPTURE_DRIVERS
 	bool "DVB/ATSC adapters"
 	depends on DVB_CORE
diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig
index 6ec5afb..73dc2ee 100644
--- a/drivers/media/dvb/b2c2/Kconfig
+++ b/drivers/media/dvb/b2c2/Kconfig
@@ -9,7 +9,7 @@
 	select DVB_STV0297 if !DVB_FE_CUSTOMISE
 	select DVB_BCM3510 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
-	select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
 	select DVB_S5H1420 if !DVB_FE_CUSTOMISE
 	select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE
 	select DVB_ISL6421 if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/dvb/b2c2/Makefile b/drivers/media/dvb/b2c2/Makefile
index 870e284..d9db066 100644
--- a/drivers/media/dvb/b2c2/Makefile
+++ b/drivers/media/dvb/b2c2/Makefile
@@ -14,4 +14,4 @@
 obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o
 
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
-EXTRA_CFLAGS += -Idrivers/media/video/
+EXTRA_CFLAGS += -Idrivers/media/common/tuners/
diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig
index 902c762..d1239b8 100644
--- a/drivers/media/dvb/bt8xx/Kconfig
+++ b/drivers/media/dvb/bt8xx/Kconfig
@@ -8,7 +8,7 @@
 	select DVB_OR51211 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
 	select DVB_ZL10353 if !DVB_FE_CUSTOMISE
-	select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
 	select FW_LOADER
 	help
 	  Support for PCI cards based on the Bt8xx PCI bridge. Examples are
diff --git a/drivers/media/dvb/bt8xx/Makefile b/drivers/media/dvb/bt8xx/Makefile
index 9d3e68b..d98f1d4 100644
--- a/drivers/media/dvb/bt8xx/Makefile
+++ b/drivers/media/dvb/bt8xx/Makefile
@@ -3,4 +3,4 @@
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
 EXTRA_CFLAGS += -Idrivers/media/video/bt8xx
-EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c
index 75711bd..a763756 100644
--- a/drivers/media/dvb/bt8xx/dst.c
+++ b/drivers/media/dvb/bt8xx/dst.c
@@ -1714,7 +1714,7 @@
 	struct dst_state *state = fe->demodulator_priv;
 	if (state->dst_ca) {
 		dvb_unregister_device(state->dst_ca);
-#ifdef CONFIG_DVB_CORE_ATTACH
+#ifdef CONFIG_MEDIA_ATTACH
 		symbol_put(dst_ca_attach);
 #endif
 	}
diff --git a/drivers/media/dvb/dvb-core/Kconfig b/drivers/media/dvb/dvb-core/Kconfig
deleted file mode 100644
index e3e6839..0000000
--- a/drivers/media/dvb/dvb-core/Kconfig
+++ /dev/null
@@ -1,34 +0,0 @@
-config DVB_CORE
-	tristate "DVB for Linux"
-	depends on NET && INET
-	select CRC32
-	help
-	  Support Digital Video Broadcasting hardware.  Enable this if you
-	  own a DVB adapter and want to use it or if you compile Linux for
-	  a digital SetTopBox.
-
-	  DVB core utility functions for device handling, software fallbacks etc.
-	  Say Y when you have a DVB card and want to use it. Say Y if your want
-	  to build your drivers outside the kernel, but need the DVB core. All
-	  in-kernel drivers will select this automatically if needed.
-
-	  API specs and user tools are available from <http://www.linuxtv.org/>.
-
-	  Please report problems regarding this driver to the LinuxDVB
-	  mailing list.
-
-	  If unsure say N.
-
-config DVB_CORE_ATTACH
-	bool "Load and attach frontend modules as needed"
-	depends on DVB_CORE
-	depends on MODULES
-	help
-	  Remove the static dependency of DVB card drivers on all
-	  frontend modules for all possible card variants. Instead,
-	  allow the card drivers to only load the frontend modules
-	  they require. This saves several KBytes of memory.
-
-	  Note: You will need module-init-tools v3.2 or later for this feature.
-
-	  If unsure say Y.
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 2dddd08..8cbdb21 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -1189,7 +1189,7 @@
 }
 EXPORT_SYMBOL(dvb_unregister_frontend);
 
-#ifdef CONFIG_DVB_CORE_ATTACH
+#ifdef CONFIG_MEDIA_ATTACH
 void dvb_frontend_detach(struct dvb_frontend* fe)
 {
 	void *ptr;
diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h
index 5f9a737..89d12dc 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.h
+++ b/drivers/media/dvb/dvb-core/dvbdev.h
@@ -115,7 +115,7 @@
 			    unsigned int cmd, void *arg));
 
 /** generic DVB attach function. */
-#ifdef CONFIG_DVB_CORE_ATTACH
+#ifdef CONFIG_MEDIA_ATTACH
 #define dvb_attach(FUNCTION, ARGS...) ({ \
 	void *__r = NULL; \
 	typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 3c8493d..4c1cff9 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -25,7 +25,7 @@
 	tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)"
 	depends on DVB_USB
 	select DVB_DIB3000MC
-	select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
 	select DVB_PLL if !DVB_FE_CUSTOMISE
 	help
 	  Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver.
@@ -35,7 +35,7 @@
 	depends on DVB_USB
 	select DVB_PLL if !DVB_FE_CUSTOMISE
 	select DVB_DIB3000MB
-	select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
 	help
 	  Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by
 	  DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-B demodulator.
@@ -56,7 +56,7 @@
 	tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)"
 	depends on DVB_USB
 	select DVB_DIB3000MC
-	select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
 	help
 	  Support for USB2.0 DVB-T receivers based on reference designs made by
 	  DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-C/P demodulator.
@@ -73,8 +73,8 @@
 	select DVB_DIB7000P
 	select DVB_DIB7000M
 	select DVB_DIB3000MC
-	select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
-	select DVB_TUNER_MT2266 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MT2266 if !DVB_FE_CUSTOMISE
 	select DVB_TUNER_DIB0070
 	help
 	  Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
@@ -93,7 +93,7 @@
 	depends on DVB_USB
 	select DVB_PLL if !DVB_FE_CUSTOMISE
 	select DVB_DIB3000MC
-	select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
 	help
 	  Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver.
 
@@ -105,7 +105,7 @@
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
 	select DVB_MT352 if !DVB_FE_CUSTOMISE
 	select DVB_ZL10353 if !DVB_FE_CUSTOMISE
-	select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
 	help
 	  Say Y here to support the Conexant USB2.0 hybrid reference design.
 	  Currently, only DVB and ATSC modes are supported, analog mode
@@ -118,7 +118,7 @@
 	tristate "Uli m920x DVB-T USB2.0 support"
 	depends on DVB_USB
 	select DVB_MT352 if !DVB_FE_CUSTOMISE
-	select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
 	help
 	  Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver.
 	  Currently, only devices with a product id of
@@ -129,7 +129,7 @@
 	tristate "Genesys Logic GL861 USB2.0 support"
 	depends on DVB_USB
 	select DVB_ZL10353 if !DVB_FE_CUSTOMISE
-	select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
 	help
 	  Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0
 	  receiver with USB ID 0db0:5581.
@@ -138,7 +138,7 @@
 	tristate "Alcor Micro AU6610 USB2.0 support"
 	depends on DVB_USB
 	select DVB_ZL10353 if !DVB_FE_CUSTOMISE
-	select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
 	help
 	  Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver.
 
@@ -190,7 +190,7 @@
 	tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support"
 	depends on DVB_USB
 	select DVB_DIB3000MC
-	select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
 	select DVB_PLL if !DVB_FE_CUSTOMISE
 	help
 	  Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver.
@@ -227,8 +227,8 @@
 config DVB_USB_AF9005
 	tristate "Afatech AF9005 DVB-T USB1.1 support"
 	depends on DVB_USB && EXPERIMENTAL
-	select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
-	select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
 	help
 	  Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver
 	  and the TerraTec Cinergy T USB XE (Rev.1)
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 60a9100..c6511a6 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -63,5 +63,5 @@
 
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
 # due to tuner-xc3028
-EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
 
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index f5fceb3..6d23846 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -15,13 +15,6 @@
 comment "DVB-S (satellite) frontends"
 	depends on DVB_CORE
 
-config DVB_STV0299
-	tristate "ST STV0299 based"
-	depends on DVB_CORE && I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A DVB-S tuner module. Say Y when you want to support this frontend.
-
 config DVB_CX24110
 	tristate "Conexant CX24110 based"
 	depends on DVB_CORE && I2C
@@ -36,13 +29,6 @@
 	help
 	  A DVB-S tuner module. Say Y when you want to support this frontend.
 
-config DVB_TDA8083
-	tristate "Philips TDA8083 based"
-	depends on DVB_CORE && I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A DVB-S tuner module. Say Y when you want to support this frontend.
-
 config DVB_MT312
 	tristate "Zarlink VP310/MT312 based"
 	depends on DVB_CORE && I2C
@@ -50,13 +36,6 @@
 	help
 	  A DVB-S tuner module. Say Y when you want to support this frontend.
 
-config DVB_VES1X93
-	tristate "VLSI VES1893 or VES1993 based"
-	depends on DVB_CORE && I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A DVB-S tuner module. Say Y when you want to support this frontend.
-
 config DVB_S5H1420
 	tristate "Samsung S5H1420 based"
 	depends on DVB_CORE && I2C
@@ -64,6 +43,20 @@
 	help
 	  A DVB-S tuner module. Say Y when you want to support this frontend.
 
+config DVB_STV0299
+	tristate "ST STV0299 based"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA8083
+	tristate "Philips TDA8083 based"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
 config DVB_TDA10086
 	tristate "Philips TDA10086 based"
 	depends on DVB_CORE && I2C
@@ -71,6 +64,34 @@
 	help
 	  A DVB-S tuner module. Say Y when you want to support this frontend.
 
+config DVB_VES1X93
+	tristate "VLSI VES1893 or VES1993 based"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TUNER_ITD1000
+	tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA826X
+	tristate "Philips TDA826X silicon tuner"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S silicon tuner module. Say Y when you want to support this tuner.
+
+config DVB_TUA6100
+	tristate "Infineon TUA6100 PLL"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S PLL chip.
+
 comment "DVB-T (terrestrial) frontends"
 	depends on DVB_CORE
 
@@ -315,7 +336,7 @@
 	  An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
 	  to support this frontend.
 
-comment "Tuners/PLL support"
+comment "Digital terrestrial only tuners/PLL"
 	depends on DVB_CORE
 
 config DVB_PLL
@@ -326,55 +347,6 @@
 	  This module drives a number of tuners based on PLL chips with a
 	  common I2C interface. Say Y when you want to support these tuners.
 
-config DVB_TDA826X
-	tristate "Philips TDA826X silicon tuner"
-	depends on DVB_CORE && I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A DVB-S silicon tuner module. Say Y when you want to support this tuner.
-
-config DVB_TDA827X
-	tristate "Philips TDA827X silicon tuner"
-	depends on DVB_CORE && I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A DVB-T silicon tuner module. Say Y when you want to support this tuner.
-
-config DVB_TDA18271
-	tristate "NXP TDA18271 silicon tuner"
-	depends on I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A silicon tuner module. Say Y when you want to support this tuner.
-
-config DVB_TUNER_QT1010
-	tristate "Quantek QT1010 silicon tuner"
-	depends on DVB_CORE && I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A driver for the silicon tuner QT1010 from Quantek.
-
-config DVB_TUNER_MT2060
-	tristate "Microtune MT2060 silicon IF tuner"
-	depends on I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A driver for the silicon IF tuner MT2060 from Microtune.
-
-config DVB_TUNER_MT2266
-	tristate "Microtune MT2266 silicon tuner"
-	depends on I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A driver for the silicon baseband tuner MT2266 from Microtune.
-
-config DVB_TUNER_MT2131
-	tristate "Microtune MT2131 silicon tuner"
-	depends on I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A driver for the silicon baseband tuner MT2131 from Microtune.
-
 config DVB_TUNER_DIB0070
 	tristate "DiBcom DiB0070 silicon base-band tuner"
 	depends on I2C
@@ -384,21 +356,7 @@
 	  This device is only used inside a SiP called togther with a
 	  demodulator for now.
 
-config DVB_TUNER_XC5000
-	tristate "Xceive XC5000 silicon tuner"
-	depends on I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A driver for the silicon tuner XC5000 from Xceive.
-	  This device is only used inside a SiP called togther with a
-	  demodulator for now.
-
-config DVB_TUNER_ITD1000
-	tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS"
-	depends on DVB_CORE && I2C
-	default m if DVB_FE_CUSTOMISE
-
-comment "Miscellaneous devices"
+comment "SEC control devices for DVB-S"
 	depends on DVB_CORE
 
 config DVB_LNBP21
@@ -422,11 +380,4 @@
 	help
 	  An SEC control chip.
 
-config DVB_TUA6100
-	tristate "TUA6100 PLL"
-	depends on DVB_CORE && I2C
-	default m if DVB_FE_CUSTOMISE
-	help
-	  A DVBS PLL chip.
-
 endmenu
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 9747c73..a89dc0f 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -3,9 +3,7 @@
 #
 
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
-EXTRA_CFLAGS += -Idrivers/media/video/
-
-tda18271-objs := tda18271-tables.o tda18271-common.o tda18271-fe.o
+EXTRA_CFLAGS += -Idrivers/media/common/tuners/
 
 obj-$(CONFIG_DVB_PLL) += dvb-pll.o
 obj-$(CONFIG_DVB_STV0299) += stv0299.o
@@ -42,16 +40,9 @@
 obj-$(CONFIG_DVB_ISL6421) += isl6421.o
 obj-$(CONFIG_DVB_TDA10086) += tda10086.o
 obj-$(CONFIG_DVB_TDA826X) += tda826x.o
-obj-$(CONFIG_DVB_TDA827X) += tda827x.o
-obj-$(CONFIG_DVB_TDA18271) += tda18271.o
-obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
-obj-$(CONFIG_DVB_TUNER_MT2266) += mt2266.o
 obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o
-obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o
 obj-$(CONFIG_DVB_TUA6100) += tua6100.o
-obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131.o
 obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
-obj-$(CONFIG_DVB_TUNER_XC5000) += xc5000.o
 obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o
 obj-$(CONFIG_DVB_AU8522) += au8522.o
 obj-$(CONFIG_DVB_TDA10048) += tda10048.o
diff --git a/drivers/media/dvb/frontends/s5h1420.c b/drivers/media/dvb/frontends/s5h1420.c
index 281e1cb..720ed9f 100644
--- a/drivers/media/dvb/frontends/s5h1420.c
+++ b/drivers/media/dvb/frontends/s5h1420.c
@@ -481,7 +481,7 @@
 		val *= 2;
 	do_div(val, (state->fclk / 1000));
 
-	dprintk("symbol rate register: %06llx\n", val);
+	dprintk("symbol rate register: %06llx\n", (unsigned long long)val);
 
 	v = s5h1420_readreg(state, Loop01);
 	s5h1420_writereg(state, Loop01, v & 0x7f);
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index fe9a4cc..fe743aa 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -1,4 +1,50 @@
 #
+# Generic video config states
+#
+
+config VIDEO_V4L2
+	tristate
+	depends on VIDEO_DEV && VIDEO_V4L2_COMMON
+	default VIDEO_DEV && VIDEO_V4L2_COMMON
+
+config VIDEO_V4L1
+	tristate
+	depends on VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1
+	default VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1
+
+config VIDEOBUF_GEN
+	tristate
+
+config VIDEOBUF_DMA_SG
+	depends on HAS_DMA
+	select VIDEOBUF_GEN
+	tristate
+
+config VIDEOBUF_VMALLOC
+	select VIDEOBUF_GEN
+	tristate
+
+config VIDEOBUF_DVB
+	tristate
+	select VIDEOBUF_GEN
+	select VIDEOBUF_DMA_SG
+
+config VIDEO_BTCX
+	tristate
+
+config VIDEO_IR_I2C
+	tristate
+
+config VIDEO_IR
+	tristate
+	depends on INPUT
+	select VIDEO_IR_I2C if I2C
+
+config VIDEO_TVEEPROM
+	tristate
+	depends on I2C
+
+#
 # Multimedia Video device configuration
 #
 
@@ -644,7 +690,7 @@
 	tristate "Siemens-Nixdorf 'Multimedia eXtension Board'"
 	depends on PCI && VIDEO_V4L1 && I2C
 	select VIDEO_SAA7146_VV
-	select VIDEO_TUNER
+	select MEDIA_TUNER
 	select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
 	select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO
 	select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO
@@ -702,6 +748,8 @@
 
 source "drivers/media/video/ivtv/Kconfig"
 
+source "drivers/media/video/cx18/Kconfig"
+
 config VIDEO_M32R_AR
 	tristate "AR devices"
 	depends on M32R && VIDEO_V4L1
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index be14227..a352c6e 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -84,17 +84,7 @@
 obj-$(CONFIG_VIDEO_DPC) += dpc7146.o
 obj-$(CONFIG_TUNER_3036) += tuner-3036.o
 
-obj-$(CONFIG_VIDEO_TUNER) += tuner.o
-
-obj-$(CONFIG_TUNER_XC2028) += tuner-xc2028.o
-obj-$(CONFIG_TUNER_SIMPLE) += tuner-simple.o
-# tuner-types will be merged into tuner-simple, in the future
-obj-$(CONFIG_TUNER_SIMPLE) += tuner-types.o
-obj-$(CONFIG_TUNER_MT20XX) += mt20xx.o
-obj-$(CONFIG_TUNER_TDA8290) += tda8290.o
-obj-$(CONFIG_TUNER_TEA5767) += tea5767.o
-obj-$(CONFIG_TUNER_TEA5761) += tea5761.o
-obj-$(CONFIG_TUNER_TDA9887) += tda9887.o
+obj-$(CONFIG_MEDIA_TUNER) += tuner.o
 
 obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
 obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
@@ -134,6 +124,7 @@
 obj-$(CONFIG_USB_QUICKCAM_MESSENGER)	+= usbvideo/
 
 obj-$(CONFIG_VIDEO_IVTV) += ivtv/
+obj-$(CONFIG_VIDEO_CX18) += cx18/
 
 obj-$(CONFIG_VIDEO_VIVI) += vivi.o
 obj-$(CONFIG_VIDEO_CX23885) += cx23885/
@@ -147,3 +138,4 @@
 
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig
index 4170826..cab277f 100644
--- a/drivers/media/video/au0828/Kconfig
+++ b/drivers/media/video/au0828/Kconfig
@@ -4,7 +4,7 @@
        depends on VIDEO_DEV && I2C && INPUT && DVB_CORE
 	select I2C_ALGOBIT
 	select DVB_AU8522 if !DVB_FE_CUSTOMIZE
-	select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
+	select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
 	---help---
 	  This is a video4linux driver for Auvitek's USB device.
 
diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile
index 9f4f572..cd2c582 100644
--- a/drivers/media/video/au0828/Makefile
+++ b/drivers/media/video/au0828/Makefile
@@ -2,7 +2,7 @@
 
 obj-$(CONFIG_VIDEO_AU0828) += au0828.o
 
-EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
 
diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig
index cfc822bb..7431ef6 100644
--- a/drivers/media/video/bt8xx/Kconfig
+++ b/drivers/media/video/bt8xx/Kconfig
@@ -6,7 +6,7 @@
 	select VIDEO_BTCX
 	select VIDEOBUF_DMA_SG
 	select VIDEO_IR
-	select VIDEO_TUNER
+	select MEDIA_TUNER
 	select VIDEO_TVEEPROM
 	select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
 	select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO
diff --git a/drivers/media/video/bt8xx/Makefile b/drivers/media/video/bt8xx/Makefile
index 924d216..e415f6f 100644
--- a/drivers/media/video/bt8xx/Makefile
+++ b/drivers/media/video/bt8xx/Makefile
@@ -9,4 +9,5 @@
 obj-$(CONFIG_VIDEO_BT848) += bttv.o
 
 EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig
new file mode 100644
index 0000000..be654a2
--- /dev/null
+++ b/drivers/media/video/cx18/Kconfig
@@ -0,0 +1,20 @@
+config VIDEO_CX18
+	tristate "Conexant cx23418 MPEG encoder support"
+	depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL
+	select I2C_ALGOBIT
+	select FW_LOADER
+	select VIDEO_IR
+	select VIDEO_TUNER
+	select VIDEO_TVEEPROM
+	select VIDEO_CX2341X
+	select VIDEO_CS5345
+	select DVB_S5H1409
+	---help---
+	  This is a video4linux driver for Conexant cx23418 based
+	  PCI combo video recorder devices.
+
+	  This is used in devices such as the Hauppauge HVR-1600
+	  cards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cx18.
diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile
new file mode 100644
index 0000000..b23d2e2
--- /dev/null
+++ b/drivers/media/video/cx18/Makefile
@@ -0,0 +1,11 @@
+cx18-objs    := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \
+	cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \
+	cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \
+	cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \
+	cx18-dvb.o
+
+obj-$(CONFIG_VIDEO_CX18) += cx18.o
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c
new file mode 100644
index 0000000..1adc404
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-audio.c
@@ -0,0 +1,73 @@
+/*
+ *  cx18 audio-related functions
+ *
+ *  Derived from ivtv-audio.c
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-i2c.h"
+#include "cx18-cards.h"
+#include "cx18-audio.h"
+
+/* Selects the audio input and output according to the current
+   settings. */
+int cx18_audio_set_io(struct cx18 *cx)
+{
+	struct v4l2_routing route;
+	u32 audio_input;
+	int mux_input;
+
+	/* Determine which input to use */
+	if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
+		audio_input = cx->card->radio_input.audio_input;
+		mux_input = cx->card->radio_input.muxer_input;
+	} else {
+		audio_input =
+			cx->card->audio_inputs[cx->audio_input].audio_input;
+		mux_input =
+			cx->card->audio_inputs[cx->audio_input].muxer_input;
+	}
+
+	/* handle muxer chips */
+	route.input = mux_input;
+	route.output = 0;
+	cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+
+	route.input = audio_input;
+	return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
+			VIDIOC_INT_S_AUDIO_ROUTING, &route);
+}
+
+void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route)
+{
+	cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
+			VIDIOC_INT_S_AUDIO_ROUTING, route);
+}
+
+void cx18_audio_set_audio_clock_freq(struct cx18 *cx, 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;
+	cx18_call_i2c_clients(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]);
+}
diff --git a/drivers/media/video/cx18/cx18-audio.h b/drivers/media/video/cx18/cx18-audio.h
new file mode 100644
index 0000000..cb569a6
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-audio.h
@@ -0,0 +1,26 @@
+/*
+ *  cx18 audio-related functions
+ *
+ *  Derived from ivtv-audio.c
+ *
+ *  Copyright (C) 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 cx18_audio_set_io(struct cx18 *cx);
+void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route);
+void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq);
diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c
new file mode 100644
index 0000000..2dc3a5d
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-audio.c
@@ -0,0 +1,361 @@
+/*
+ *  cx18 ADEC audio functions
+ *
+ *  Derived from cx25840-audio.c
+ *
+ *  Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include "cx18-driver.h"
+
+static int set_audclk_freq(struct cx18 *cx, u32 freq)
+{
+	struct cx18_av_state *state = &cx->av_state;
+
+	if (freq != 32000 && freq != 44100 && freq != 48000)
+		return -EINVAL;
+
+	/* common for all inputs and rates */
+	/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
+	cx18_av_write(cx, 0x127, 0x50);
+
+	if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
+		switch (freq) {
+		case 32000:
+			/* VID_PLL and AUX_PLL */
+			cx18_av_write4(cx, 0x108, 0x1006040f);
+
+			/* AUX_PLL_FRAC */
+			cx18_av_write4(cx, 0x110, 0x01bb39ee);
+
+			/* src3/4/6_ctl = 0x0801f77f */
+			cx18_av_write4(cx, 0x900, 0x0801f77f);
+			cx18_av_write4(cx, 0x904, 0x0801f77f);
+			cx18_av_write4(cx, 0x90c, 0x0801f77f);
+			break;
+
+		case 44100:
+			/* VID_PLL and AUX_PLL */
+			cx18_av_write4(cx, 0x108, 0x1009040f);
+
+			/* AUX_PLL_FRAC */
+			cx18_av_write4(cx, 0x110, 0x00ec6bd6);
+
+			/* src3/4/6_ctl = 0x08016d59 */
+			cx18_av_write4(cx, 0x900, 0x08016d59);
+			cx18_av_write4(cx, 0x904, 0x08016d59);
+			cx18_av_write4(cx, 0x90c, 0x08016d59);
+			break;
+
+		case 48000:
+			/* VID_PLL and AUX_PLL */
+			cx18_av_write4(cx, 0x108, 0x100a040f);
+
+			/* AUX_PLL_FRAC */
+			cx18_av_write4(cx, 0x110, 0x0098d6e5);
+
+			/* src3/4/6_ctl = 0x08014faa */
+			cx18_av_write4(cx, 0x900, 0x08014faa);
+			cx18_av_write4(cx, 0x904, 0x08014faa);
+			cx18_av_write4(cx, 0x90c, 0x08014faa);
+			break;
+		}
+	} else {
+		switch (freq) {
+		case 32000:
+			/* VID_PLL and AUX_PLL */
+			cx18_av_write4(cx, 0x108, 0x1e08040f);
+
+			/* AUX_PLL_FRAC */
+			cx18_av_write4(cx, 0x110, 0x012a0869);
+
+			/* src1_ctl = 0x08010000 */
+			cx18_av_write4(cx, 0x8f8, 0x08010000);
+
+			/* src3/4/6_ctl = 0x08020000 */
+			cx18_av_write4(cx, 0x900, 0x08020000);
+			cx18_av_write4(cx, 0x904, 0x08020000);
+			cx18_av_write4(cx, 0x90c, 0x08020000);
+
+			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
+			cx18_av_write(cx, 0x127, 0x54);
+			break;
+
+		case 44100:
+			/* VID_PLL and AUX_PLL */
+			cx18_av_write4(cx, 0x108, 0x1809040f);
+
+			/* AUX_PLL_FRAC */
+			cx18_av_write4(cx, 0x110, 0x00ec6bd6);
+
+			/* src1_ctl = 0x08010000 */
+			cx18_av_write4(cx, 0x8f8, 0x080160cd);
+
+			/* src3/4/6_ctl = 0x08020000 */
+			cx18_av_write4(cx, 0x900, 0x08017385);
+			cx18_av_write4(cx, 0x904, 0x08017385);
+			cx18_av_write4(cx, 0x90c, 0x08017385);
+			break;
+
+		case 48000:
+			/* VID_PLL and AUX_PLL */
+			cx18_av_write4(cx, 0x108, 0x180a040f);
+
+			/* AUX_PLL_FRAC */
+			cx18_av_write4(cx, 0x110, 0x0098d6e5);
+
+			/* src1_ctl = 0x08010000 */
+			cx18_av_write4(cx, 0x8f8, 0x08018000);
+
+			/* src3/4/6_ctl = 0x08020000 */
+			cx18_av_write4(cx, 0x900, 0x08015555);
+			cx18_av_write4(cx, 0x904, 0x08015555);
+			cx18_av_write4(cx, 0x90c, 0x08015555);
+			break;
+		}
+	}
+
+	state->audclk_freq = freq;
+
+	return 0;
+}
+
+void cx18_av_audio_set_path(struct cx18 *cx)
+{
+	struct cx18_av_state *state = &cx->av_state;
+
+	/* stop microcontroller */
+	cx18_av_and_or(cx, 0x803, ~0x10, 0);
+
+	/* assert soft reset */
+	cx18_av_and_or(cx, 0x810, ~0x1, 0x01);
+
+	/* Mute everything to prevent the PFFT! */
+	cx18_av_write(cx, 0x8d3, 0x1f);
+
+	if (state->aud_input == CX18_AV_AUDIO_SERIAL) {
+		/* Set Path1 to Serial Audio Input */
+		cx18_av_write4(cx, 0x8d0, 0x01011012);
+
+		/* The microcontroller should not be started for the
+		 * non-tuner inputs: autodetection is specific for
+		 * TV audio. */
+	} else {
+		/* Set Path1 to Analog Demod Main Channel */
+		cx18_av_write4(cx, 0x8d0, 0x1f063870);
+	}
+
+	set_audclk_freq(cx, state->audclk_freq);
+
+	/* deassert soft reset */
+	cx18_av_and_or(cx, 0x810, ~0x1, 0x00);
+
+	if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
+		/* When the microcontroller detects the
+		 * audio format, it will unmute the lines */
+		cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+	}
+}
+
+static int get_volume(struct cx18 *cx)
+{
+	/* Volume runs +18dB to -96dB in 1/2dB steps
+	 * change to fit the msp3400 -114dB to +12dB range */
+
+	/* check PATH1_VOLUME */
+	int vol = 228 - cx18_av_read(cx, 0x8d4);
+	vol = (vol / 2) + 23;
+	return vol << 9;
+}
+
+static void set_volume(struct cx18 *cx, int volume)
+{
+	/* First convert the volume to msp3400 values (0-127) */
+	int vol = volume >> 9;
+	/* now scale it up to cx18_av values
+	 * -114dB to -96dB maps to 0
+	 * this should be 19, but in my testing that was 4dB too loud */
+	if (vol <= 23)
+		vol = 0;
+	else
+		vol -= 23;
+
+	/* PATH1_VOLUME */
+	cx18_av_write(cx, 0x8d4, 228 - (vol * 2));
+}
+
+static int get_bass(struct cx18 *cx)
+{
+	/* bass is 49 steps +12dB to -12dB */
+
+	/* check PATH1_EQ_BASS_VOL */
+	int bass = cx18_av_read(cx, 0x8d9) & 0x3f;
+	bass = (((48 - bass) * 0xffff) + 47) / 48;
+	return bass;
+}
+
+static void set_bass(struct cx18 *cx, int bass)
+{
+	/* PATH1_EQ_BASS_VOL */
+	cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
+}
+
+static int get_treble(struct cx18 *cx)
+{
+	/* treble is 49 steps +12dB to -12dB */
+
+	/* check PATH1_EQ_TREBLE_VOL */
+	int treble = cx18_av_read(cx, 0x8db) & 0x3f;
+	treble = (((48 - treble) * 0xffff) + 47) / 48;
+	return treble;
+}
+
+static void set_treble(struct cx18 *cx, int treble)
+{
+	/* PATH1_EQ_TREBLE_VOL */
+	cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
+}
+
+static int get_balance(struct cx18 *cx)
+{
+	/* balance is 7 bit, 0 to -96dB */
+
+	/* check PATH1_BAL_LEVEL */
+	int balance = cx18_av_read(cx, 0x8d5) & 0x7f;
+	/* check PATH1_BAL_LEFT */
+	if ((cx18_av_read(cx, 0x8d5) & 0x80) == 0)
+		balance = 0x80 - balance;
+	else
+		balance = 0x80 + balance;
+	return balance << 8;
+}
+
+static void set_balance(struct cx18 *cx, int balance)
+{
+	int bal = balance >> 8;
+	if (bal > 0x80) {
+		/* PATH1_BAL_LEFT */
+		cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80);
+		/* PATH1_BAL_LEVEL */
+		cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f);
+	} else {
+		/* PATH1_BAL_LEFT */
+		cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00);
+		/* PATH1_BAL_LEVEL */
+		cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal);
+	}
+}
+
+static int get_mute(struct cx18 *cx)
+{
+	/* check SRC1_MUTE_EN */
+	return cx18_av_read(cx, 0x8d3) & 0x2 ? 1 : 0;
+}
+
+static void set_mute(struct cx18 *cx, int mute)
+{
+	struct cx18_av_state *state = &cx->av_state;
+
+	if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
+		/* Must turn off microcontroller in order to mute sound.
+		 * Not sure if this is the best method, but it does work.
+		 * If the microcontroller is running, then it will undo any
+		 * changes to the mute register. */
+		if (mute) {
+			/* disable microcontroller */
+			cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
+			cx18_av_write(cx, 0x8d3, 0x1f);
+		} else {
+			/* enable microcontroller */
+			cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+		}
+	} else {
+		/* SRC1_MUTE_EN */
+		cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
+	}
+}
+
+int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	struct v4l2_control *ctrl = arg;
+	int retval;
+
+	switch (cmd) {
+	case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+		if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
+			cx18_av_and_or(cx, 0x803, ~0x10, 0);
+			cx18_av_write(cx, 0x8d3, 0x1f);
+		}
+		cx18_av_and_or(cx, 0x810, ~0x1, 1);
+		retval = set_audclk_freq(cx, *(u32 *)arg);
+		cx18_av_and_or(cx, 0x810, ~0x1, 0);
+		if (state->aud_input != CX18_AV_AUDIO_SERIAL)
+			cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+		return retval;
+
+	case VIDIOC_G_CTRL:
+		switch (ctrl->id) {
+		case V4L2_CID_AUDIO_VOLUME:
+			ctrl->value = get_volume(cx);
+			break;
+		case V4L2_CID_AUDIO_BASS:
+			ctrl->value = get_bass(cx);
+			break;
+		case V4L2_CID_AUDIO_TREBLE:
+			ctrl->value = get_treble(cx);
+			break;
+		case V4L2_CID_AUDIO_BALANCE:
+			ctrl->value = get_balance(cx);
+			break;
+		case V4L2_CID_AUDIO_MUTE:
+			ctrl->value = get_mute(cx);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case VIDIOC_S_CTRL:
+		switch (ctrl->id) {
+		case V4L2_CID_AUDIO_VOLUME:
+			set_volume(cx, ctrl->value);
+			break;
+		case V4L2_CID_AUDIO_BASS:
+			set_bass(cx, ctrl->value);
+			break;
+		case V4L2_CID_AUDIO_TREBLE:
+			set_treble(cx, ctrl->value);
+			break;
+		case V4L2_CID_AUDIO_BALANCE:
+			set_balance(cx, ctrl->value);
+			break;
+		case V4L2_CID_AUDIO_MUTE:
+			set_mute(cx, ctrl->value);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
new file mode 100644
index 0000000..6686490
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -0,0 +1,879 @@
+/*
+ *  cx18 ADEC audio functions
+ *
+ *  Derived from cx25840-core.c
+ *
+ *  Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include "cx18-driver.h"
+
+int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
+{
+	u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3));
+	u32 mask = 0xff;
+	int shift = (addr & 3) * 8;
+
+	x = (x & ~(mask << shift)) | ((u32)value << shift);
+	writel(x, cx->reg_mem + 0xc40000 + (addr & ~3));
+	return 0;
+}
+
+int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value)
+{
+	writel(value, cx->reg_mem + 0xc40000 + addr);
+	return 0;
+}
+
+u8 cx18_av_read(struct cx18 *cx, u16 addr)
+{
+	u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3));
+	int shift = (addr & 3) * 8;
+
+	return (x >> shift) & 0xff;
+}
+
+u32 cx18_av_read4(struct cx18 *cx, u16 addr)
+{
+	return readl(cx->reg_mem + 0xc40000 + addr);
+}
+
+int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask,
+		   u8 or_value)
+{
+	return cx18_av_write(cx, addr,
+			     (cx18_av_read(cx, addr) & and_mask) |
+			     or_value);
+}
+
+int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask,
+		   u32 or_value)
+{
+	return cx18_av_write4(cx, addr,
+			     (cx18_av_read4(cx, addr) & and_mask) |
+			     or_value);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
+					enum cx18_av_audio_input aud_input);
+static void log_audio_status(struct cx18 *cx);
+static void log_video_status(struct cx18 *cx);
+
+/* ----------------------------------------------------------------------- */
+
+static void cx18_av_initialize(struct cx18 *cx)
+{
+	u32 v;
+
+	cx18_av_loadfw(cx);
+	/* Stop 8051 code execution */
+	cx18_av_write4(cx, CXADEC_DL_CTL, 0x03000000);
+
+	/* initallize the PLL by toggling sleep bit */
+	v = cx18_av_read4(cx, CXADEC_HOST_REG1);
+	/* enable sleep mode */
+	cx18_av_write4(cx, CXADEC_HOST_REG1, v | 1);
+	/* disable sleep mode */
+	cx18_av_write4(cx, CXADEC_HOST_REG1, v & 0xfffe);
+
+	/* initialize DLLs */
+	v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF;
+	/* disable FLD */
+	cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v);
+	/* enable FLD */
+	cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v | 0x10000100);
+
+	v = cx18_av_read4(cx, CXADEC_DLL2_DIAG_CTRL) & 0xE1FFFEFF;
+	/* disable FLD */
+	cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v);
+	/* enable FLD */
+	cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v | 0x06000100);
+
+	/* set analog bias currents. Set Vreg to 1.20V. */
+	cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL1, 0x000A1802);
+
+	v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1;
+	/* enable TUNE_FIL_RST */
+	cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v);
+	/* disable TUNE_FIL_RST */
+	cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v & 0xFFFFFFFE);
+
+	/* enable 656 output */
+	cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00);
+
+	/* video output drive strength */
+	cx18_av_and_or4(cx, CXADEC_PIN_CTRL2, ~0, 0x2);
+
+	/* reset video */
+	cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000);
+	cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0);
+
+	/* set video to auto-detect */
+	/* Clear bits 11-12 to enable slow locking mode.  Set autodetect mode */
+	/* set the comb notch = 1 */
+	cx18_av_and_or4(cx, CXADEC_MODE_CTRL, 0xFFF7E7F0, 0x02040800);
+
+	/* Enable wtw_en in CRUSH_CTRL (Set bit 22) */
+	/* Enable maj_sel in CRUSH_CTRL (Set bit 20) */
+	cx18_av_and_or4(cx, CXADEC_CRUSH_CTRL, ~0, 0x00500000);
+
+	/* Set VGA_TRACK_RANGE to 0x20 */
+	cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000);
+
+	/* Enable VBI capture */
+	cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253F);
+	/* cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253E); */
+
+	/* Set the video input.
+	   The setting in MODE_CTRL gets lost when we do the above setup */
+	/* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */
+	/* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */
+
+	v = cx18_av_read4(cx, CXADEC_AFE_CTRL);
+	v &= 0xFFFBFFFF;            /* turn OFF bit 18 for droop_comp_ch1 */
+	v &= 0xFFFF7FFF;            /* turn OFF bit 9 for clamp_sel_ch1 */
+	v &= 0xFFFFFFFE;            /* turn OFF bit 0 for 12db_ch1 */
+	/* v |= 0x00000001;*/            /* turn ON bit 0 for 12db_ch1 */
+	cx18_av_write4(cx, CXADEC_AFE_CTRL, v);
+
+/* 	if(dwEnable && dw3DCombAvailable) { */
+/*      	CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */
+/*    } else { */
+/*      	CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */
+/*    } */
+	cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void input_change(struct cx18 *cx)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	v4l2_std_id std = state->std;
+
+	/* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */
+	if (std & V4L2_STD_SECAM)
+		cx18_av_write(cx, 0x402, 0);
+	else {
+		cx18_av_write(cx, 0x402, 0x04);
+		cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
+	}
+	cx18_av_and_or(cx, 0x401, ~0x60, 0);
+	cx18_av_and_or(cx, 0x401, ~0x60, 0x60);
+
+	if (std & V4L2_STD_525_60) {
+		if (std == V4L2_STD_NTSC_M_JP) {
+			/* Japan uses EIAJ audio standard */
+			cx18_av_write(cx, 0x808, 0xf7);
+		} else if (std == V4L2_STD_NTSC_M_KR) {
+			/* South Korea uses A2 audio standard */
+			cx18_av_write(cx, 0x808, 0xf8);
+		} else {
+			/* Others use the BTSC audio standard */
+			cx18_av_write(cx, 0x808, 0xf6);
+		}
+		cx18_av_write(cx, 0x80b, 0x00);
+	} else if (std & V4L2_STD_PAL) {
+		/* Follow tuner change procedure for PAL */
+		cx18_av_write(cx, 0x808, 0xff);
+		cx18_av_write(cx, 0x80b, 0x03);
+	} else if (std & V4L2_STD_SECAM) {
+		/* Select autodetect for SECAM */
+		cx18_av_write(cx, 0x808, 0xff);
+		cx18_av_write(cx, 0x80b, 0x03);
+	}
+
+	if (cx18_av_read(cx, 0x803) & 0x10) {
+		/* restart audio decoder microcontroller */
+		cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
+		cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+	}
+}
+
+static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
+					enum cx18_av_audio_input aud_input)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 &&
+			   vid_input <= CX18_AV_COMPOSITE8);
+	u8 reg;
+
+	CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n",
+			vid_input, aud_input);
+
+	if (is_composite) {
+		reg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1);
+	} else {
+		int luma = vid_input & 0xf0;
+		int chroma = vid_input & 0xf00;
+
+		if ((vid_input & ~0xff0) ||
+		    luma < CX18_AV_SVIDEO_LUMA1 ||
+		    luma > CX18_AV_SVIDEO_LUMA4 ||
+		    chroma < CX18_AV_SVIDEO_CHROMA4 ||
+		    chroma > CX18_AV_SVIDEO_CHROMA8) {
+			CX18_ERR("0x%04x is not a valid video input!\n",
+					vid_input);
+			return -EINVAL;
+		}
+		reg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4);
+		if (chroma >= CX18_AV_SVIDEO_CHROMA7) {
+			reg &= 0x3f;
+			reg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2;
+		} else {
+			reg &= 0xcf;
+			reg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4;
+		}
+	}
+
+	switch (aud_input) {
+	case CX18_AV_AUDIO_SERIAL:
+		/* do nothing, use serial audio input */
+		break;
+	case CX18_AV_AUDIO4: reg &= ~0x30; break;
+	case CX18_AV_AUDIO5: reg &= ~0x30; reg |= 0x10; break;
+	case CX18_AV_AUDIO6: reg &= ~0x30; reg |= 0x20; break;
+	case CX18_AV_AUDIO7: reg &= ~0xc0; break;
+	case CX18_AV_AUDIO8: reg &= ~0xc0; reg |= 0x40; break;
+
+	default:
+		CX18_ERR("0x%04x is not a valid audio input!\n", aud_input);
+		return -EINVAL;
+	}
+
+	cx18_av_write(cx, 0x103, reg);
+	/* Set INPUT_MODE to Composite (0) or S-Video (1) */
+	cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02);
+	/* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
+	cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
+	/* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
+	if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
+		cx18_av_and_or(cx, 0x102, ~0x4, 4);
+	else
+		cx18_av_and_or(cx, 0x102, ~0x4, 0);
+	/*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/
+
+	state->vid_input = vid_input;
+	state->aud_input = aud_input;
+	cx18_av_audio_set_path(cx);
+	input_change(cx);
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int set_v4lstd(struct cx18 *cx)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	u8 fmt = 0; 	/* zero is autodetect */
+	u8 pal_m = 0;
+
+	/* First tests should be against specific std */
+	if (state->std == V4L2_STD_NTSC_M_JP) {
+		fmt = 0x2;
+	} else if (state->std == V4L2_STD_NTSC_443) {
+		fmt = 0x3;
+	} else if (state->std == V4L2_STD_PAL_M) {
+		pal_m = 1;
+		fmt = 0x5;
+	} else if (state->std == V4L2_STD_PAL_N) {
+		fmt = 0x6;
+	} else if (state->std == V4L2_STD_PAL_Nc) {
+		fmt = 0x7;
+	} else if (state->std == V4L2_STD_PAL_60) {
+		fmt = 0x8;
+	} else {
+		/* Then, test against generic ones */
+		if (state->std & V4L2_STD_NTSC)
+			fmt = 0x1;
+		else if (state->std & V4L2_STD_PAL)
+			fmt = 0x4;
+		else if (state->std & V4L2_STD_SECAM)
+			fmt = 0xc;
+	}
+
+	CX18_DEBUG_INFO("changing video std to fmt %i\n", fmt);
+
+	/* Follow step 9 of section 3.16 in the cx18_av datasheet.
+	   Without this PAL may display a vertical ghosting effect.
+	   This happens for example with the Yuan MPC622. */
+	if (fmt >= 4 && fmt < 8) {
+		/* Set format to NTSC-M */
+		cx18_av_and_or(cx, 0x400, ~0xf, 1);
+		/* Turn off LCOMB */
+		cx18_av_and_or(cx, 0x47b, ~6, 0);
+	}
+	cx18_av_and_or(cx, 0x400, ~0xf, fmt);
+	cx18_av_and_or(cx, 0x403, ~0x3, pal_m);
+	cx18_av_vbi_setup(cx);
+	input_change(cx);
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		if (ctrl->value < 0 || ctrl->value > 255) {
+			CX18_ERR("invalid brightness setting %d\n",
+				    ctrl->value);
+			return -ERANGE;
+		}
+
+		cx18_av_write(cx, 0x414, ctrl->value - 128);
+		break;
+
+	case V4L2_CID_CONTRAST:
+		if (ctrl->value < 0 || ctrl->value > 127) {
+			CX18_ERR("invalid contrast setting %d\n",
+				    ctrl->value);
+			return -ERANGE;
+		}
+
+		cx18_av_write(cx, 0x415, ctrl->value << 1);
+		break;
+
+	case V4L2_CID_SATURATION:
+		if (ctrl->value < 0 || ctrl->value > 127) {
+			CX18_ERR("invalid saturation setting %d\n",
+				    ctrl->value);
+			return -ERANGE;
+		}
+
+		cx18_av_write(cx, 0x420, ctrl->value << 1);
+		cx18_av_write(cx, 0x421, ctrl->value << 1);
+		break;
+
+	case V4L2_CID_HUE:
+		if (ctrl->value < -127 || ctrl->value > 127) {
+			CX18_ERR("invalid hue setting %d\n", ctrl->value);
+			return -ERANGE;
+		}
+
+		cx18_av_write(cx, 0x422, ctrl->value);
+		break;
+
+	case V4L2_CID_AUDIO_VOLUME:
+	case V4L2_CID_AUDIO_BASS:
+	case V4L2_CID_AUDIO_TREBLE:
+	case V4L2_CID_AUDIO_BALANCE:
+	case V4L2_CID_AUDIO_MUTE:
+		return cx18_av_audio(cx, VIDIOC_S_CTRL, ctrl);
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+{
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128;
+		break;
+	case V4L2_CID_CONTRAST:
+		ctrl->value = cx18_av_read(cx, 0x415) >> 1;
+		break;
+	case V4L2_CID_SATURATION:
+		ctrl->value = cx18_av_read(cx, 0x420) >> 1;
+		break;
+	case V4L2_CID_HUE:
+		ctrl->value = (s8)cx18_av_read(cx, 0x422);
+		break;
+	case V4L2_CID_AUDIO_VOLUME:
+	case V4L2_CID_AUDIO_BASS:
+	case V4L2_CID_AUDIO_TREBLE:
+	case V4L2_CID_AUDIO_BALANCE:
+	case V4L2_CID_AUDIO_MUTE:
+		return cx18_av_audio(cx, VIDIOC_G_CTRL, ctrl);
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int get_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
+{
+	switch (fmt->type) {
+	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+		return cx18_av_vbi(cx, VIDIOC_G_FMT, fmt);
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	struct v4l2_pix_format *pix;
+	int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
+	int is_50Hz = !(state->std & V4L2_STD_525_60);
+
+	switch (fmt->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		pix = &(fmt->fmt.pix);
+
+		Vsrc = (cx18_av_read(cx, 0x476) & 0x3f) << 4;
+		Vsrc |= (cx18_av_read(cx, 0x475) & 0xf0) >> 4;
+
+		Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4;
+		Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4;
+
+		Vlines = pix->height + (is_50Hz ? 4 : 7);
+
+		if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) ||
+		    (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
+			CX18_ERR("%dx%d is not a valid size!\n",
+				    pix->width, pix->height);
+			return -ERANGE;
+		}
+
+		HSC = (Hsrc * (1 << 20)) / pix->width - (1 << 20);
+		VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9));
+		VSC &= 0x1fff;
+
+		if (pix->width >= 385)
+			filter = 0;
+		else if (pix->width > 192)
+			filter = 1;
+		else if (pix->width > 96)
+			filter = 2;
+		else
+			filter = 3;
+
+		CX18_DEBUG_INFO("decoder set size %dx%d -> scale  %ux%u\n",
+			    pix->width, pix->height, HSC, VSC);
+
+		/* HSCALE=HSC */
+		cx18_av_write(cx, 0x418, HSC & 0xff);
+		cx18_av_write(cx, 0x419, (HSC >> 8) & 0xff);
+		cx18_av_write(cx, 0x41a, HSC >> 16);
+		/* VSCALE=VSC */
+		cx18_av_write(cx, 0x41c, VSC & 0xff);
+		cx18_av_write(cx, 0x41d, VSC >> 8);
+		/* VS_INTRLACE=1 VFILT=filter */
+		cx18_av_write(cx, 0x41e, 0x8 | filter);
+		break;
+
+	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+		return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt);
+
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt);
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	struct v4l2_tuner *vt = arg;
+	struct v4l2_routing *route = arg;
+
+	/* ignore these commands */
+	switch (cmd) {
+	case TUNER_SET_TYPE_ADDR:
+		return 0;
+	}
+
+	if (!state->is_initialized) {
+		CX18_DEBUG_INFO("cmd %08x triggered fw load\n", cmd);
+		/* initialize on first use */
+		state->is_initialized = 1;
+		cx18_av_initialize(cx);
+	}
+
+	switch (cmd) {
+	case VIDIOC_INT_DECODE_VBI_LINE:
+		return cx18_av_vbi(cx, cmd, arg);
+
+	case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+		return cx18_av_audio(cx, cmd, arg);
+
+	case VIDIOC_STREAMON:
+		CX18_DEBUG_INFO("enable output\n");
+		cx18_av_write(cx, 0x115, 0x8c);
+		cx18_av_write(cx, 0x116, 0x07);
+		break;
+
+	case VIDIOC_STREAMOFF:
+		CX18_DEBUG_INFO("disable output\n");
+		cx18_av_write(cx, 0x115, 0x00);
+		cx18_av_write(cx, 0x116, 0x00);
+		break;
+
+	case VIDIOC_LOG_STATUS:
+		log_video_status(cx);
+		log_audio_status(cx);
+		break;
+
+	case VIDIOC_G_CTRL:
+		return get_v4lctrl(cx, (struct v4l2_control *)arg);
+
+	case VIDIOC_S_CTRL:
+		return set_v4lctrl(cx, (struct v4l2_control *)arg);
+
+	case VIDIOC_QUERYCTRL:
+	{
+		struct v4l2_queryctrl *qc = arg;
+
+		switch (qc->id) {
+		case V4L2_CID_BRIGHTNESS:
+		case V4L2_CID_CONTRAST:
+		case V4L2_CID_SATURATION:
+		case V4L2_CID_HUE:
+			return v4l2_ctrl_query_fill_std(qc);
+		default:
+			break;
+		}
+
+		switch (qc->id) {
+		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:
+			return v4l2_ctrl_query_fill_std(qc);
+		default:
+			return -EINVAL;
+		}
+		return -EINVAL;
+	}
+
+	case VIDIOC_G_STD:
+		*(v4l2_std_id *)arg = state->std;
+		break;
+
+	case VIDIOC_S_STD:
+		if (state->radio == 0 && state->std == *(v4l2_std_id *)arg)
+			return 0;
+		state->radio = 0;
+		state->std = *(v4l2_std_id *)arg;
+		return set_v4lstd(cx);
+
+	case AUDC_SET_RADIO:
+		state->radio = 1;
+		break;
+
+	case VIDIOC_INT_G_VIDEO_ROUTING:
+		route->input = state->vid_input;
+		route->output = 0;
+		break;
+
+	case VIDIOC_INT_S_VIDEO_ROUTING:
+		return set_input(cx, route->input, state->aud_input);
+
+	case VIDIOC_INT_G_AUDIO_ROUTING:
+		route->input = state->aud_input;
+		route->output = 0;
+		break;
+
+	case VIDIOC_INT_S_AUDIO_ROUTING:
+		return set_input(cx, state->vid_input, route->input);
+
+	case VIDIOC_S_FREQUENCY:
+		input_change(cx);
+		break;
+
+	case VIDIOC_G_TUNER:
+	{
+		u8 vpres = cx18_av_read(cx, 0x40e) & 0x20;
+		u8 mode;
+		int val = 0;
+
+		if (state->radio)
+			break;
+
+		vt->signal = vpres ? 0xffff : 0x0;
+
+		vt->capability |=
+		    V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+		    V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+
+		mode = cx18_av_read(cx, 0x804);
+
+		/* get rxsubchans and audmode */
+		if ((mode & 0xf) == 1)
+			val |= V4L2_TUNER_SUB_STEREO;
+		else
+			val |= V4L2_TUNER_SUB_MONO;
+
+		if (mode == 2 || mode == 4)
+			val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+
+		if (mode & 0x10)
+			val |= V4L2_TUNER_SUB_SAP;
+
+		vt->rxsubchans = val;
+		vt->audmode = state->audmode;
+		break;
+	}
+
+	case VIDIOC_S_TUNER:
+		if (state->radio)
+			break;
+
+		switch (vt->audmode) {
+		case V4L2_TUNER_MODE_MONO:
+			/* mono      -> mono
+			   stereo    -> mono
+			   bilingual -> lang1 */
+			cx18_av_and_or(cx, 0x809, ~0xf, 0x00);
+			break;
+		case V4L2_TUNER_MODE_STEREO:
+		case V4L2_TUNER_MODE_LANG1:
+			/* mono      -> mono
+			   stereo    -> stereo
+			   bilingual -> lang1 */
+			cx18_av_and_or(cx, 0x809, ~0xf, 0x04);
+			break;
+		case V4L2_TUNER_MODE_LANG1_LANG2:
+			/* mono      -> mono
+			   stereo    -> stereo
+			   bilingual -> lang1/lang2 */
+			cx18_av_and_or(cx, 0x809, ~0xf, 0x07);
+			break;
+		case V4L2_TUNER_MODE_LANG2:
+			/* mono      -> mono
+			   stereo    -> stereo
+			   bilingual -> lang2 */
+			cx18_av_and_or(cx, 0x809, ~0xf, 0x01);
+			break;
+		default:
+			return -EINVAL;
+		}
+		state->audmode = vt->audmode;
+		break;
+
+	case VIDIOC_G_FMT:
+		return get_v4lfmt(cx, (struct v4l2_format *)arg);
+
+	case VIDIOC_S_FMT:
+		return set_v4lfmt(cx, (struct v4l2_format *)arg);
+
+	case VIDIOC_INT_RESET:
+		cx18_av_initialize(cx);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------- */
+
+static void log_video_status(struct cx18 *cx)
+{
+	static const char *const fmt_strs[] = {
+		"0x0",
+		"NTSC-M", "NTSC-J", "NTSC-4.43",
+		"PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60",
+		"0x9", "0xA", "0xB",
+		"SECAM",
+		"0xD", "0xE", "0xF"
+	};
+
+	struct cx18_av_state *state = &cx->av_state;
+	u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf;
+	u8 gen_stat1 = cx18_av_read(cx, 0x40d);
+	u8 gen_stat2 = cx18_av_read(cx, 0x40e);
+	int vid_input = state->vid_input;
+
+	CX18_INFO("Video signal:              %spresent\n",
+		    (gen_stat2 & 0x20) ? "" : "not ");
+	CX18_INFO("Detected format:           %s\n",
+		    fmt_strs[gen_stat1 & 0xf]);
+
+	CX18_INFO("Specified standard:        %s\n",
+		    vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection");
+
+	if (vid_input >= CX18_AV_COMPOSITE1 &&
+	    vid_input <= CX18_AV_COMPOSITE8) {
+		CX18_INFO("Specified video input:     Composite %d\n",
+			vid_input - CX18_AV_COMPOSITE1 + 1);
+	} else {
+		CX18_INFO("Specified video input:     S-Video (Luma In%d, Chroma In%d)\n",
+			(vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8);
+	}
+
+	CX18_INFO("Specified audioclock freq: %d Hz\n", state->audclk_freq);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void log_audio_status(struct cx18 *cx)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	u8 download_ctl = cx18_av_read(cx, 0x803);
+	u8 mod_det_stat0 = cx18_av_read(cx, 0x805);
+	u8 mod_det_stat1 = cx18_av_read(cx, 0x804);
+	u8 audio_config = cx18_av_read(cx, 0x808);
+	u8 pref_mode = cx18_av_read(cx, 0x809);
+	u8 afc0 = cx18_av_read(cx, 0x80b);
+	u8 mute_ctl = cx18_av_read(cx, 0x8d3);
+	int aud_input = state->aud_input;
+	char *p;
+
+	switch (mod_det_stat0) {
+	case 0x00: p = "mono"; break;
+	case 0x01: p = "stereo"; break;
+	case 0x02: p = "dual"; break;
+	case 0x04: p = "tri"; break;
+	case 0x10: p = "mono with SAP"; break;
+	case 0x11: p = "stereo with SAP"; break;
+	case 0x12: p = "dual with SAP"; break;
+	case 0x14: p = "tri with SAP"; break;
+	case 0xfe: p = "forced mode"; break;
+	default: p = "not defined";
+	}
+	CX18_INFO("Detected audio mode:       %s\n", p);
+
+	switch (mod_det_stat1) {
+	case 0x00: p = "BTSC"; break;
+	case 0x01: p = "EIAJ"; break;
+	case 0x02: p = "A2-M"; break;
+	case 0x03: p = "A2-BG"; break;
+	case 0x04: p = "A2-DK1"; break;
+	case 0x05: p = "A2-DK2"; break;
+	case 0x06: p = "A2-DK3"; break;
+	case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
+	case 0x08: p = "AM-L"; break;
+	case 0x09: p = "NICAM-BG"; break;
+	case 0x0a: p = "NICAM-DK"; break;
+	case 0x0b: p = "NICAM-I"; break;
+	case 0x0c: p = "NICAM-L"; break;
+	case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break;
+	case 0xff: p = "no detected audio standard"; break;
+	default: p = "not defined";
+	}
+	CX18_INFO("Detected audio standard:   %s\n", p);
+	CX18_INFO("Audio muted:               %s\n",
+		    (mute_ctl & 0x2) ? "yes" : "no");
+	CX18_INFO("Audio microcontroller:     %s\n",
+		    (download_ctl & 0x10) ? "running" : "stopped");
+
+	switch (audio_config >> 4) {
+	case 0x00: p = "BTSC"; break;
+	case 0x01: p = "EIAJ"; break;
+	case 0x02: p = "A2-M"; break;
+	case 0x03: p = "A2-BG"; break;
+	case 0x04: p = "A2-DK1"; break;
+	case 0x05: p = "A2-DK2"; break;
+	case 0x06: p = "A2-DK3"; break;
+	case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
+	case 0x08: p = "AM-L"; break;
+	case 0x09: p = "NICAM-BG"; break;
+	case 0x0a: p = "NICAM-DK"; break;
+	case 0x0b: p = "NICAM-I"; break;
+	case 0x0c: p = "NICAM-L"; break;
+	case 0x0d: p = "FM radio"; break;
+	case 0x0f: p = "automatic detection"; break;
+	default: p = "undefined";
+	}
+	CX18_INFO("Configured audio standard: %s\n", p);
+
+	if ((audio_config >> 4) < 0xF) {
+		switch (audio_config & 0xF) {
+		case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break;
+		case 0x01: p = "MONO2 (LANGUAGE B)"; break;
+		case 0x02: p = "MONO3 (STEREO forced MONO)"; break;
+		case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break;
+		case 0x04: p = "STEREO"; break;
+		case 0x05: p = "DUAL1 (AB)"; break;
+		case 0x06: p = "DUAL2 (AC) (FM)"; break;
+		case 0x07: p = "DUAL3 (BC) (FM)"; break;
+		case 0x08: p = "DUAL4 (AC) (AM)"; break;
+		case 0x09: p = "DUAL5 (BC) (AM)"; break;
+		case 0x0a: p = "SAP"; break;
+		default: p = "undefined";
+		}
+		CX18_INFO("Configured audio mode:     %s\n", p);
+	} else {
+		switch (audio_config & 0xF) {
+		case 0x00: p = "BG"; break;
+		case 0x01: p = "DK1"; break;
+		case 0x02: p = "DK2"; break;
+		case 0x03: p = "DK3"; break;
+		case 0x04: p = "I"; break;
+		case 0x05: p = "L"; break;
+		case 0x06: p = "BTSC"; break;
+		case 0x07: p = "EIAJ"; break;
+		case 0x08: p = "A2-M"; break;
+		case 0x09: p = "FM Radio"; break;
+		case 0x0f: p = "automatic standard and mode detection"; break;
+		default: p = "undefined";
+		}
+		CX18_INFO("Configured audio system:   %s\n", p);
+	}
+
+	if (aud_input)
+		CX18_INFO("Specified audio input:     Tuner (In%d)\n",
+				aud_input);
+	else
+		CX18_INFO("Specified audio input:     External\n");
+
+	switch (pref_mode & 0xf) {
+	case 0: p = "mono/language A"; break;
+	case 1: p = "language B"; break;
+	case 2: p = "language C"; break;
+	case 3: p = "analog fallback"; break;
+	case 4: p = "stereo"; break;
+	case 5: p = "language AC"; break;
+	case 6: p = "language BC"; break;
+	case 7: p = "language AB"; break;
+	default: p = "undefined";
+	}
+	CX18_INFO("Preferred audio mode:      %s\n", p);
+
+	if ((audio_config & 0xf) == 0xf) {
+		switch ((afc0 >> 2) & 0x1) {
+		case 0: p = "system DK"; break;
+		case 1: p = "system L"; break;
+		}
+		CX18_INFO("Selected 65 MHz format:    %s\n", p);
+
+		switch (afc0 & 0x3) {
+		case 0: p = "BTSC"; break;
+		case 1: p = "EIAJ"; break;
+		case 2: p = "A2-M"; break;
+		default: p = "undefined";
+		}
+		CX18_INFO("Selected 45 MHz format:    %s\n", p);
+	}
+}
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
new file mode 100644
index 0000000..786901d
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -0,0 +1,318 @@
+/*
+ *  cx18 ADEC header
+ *
+ *  Derived from cx25840-core.h
+ *
+ *  Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _CX18_AV_CORE_H_
+#define _CX18_AV_CORE_H_
+
+struct cx18;
+
+enum cx18_av_video_input {
+	/* Composite video inputs In1-In8 */
+	CX18_AV_COMPOSITE1 = 1,
+	CX18_AV_COMPOSITE2,
+	CX18_AV_COMPOSITE3,
+	CX18_AV_COMPOSITE4,
+	CX18_AV_COMPOSITE5,
+	CX18_AV_COMPOSITE6,
+	CX18_AV_COMPOSITE7,
+	CX18_AV_COMPOSITE8,
+
+	/* S-Video inputs consist of one luma input (In1-In4) ORed with one
+	   chroma input (In5-In8) */
+	CX18_AV_SVIDEO_LUMA1 = 0x10,
+	CX18_AV_SVIDEO_LUMA2 = 0x20,
+	CX18_AV_SVIDEO_LUMA3 = 0x30,
+	CX18_AV_SVIDEO_LUMA4 = 0x40,
+	CX18_AV_SVIDEO_CHROMA4 = 0x400,
+	CX18_AV_SVIDEO_CHROMA5 = 0x500,
+	CX18_AV_SVIDEO_CHROMA6 = 0x600,
+	CX18_AV_SVIDEO_CHROMA7 = 0x700,
+	CX18_AV_SVIDEO_CHROMA8 = 0x800,
+
+	/* S-Video aliases for common luma/chroma combinations */
+	CX18_AV_SVIDEO1 = 0x510,
+	CX18_AV_SVIDEO2 = 0x620,
+	CX18_AV_SVIDEO3 = 0x730,
+	CX18_AV_SVIDEO4 = 0x840,
+};
+
+enum cx18_av_audio_input {
+	/* Audio inputs: serial or In4-In8 */
+	CX18_AV_AUDIO_SERIAL,
+	CX18_AV_AUDIO4 = 4,
+	CX18_AV_AUDIO5,
+	CX18_AV_AUDIO6,
+	CX18_AV_AUDIO7,
+	CX18_AV_AUDIO8,
+};
+
+struct cx18_av_state {
+	int radio;
+	v4l2_std_id std;
+	enum cx18_av_video_input vid_input;
+	enum cx18_av_audio_input aud_input;
+	u32 audclk_freq;
+	int audmode;
+	int vbi_line_offset;
+	u32 id;
+	u32 rev;
+	int is_initialized;
+};
+
+
+/* Registers */
+#define CXADEC_CHIP_TYPE_TIGER     0x837
+#define CXADEC_CHIP_TYPE_MAKO      0x843
+
+#define CXADEC_HOST_REG1           0x000
+#define CXADEC_HOST_REG2           0x001
+
+#define CXADEC_CHIP_CTRL           0x100
+#define CXADEC_AFE_CTRL            0x104
+#define CXADEC_PLL_CTRL1           0x108
+#define CXADEC_VID_PLL_FRAC        0x10C
+#define CXADEC_AUX_PLL_FRAC        0x110
+#define CXADEC_PIN_CTRL1           0x114
+#define CXADEC_PIN_CTRL2           0x118
+#define CXADEC_PIN_CFG1            0x11C
+#define CXADEC_PIN_CFG2            0x120
+
+#define CXADEC_PIN_CFG3            0x124
+#define CXADEC_I2S_MCLK            0x127
+
+#define CXADEC_AUD_LOCK1           0x128
+#define CXADEC_AUD_LOCK2           0x12C
+#define CXADEC_POWER_CTRL          0x130
+#define CXADEC_AFE_DIAG_CTRL1      0x134
+#define CXADEC_AFE_DIAG_CTRL2      0x138
+#define CXADEC_AFE_DIAG_CTRL3      0x13C
+#define CXADEC_PLL_DIAG_CTRL       0x140
+#define CXADEC_TEST_CTRL1          0x144
+#define CXADEC_TEST_CTRL2          0x148
+#define CXADEC_BIST_STAT           0x14C
+#define CXADEC_DLL1_DIAG_CTRL      0x158
+#define CXADEC_DLL2_DIAG_CTRL      0x15C
+
+/* IR registers */
+#define CXADEC_IR_CTRL_REG         0x200
+#define CXADEC_IR_TXCLK_REG        0x204
+#define CXADEC_IR_RXCLK_REG        0x208
+#define CXADEC_IR_CDUTY_REG        0x20C
+#define CXADEC_IR_STAT_REG         0x210
+#define CXADEC_IR_IRQEN_REG        0x214
+#define CXADEC_IR_FILTER_REG       0x218
+#define CXADEC_IR_FIFO_REG         0x21C
+
+/* Video Registers */
+#define CXADEC_MODE_CTRL           0x400
+#define CXADEC_OUT_CTRL1           0x404
+#define CXADEC_OUT_CTRL2           0x408
+#define CXADEC_GEN_STAT            0x40C
+#define CXADEC_INT_STAT_MASK       0x410
+#define CXADEC_LUMA_CTRL           0x414
+
+#define CXADEC_BRIGHTNESS_CTRL_BYTE 0x414
+#define CXADEC_CONTRAST_CTRL_BYTE  0x415
+#define CXADEC_LUMA_CTRL_BYTE_3    0x416
+
+#define CXADEC_HSCALE_CTRL         0x418
+#define CXADEC_VSCALE_CTRL         0x41C
+
+#define CXADEC_CHROMA_CTRL         0x420
+
+#define CXADEC_USAT_CTRL_BYTE      0x420
+#define CXADEC_VSAT_CTRL_BYTE      0x421
+#define CXADEC_HUE_CTRL_BYTE       0x422
+
+#define CXADEC_VBI_LINE_CTRL1      0x424
+#define CXADEC_VBI_LINE_CTRL2      0x428
+#define CXADEC_VBI_LINE_CTRL3      0x42C
+#define CXADEC_VBI_LINE_CTRL4      0x430
+#define CXADEC_VBI_LINE_CTRL5      0x434
+#define CXADEC_VBI_FC_CFG          0x438
+#define CXADEC_VBI_MISC_CFG1       0x43C
+#define CXADEC_VBI_MISC_CFG2       0x440
+#define CXADEC_VBI_PAY1            0x444
+#define CXADEC_VBI_PAY2            0x448
+#define CXADEC_VBI_CUST1_CFG1      0x44C
+#define CXADEC_VBI_CUST1_CFG2      0x450
+#define CXADEC_VBI_CUST1_CFG3      0x454
+#define CXADEC_VBI_CUST2_CFG1      0x458
+#define CXADEC_VBI_CUST2_CFG2      0x45C
+#define CXADEC_VBI_CUST2_CFG3      0x460
+#define CXADEC_VBI_CUST3_CFG1      0x464
+#define CXADEC_VBI_CUST3_CFG2      0x468
+#define CXADEC_VBI_CUST3_CFG3      0x46C
+#define CXADEC_HORIZ_TIM_CTRL      0x470
+#define CXADEC_VERT_TIM_CTRL       0x474
+#define CXADEC_SRC_COMB_CFG        0x478
+#define CXADEC_CHROMA_VBIOFF_CFG   0x47C
+#define CXADEC_FIELD_COUNT         0x480
+#define CXADEC_MISC_TIM_CTRL       0x484
+#define CXADEC_DFE_CTRL1           0x488
+#define CXADEC_DFE_CTRL2           0x48C
+#define CXADEC_DFE_CTRL3           0x490
+#define CXADEC_PLL_CTRL2           0x494
+#define CXADEC_HTL_CTRL            0x498
+#define CXADEC_COMB_CTRL           0x49C
+#define CXADEC_CRUSH_CTRL          0x4A0
+#define CXADEC_SOFT_RST_CTRL       0x4A4
+#define CXADEC_MV_DT_CTRL2         0x4A8
+#define CXADEC_MV_DT_CTRL3         0x4AC
+#define CXADEC_MISC_DIAG_CTRL      0x4B8
+
+#define CXADEC_DL_CTL              0x800
+#define CXADEC_DL_CTL_ADDRESS_LOW  0x800   /* Byte 1 in DL_CTL */
+#define CXADEC_DL_CTL_ADDRESS_HIGH 0x801   /* Byte 2 in DL_CTL */
+#define CXADEC_DL_CTL_DATA         0x802   /* Byte 3 in DL_CTL */
+#define CXADEC_DL_CTL_CONTROL      0x803   /* Byte 4 in DL_CTL */
+
+#define CXADEC_STD_DET_STATUS      0x804
+
+#define CXADEC_STD_DET_CTL         0x808
+#define CXADEC_STD_DET_CTL_AUD_CTL   0x808 /* Byte 1 in STD_DET_CTL */
+#define CXADEC_STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */
+
+#define CXADEC_DW8051_INT          0x80C
+#define CXADEC_GENERAL_CTL         0x810
+#define CXADEC_AAGC_CTL            0x814
+#define CXADEC_IF_SRC_CTL          0x818
+#define CXADEC_ANLOG_DEMOD_CTL     0x81C
+#define CXADEC_ROT_FREQ_CTL        0x820
+#define CXADEC_FM1_CTL             0x824
+#define CXADEC_PDF_CTL             0x828
+#define CXADEC_DFT1_CTL1           0x82C
+#define CXADEC_DFT1_CTL2           0x830
+#define CXADEC_DFT_STATUS          0x834
+#define CXADEC_DFT2_CTL1           0x838
+#define CXADEC_DFT2_CTL2           0x83C
+#define CXADEC_DFT2_STATUS         0x840
+#define CXADEC_DFT3_CTL1           0x844
+#define CXADEC_DFT3_CTL2           0x848
+#define CXADEC_DFT3_STATUS         0x84C
+#define CXADEC_DFT4_CTL1           0x850
+#define CXADEC_DFT4_CTL2           0x854
+#define CXADEC_DFT4_STATUS         0x858
+#define CXADEC_AM_MTS_DET          0x85C
+#define CXADEC_ANALOG_MUX_CTL      0x860
+#define CXADEC_DIG_PLL_CTL1        0x864
+#define CXADEC_DIG_PLL_CTL2        0x868
+#define CXADEC_DIG_PLL_CTL3        0x86C
+#define CXADEC_DIG_PLL_CTL4        0x870
+#define CXADEC_DIG_PLL_CTL5        0x874
+#define CXADEC_DEEMPH_GAIN_CTL     0x878
+#define CXADEC_DEEMPH_COEF1        0x87C
+#define CXADEC_DEEMPH_COEF2        0x880
+#define CXADEC_DBX1_CTL1           0x884
+#define CXADEC_DBX1_CTL2           0x888
+#define CXADEC_DBX1_STATUS         0x88C
+#define CXADEC_DBX2_CTL1           0x890
+#define CXADEC_DBX2_CTL2           0x894
+#define CXADEC_DBX2_STATUS         0x898
+#define CXADEC_AM_FM_DIFF          0x89C
+
+/* NICAM registers go here */
+#define CXADEC_NICAM_STATUS        0x8C8
+#define CXADEC_DEMATRIX_CTL        0x8CC
+
+#define CXADEC_PATH1_CTL1          0x8D0
+#define CXADEC_PATH1_VOL_CTL       0x8D4
+#define CXADEC_PATH1_EQ_CTL        0x8D8
+#define CXADEC_PATH1_SC_CTL        0x8DC
+
+#define CXADEC_PATH2_CTL1          0x8E0
+#define CXADEC_PATH2_VOL_CTL       0x8E4
+#define CXADEC_PATH2_EQ_CTL        0x8E8
+#define CXADEC_PATH2_SC_CTL        0x8EC
+
+#define CXADEC_SRC_CTL             0x8F0
+#define CXADEC_SRC_LF_COEF         0x8F4
+#define CXADEC_SRC1_CTL            0x8F8
+#define CXADEC_SRC2_CTL            0x8FC
+#define CXADEC_SRC3_CTL            0x900
+#define CXADEC_SRC4_CTL            0x904
+#define CXADEC_SRC5_CTL            0x908
+#define CXADEC_SRC6_CTL            0x90C
+
+#define CXADEC_BASEBAND_OUT_SEL    0x910
+#define CXADEC_I2S_IN_CTL          0x914
+#define CXADEC_I2S_OUT_CTL         0x918
+#define CXADEC_AC97_CTL            0x91C
+#define CXADEC_QAM_PDF             0x920
+#define CXADEC_QAM_CONST_DEC       0x924
+#define CXADEC_QAM_ROTATOR_FREQ    0x948
+
+/* Bit defintions / settings used in Mako Audio */
+#define CXADEC_PREF_MODE_MONO_LANGA        0
+#define CXADEC_PREF_MODE_MONO_LANGB        1
+#define CXADEC_PREF_MODE_MONO_LANGC        2
+#define CXADEC_PREF_MODE_FALLBACK          3
+#define CXADEC_PREF_MODE_STEREO            4
+#define CXADEC_PREF_MODE_DUAL_LANG_AC      5
+#define CXADEC_PREF_MODE_DUAL_LANG_BC      6
+#define CXADEC_PREF_MODE_DUAL_LANG_AB      7
+
+
+#define CXADEC_DETECT_STEREO               1
+#define CXADEC_DETECT_DUAL                 2
+#define CXADEC_DETECT_TRI                  4
+#define CXADEC_DETECT_SAP                  0x10
+#define CXADEC_DETECT_NO_SIGNAL            0xFF
+
+#define CXADEC_SELECT_AUDIO_STANDARD_BG    0xF0  /* NICAM BG and A2 BG */
+#define CXADEC_SELECT_AUDIO_STANDARD_DK1   0xF1  /* NICAM DK and A2 DK */
+#define CXADEC_SELECT_AUDIO_STANDARD_DK2   0xF2
+#define CXADEC_SELECT_AUDIO_STANDARD_DK3   0xF3
+#define CXADEC_SELECT_AUDIO_STANDARD_I     0xF4  /* NICAM I and A1 */
+#define CXADEC_SELECT_AUDIO_STANDARD_L     0xF5  /* NICAM L and System L AM */
+#define CXADEC_SELECT_AUDIO_STANDARD_BTSC  0xF6
+#define CXADEC_SELECT_AUDIO_STANDARD_EIAJ  0xF7
+#define CXADEC_SELECT_AUDIO_STANDARD_A2_M  0xF8  /* A2 M */
+#define CXADEC_SELECT_AUDIO_STANDARD_FM    0xF9  /* FM radio */
+#define CXADEC_SELECT_AUDIO_STANDARD_AUTO  0xFF  /* Auto detect */
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-core.c 							   */
+int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
+int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value);
+u8 cx18_av_read(struct cx18 *cx, u16 addr);
+u32 cx18_av_read4(struct cx18 *cx, u16 addr);
+int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
+int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
+int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg);
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-firmware.c                                                      */
+int cx18_av_loadfw(struct cx18 *cx);
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-audio.c                                                         */
+int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg);
+void cx18_av_audio_set_path(struct cx18 *cx);
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-vbi.c                                                           */
+void cx18_av_vbi_setup(struct cx18 *cx);
+int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg);
+
+#endif
diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c
new file mode 100644
index 0000000..526e142
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-firmware.c
@@ -0,0 +1,120 @@
+/*
+ *  cx18 ADEC firmware functions
+ *
+ *  Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include "cx18-driver.h"
+#include <linux/firmware.h>
+
+#define FWFILE "v4l-cx23418-dig.fw"
+
+int cx18_av_loadfw(struct cx18 *cx)
+{
+	const struct firmware *fw = NULL;
+	u32 size;
+	u32 v;
+	u8 *ptr;
+	int i;
+
+	if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) {
+		CX18_ERR("unable to open firmware %s\n", FWFILE);
+		return -EINVAL;
+	}
+
+	cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000);
+	cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); /* Byte 0 */
+
+	/* Reset the Mako core (Register is undocumented.) */
+	cx18_av_write4(cx, 0x8100, 0x00010000);
+
+	/* Put the 8051 in reset and enable firmware upload */
+	cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000);
+
+	ptr = fw->data;
+	size = fw->size;
+
+	for (i = 0; i < size; i++) {
+		u32 dl_control = 0x0F000000 | ((u32)ptr[i] << 16);
+		u32 value = 0;
+		int retries;
+
+		for (retries = 0; retries < 5; retries++) {
+			cx18_av_write4(cx, CXADEC_DL_CTL, dl_control);
+			value = cx18_av_read4(cx, CXADEC_DL_CTL);
+			if ((value & 0x3F00) == (dl_control & 0x3F00))
+				break;
+		}
+		if (retries >= 5) {
+			CX18_ERR("unable to load firmware %s\n", FWFILE);
+			release_firmware(fw);
+			return -EIO;
+		}
+	}
+
+	cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size);
+
+	/* Output to the 416 */
+	cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
+
+	/* Audio input control 1 set to Sony mode */
+	/* Audio output input 2 is 0 for slave operation input */
+	/* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
+	/* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
+	   after WS transition for first bit of audio word. */
+	cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0);
+
+	/* Audio output control 1 is set to Sony mode */
+	/* Audio output control 2 is set to 1 for master mode */
+	/* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
+	/* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
+	   after WS transition for first bit of audio word. */
+	/* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT
+	   are generated) */
+	cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0);
+
+	/* set alt I2s master clock to /16 and enable alt divider i2s
+	   passthrough */
+	cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687);
+
+	cx18_av_write4(cx, CXADEC_STD_DET_CTL, 0x000000F6);
+	/* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */
+
+	/* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
+	/* Register 0x09CC is defined by the Merlin firmware, and doesn't
+	   have a name in the spec. */
+	cx18_av_write4(cx, 0x09CC, 1);
+
+#define CX18_AUDIO_ENABLE            	0xc72014
+	v = read_reg(CX18_AUDIO_ENABLE);
+	/* If bit 11 is 1 */
+	if (v & 0x800)
+		write_reg(v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); /* Clear bit 10 */
+
+	/* Enable WW auto audio standard detection */
+	v = cx18_av_read4(cx, CXADEC_STD_DET_CTL);
+	v |= 0xFF;   /* Auto by default */
+	v |= 0x400;  /* Stereo by default */
+	v |= 0x14000000;
+	cx18_av_write4(cx, CXADEC_STD_DET_CTL, v);
+
+	release_firmware(fw);
+
+	CX18_INFO("loaded %s firmware (%d bytes)\n", FWFILE, size);
+	return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c
new file mode 100644
index 0000000..d09f1da
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-vbi.c
@@ -0,0 +1,413 @@
+/*
+ *  cx18 ADEC VBI functions
+ *
+ *  Derived from cx25840-vbi.c
+ *
+ *  Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+
+#include "cx18-driver.h"
+
+static int odd_parity(u8 c)
+{
+	c ^= (c >> 4);
+	c ^= (c >> 2);
+	c ^= (c >> 1);
+
+	return c & 1;
+}
+
+static int decode_vps(u8 *dst, u8 *p)
+{
+	static const u8 biphase_tbl[] = {
+		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+		0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+		0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+		0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+		0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+		0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+		0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+		0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
+		0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
+		0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
+		0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
+		0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+		0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+		0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+		0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+		0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
+		0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
+		0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
+		0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
+		0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+		0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+		0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+		0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+		0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+		0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+		0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+		0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+	};
+
+	u8 c, err = 0;
+	int i;
+
+	for (i = 0; i < 2 * 13; i += 2) {
+		err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
+		c = (biphase_tbl[p[i + 1]] & 0xf) |
+		    ((biphase_tbl[p[i]] & 0xf) << 4);
+		dst[i / 2] = c;
+	}
+
+	return err & 0xf0;
+}
+
+void cx18_av_vbi_setup(struct cx18 *cx)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	v4l2_std_id std = state->std;
+	int hblank, hactive, burst, vblank, vactive, sc;
+	int vblank656, src_decimation;
+	int luma_lpf, uv_lpf, comb;
+	u32 pll_int, pll_frac, pll_post;
+
+	/* datasheet startup, step 8d */
+	if (std & ~V4L2_STD_NTSC)
+		cx18_av_write(cx, 0x49f, 0x11);
+	else
+		cx18_av_write(cx, 0x49f, 0x14);
+
+	if (std & V4L2_STD_625_50) {
+		hblank = 0x084;
+		hactive = 0x2d0;
+		burst = 0x5d;
+		vblank = 0x024;
+		vactive = 0x244;
+		vblank656 = 0x28;
+		src_decimation = 0x21f;
+
+		luma_lpf = 2;
+		if (std & V4L2_STD_SECAM) {
+			uv_lpf = 0;
+			comb = 0;
+			sc = 0x0a425f;
+		} else if (std == V4L2_STD_PAL_Nc) {
+			uv_lpf = 1;
+			comb = 0x20;
+			sc = 556453;
+		} else {
+			uv_lpf = 1;
+			comb = 0x20;
+			sc = 0x0a8263;
+		}
+	} else {
+		hactive = 720;
+		hblank = 122;
+		vactive = 487;
+		luma_lpf = 1;
+		uv_lpf = 1;
+
+		src_decimation = 0x21f;
+		if (std == V4L2_STD_PAL_60) {
+			vblank = 26;
+			vblank656 = 26;
+			burst = 0x5b;
+			luma_lpf = 2;
+			comb = 0x20;
+			sc = 0x0a8263;
+		} else if (std == V4L2_STD_PAL_M) {
+			vblank = 20;
+			vblank656 = 24;
+			burst = 0x61;
+			comb = 0x20;
+
+			sc = 555452;
+		} else {
+			vblank = 26;
+			vblank656 = 26;
+			burst = 0x5b;
+			comb = 0x66;
+			sc = 556063;
+		}
+	}
+
+	/* DEBUG: Displays configured PLL frequency */
+	pll_int = cx18_av_read(cx, 0x108);
+	pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff;
+	pll_post = cx18_av_read(cx, 0x109);
+	CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n",
+			pll_int, pll_frac, pll_post);
+
+	if (pll_post) {
+		int fin, fsc;
+		int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac);
+
+		pll >>= 25;
+		pll /= pll_post;
+		CX18_DEBUG_INFO("PLL = %d.%06d MHz\n",
+					pll / 1000000, pll % 1000000);
+		CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n",
+					pll / 8000000, (pll / 8) % 1000000);
+
+		fin = ((u64)src_decimation * pll) >> 12;
+		CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n",
+					fin / 1000000, fin % 1000000);
+
+		fsc = (((u64)sc) * pll) >> 24L;
+		CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n",
+					fsc / 1000000, fsc % 1000000);
+
+		CX18_DEBUG_INFO("hblank %i, hactive %i, "
+			"vblank %i , vactive %i, vblank656 %i, src_dec %i,"
+			"burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x,"
+			" sc 0x%06x\n",
+			hblank, hactive, vblank, vactive, vblank656,
+			src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
+	}
+
+	/* Sets horizontal blanking delay and active lines */
+	cx18_av_write(cx, 0x470, hblank);
+	cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) |
+						(hactive << 4)));
+	cx18_av_write(cx, 0x472, hactive >> 4);
+
+	/* Sets burst gate delay */
+	cx18_av_write(cx, 0x473, burst);
+
+	/* Sets vertical blanking delay and active duration */
+	cx18_av_write(cx, 0x474, vblank);
+	cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) |
+						(vactive << 4)));
+	cx18_av_write(cx, 0x476, vactive >> 4);
+	cx18_av_write(cx, 0x477, vblank656);
+
+	/* Sets src decimation rate */
+	cx18_av_write(cx, 0x478, 0xff & src_decimation);
+	cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8));
+
+	/* Sets Luma and UV Low pass filters */
+	cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30));
+
+	/* Enables comb filters */
+	cx18_av_write(cx, 0x47b, comb);
+
+	/* Sets SC Step*/
+	cx18_av_write(cx, 0x47c, sc);
+	cx18_av_write(cx, 0x47d, 0xff & sc >> 8);
+	cx18_av_write(cx, 0x47e, 0xff & sc >> 16);
+
+	/* Sets VBI parameters */
+	if (std & V4L2_STD_625_50) {
+		cx18_av_write(cx, 0x47f, 0x01);
+		state->vbi_line_offset = 5;
+	} else {
+		cx18_av_write(cx, 0x47f, 0x00);
+		state->vbi_line_offset = 8;
+	}
+}
+
+int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+	struct cx18_av_state *state = &cx->av_state;
+	struct v4l2_format *fmt;
+	struct v4l2_sliced_vbi_format *svbi;
+
+	switch (cmd) {
+	case VIDIOC_G_FMT:
+	{
+		static u16 lcr2vbi[] = {
+			0, V4L2_SLICED_TELETEXT_B, 0,	/* 1 */
+			0, V4L2_SLICED_WSS_625, 0,	/* 4 */
+			V4L2_SLICED_CAPTION_525,	/* 6 */
+			0, 0, V4L2_SLICED_VPS, 0, 0,	/* 9 */
+			0, 0, 0, 0
+		};
+		int is_pal = !(state->std & V4L2_STD_525_60);
+		int i;
+
+		fmt = arg;
+		if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+			return -EINVAL;
+		svbi = &fmt->fmt.sliced;
+		memset(svbi, 0, sizeof(*svbi));
+		/* we're done if raw VBI is active */
+		if ((cx18_av_read(cx, 0x404) & 0x10) == 0)
+			break;
+
+		if (is_pal) {
+			for (i = 7; i <= 23; i++) {
+				u8 v = cx18_av_read(cx, 0x424 + i - 7);
+
+				svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+				svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+				svbi->service_set |= svbi->service_lines[0][i] |
+					svbi->service_lines[1][i];
+			}
+		} else {
+			for (i = 10; i <= 21; i++) {
+				u8 v = cx18_av_read(cx, 0x424 + i - 10);
+
+				svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+				svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+				svbi->service_set |= svbi->service_lines[0][i] |
+					svbi->service_lines[1][i];
+			}
+		}
+		break;
+	}
+
+	case VIDIOC_S_FMT:
+	{
+		int is_pal = !(state->std & V4L2_STD_525_60);
+		int vbi_offset = is_pal ? 1 : 0;
+		int i, x;
+		u8 lcr[24];
+
+		fmt = arg;
+		if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+			return -EINVAL;
+		svbi = &fmt->fmt.sliced;
+		if (svbi->service_set == 0) {
+			/* raw VBI */
+			memset(svbi, 0, sizeof(*svbi));
+
+			/* Setup VBI */
+			cx18_av_vbi_setup(cx);
+
+			/* VBI Offset */
+			cx18_av_write(cx, 0x47f, vbi_offset);
+			cx18_av_write(cx, 0x404, 0x2e);
+			break;
+		}
+
+		for (x = 0; x <= 23; x++)
+			lcr[x] = 0x00;
+
+		/* Setup VBI */
+		cx18_av_vbi_setup(cx);
+
+		/* Sliced VBI */
+		cx18_av_write(cx, 0x404, 0x32);	/* Ancillary data */
+		cx18_av_write(cx, 0x406, 0x13);
+		cx18_av_write(cx, 0x47f, vbi_offset);
+
+		if (is_pal) {
+			for (i = 0; i <= 6; i++)
+				svbi->service_lines[0][i] =
+					svbi->service_lines[1][i] = 0;
+		} else {
+			for (i = 0; i <= 9; i++)
+				svbi->service_lines[0][i] =
+					svbi->service_lines[1][i] = 0;
+
+			for (i = 22; i <= 23; i++)
+				svbi->service_lines[0][i] =
+					svbi->service_lines[1][i] = 0;
+		}
+
+		for (i = 7; i <= 23; i++) {
+			for (x = 0; x <= 1; x++) {
+				switch (svbi->service_lines[1-x][i]) {
+				case V4L2_SLICED_TELETEXT_B:
+					lcr[i] |= 1 << (4 * x);
+					break;
+				case V4L2_SLICED_WSS_625:
+					lcr[i] |= 4 << (4 * x);
+					break;
+				case V4L2_SLICED_CAPTION_525:
+					lcr[i] |= 6 << (4 * x);
+					break;
+				case V4L2_SLICED_VPS:
+					lcr[i] |= 9 << (4 * x);
+					break;
+				}
+			}
+		}
+
+		if (is_pal) {
+			for (x = 1, i = 0x424; i <= 0x434; i++, x++)
+				cx18_av_write(cx, i, lcr[6 + x]);
+		} else {
+			for (x = 1, i = 0x424; i <= 0x430; i++, x++)
+				cx18_av_write(cx, i, lcr[9 + x]);
+			for (i = 0x431; i <= 0x434; i++)
+				cx18_av_write(cx, i, 0);
+		}
+
+		cx18_av_write(cx, 0x43c, 0x16);
+		cx18_av_write(cx, 0x474, is_pal ? 0x2a : 0x22);
+		break;
+	}
+
+	case VIDIOC_INT_DECODE_VBI_LINE:
+	{
+		struct v4l2_decode_vbi_line *vbi = arg;
+		u8 *p = vbi->p;
+		int id1, id2, l, err = 0;
+
+		if (p[0] || p[1] != 0xff || p[2] != 0xff ||
+		    (p[3] != 0x55 && p[3] != 0x91)) {
+			vbi->line = vbi->type = 0;
+			break;
+		}
+
+		p += 4;
+		id1 = p[-1];
+		id2 = p[0] & 0xf;
+		l = p[2] & 0x3f;
+		l += state->vbi_line_offset;
+		p += 4;
+
+		switch (id2) {
+		case 1:
+			id2 = V4L2_SLICED_TELETEXT_B;
+			break;
+		case 4:
+			id2 = V4L2_SLICED_WSS_625;
+			break;
+		case 6:
+			id2 = V4L2_SLICED_CAPTION_525;
+			err = !odd_parity(p[0]) || !odd_parity(p[1]);
+			break;
+		case 9:
+			id2 = V4L2_SLICED_VPS;
+			if (decode_vps(p, p) != 0)
+				err = 1;
+			break;
+		default:
+			id2 = 0;
+			err = 1;
+			break;
+		}
+
+		vbi->type = err ? 0 : id2;
+		vbi->line = err ? 0 : l;
+		vbi->is_second_field = err ? 0 : (id1 == 0x55);
+		vbi->p = p;
+		break;
+	}
+	}
+
+	return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
new file mode 100644
index 0000000..f5e3ba1
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -0,0 +1,277 @@
+/*
+ *  cx18 functions to query card hardware
+ *
+ *  Derived from ivtv-cards.c
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-cards.h"
+#include "cx18-i2c.h"
+#include <media/cs5345.h>
+
+/********************** card configuration *******************************/
+
+/* usual i2c tuner addresses to probe */
+static struct cx18_card_tuner_i2c cx18_i2c_std = {
+	.radio = { I2C_CLIENT_END },
+	.demod = { 0x43, I2C_CLIENT_END },
+	.tv    = { 0x61, 0x60, I2C_CLIENT_END },
+};
+
+/* 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 HVR-1600 cards */
+
+/* Note: for Hauppauge cards the tveeprom information is used instead
+   of PCI IDs */
+static const struct cx18_card cx18_card_hvr1600_esmt = {
+	.type = CX18_CARD_HVR_1600_ESMT,
+	.name = "Hauppauge HVR-1600",
+	.comment = "DVB & VBI are not yet supported\n",
+	.v4l2_capabilities = CX18_CAP_ENCODER,
+	.hw_audio_ctrl = CX18_HW_CX23418,
+	.hw_muxer = CX18_HW_CS5345,
+	.hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | CX18_HW_CS5345,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX23418_COMPOSITE7 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1, CX23418_SVIDEO1    },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
+		{ CX18_CARD_INPUT_SVIDEO2,    2, CX23418_SVIDEO2    },
+		{ CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER,
+		  CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
+		{ CX18_CARD_INPUT_LINE_IN1,
+		  CX23418_AUDIO_SERIAL, CS5345_IN_2 },
+		{ CX18_CARD_INPUT_LINE_IN2,
+		  CX23418_AUDIO_SERIAL, CS5345_IN_2 },
+	},
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+			 CX23418_AUDIO_SERIAL, 0 },
+	.ddr = {
+		/* ESMT M13S128324A-5B memory */
+		.chip_config = 0x003,
+		.refresh = 0x30c,
+		.timing1 = 0x44220e82,
+		.timing2 = 0x08,
+		.tune_lane = 0,
+		.initial_emrs = 0,
+	},
+	.gpio_init.initial_value = 0x3001,
+	.gpio_init.direction = 0x3001,
+	.i2c = &cx18_i2c_std,
+};
+
+static const struct cx18_card cx18_card_hvr1600_samsung = {
+	.type = CX18_CARD_HVR_1600_SAMSUNG,
+	.name = "Hauppauge HVR-1600 (Preproduction)",
+	.comment = "DVB & VBI are not yet supported\n",
+	.v4l2_capabilities = CX18_CAP_ENCODER,
+	.hw_audio_ctrl = CX18_HW_CX23418,
+	.hw_muxer = CX18_HW_CS5345,
+	.hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | CX18_HW_CS5345,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX23418_COMPOSITE7 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1, CX23418_SVIDEO1    },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
+		{ CX18_CARD_INPUT_SVIDEO2,    2, CX23418_SVIDEO2    },
+		{ CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER,
+		  CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
+		{ CX18_CARD_INPUT_LINE_IN1,
+		  CX23418_AUDIO_SERIAL, CS5345_IN_2 },
+		{ CX18_CARD_INPUT_LINE_IN2,
+		  CX23418_AUDIO_SERIAL, CS5345_IN_2 },
+	},
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+			 CX23418_AUDIO_SERIAL, 0 },
+	.ddr = {
+		/* Samsung K4D263238G-VC33 memory */
+		.chip_config = 0x003,
+		.refresh = 0x30c,
+		.timing1 = 0x23230b73,
+		.timing2 = 0x08,
+		.tune_lane = 0,
+		.initial_emrs = 2,
+	},
+	.gpio_init.initial_value = 0x3001,
+	.gpio_init.direction = 0x3001,
+	.i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Compro VideoMate H900: not working at the moment! */
+
+static const struct cx18_card_pci_info cx18_pci_h900[] = {
+	{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_COMPRO, 0xe100 },
+	{ 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_h900 = {
+	.type = CX18_CARD_COMPRO_H900,
+	.name = "Compro VideoMate H900",
+	.comment = "Not yet supported!\n",
+	.v4l2_capabilities = 0,
+	.hw_audio_ctrl = CX18_HW_CX23418,
+	.hw_all = CX18_HW_TUNER,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX23418_COMPOSITE7 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1, CX23418_SVIDEO1    },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER,
+		  CX23418_AUDIO8, 0 },
+		{ CX18_CARD_INPUT_LINE_IN1,
+		  CX23418_AUDIO_SERIAL, 0 },
+	},
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+			 CX23418_AUDIO_SERIAL, 0 },
+	.tuners = {
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	.ddr = {
+		/* EtronTech EM6A9160TS-5G memory */
+		.chip_config = 0x50003,
+		.refresh = 0x753,
+		.timing1 = 0x24330e84,
+		.timing2 = 0x1f,
+		.tune_lane = 0,
+		.initial_emrs = 0,
+	},
+	.pci_list = cx18_pci_h900,
+	.i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPC718: not working at the moment! */
+
+static const struct cx18_card_pci_info cx18_pci_mpc718[] = {
+	{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_YUAN, 0x0718 },
+	{ 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_mpc718 = {
+	.type = CX18_CARD_YUAN_MPC718,
+	.name = "Yuan MPC718",
+	.comment = "Not yet supported!\n",
+	.v4l2_capabilities = 0,
+	.hw_audio_ctrl = CX18_HW_CX23418,
+	.hw_all = CX18_HW_TUNER,
+	.video_inputs = {
+		{ CX18_CARD_INPUT_VID_TUNER,  0, CX23418_COMPOSITE7 },
+		{ CX18_CARD_INPUT_SVIDEO1,    1, CX23418_SVIDEO1    },
+		{ CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
+	},
+	.audio_inputs = {
+		{ CX18_CARD_INPUT_AUD_TUNER,
+		  CX23418_AUDIO8, 0 },
+		{ CX18_CARD_INPUT_LINE_IN1,
+		  CX23418_AUDIO_SERIAL, 0 },
+	},
+	.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+			 CX23418_AUDIO_SERIAL, 0 },
+	.tuners = {
+		/* XC3028 tuner */
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	/* tuner reset */
+	.gpio_init = { .direction = 0x1000, .initial_value = 0x1000 },
+	.ddr = {
+		/* Probably Samsung K4D263238G-VC33 memory */
+		.chip_config = 0x003,
+		.refresh = 0x30c,
+		.timing1 = 0x23230b73,
+		.timing2 = 0x08,
+		.tune_lane = 0,
+		.initial_emrs = 2,
+	},
+	.pci_list = cx18_pci_mpc718,
+	.i2c = &cx18_i2c_std,
+};
+
+static const struct cx18_card *cx18_card_list[] = {
+	&cx18_card_hvr1600_esmt,
+	&cx18_card_hvr1600_samsung,
+	&cx18_card_h900,
+	&cx18_card_mpc718,
+};
+
+const struct cx18_card *cx18_get_card(u16 index)
+{
+	if (index >= ARRAY_SIZE(cx18_card_list))
+		return NULL;
+	return cx18_card_list[index];
+}
+
+int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input)
+{
+	const struct cx18_card_video_input *card_input =
+		cx->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 >= cx->nof_inputs)
+		return -EINVAL;
+	input->index = index;
+	strlcpy(input->name, input_strs[card_input->video_type - 1],
+			sizeof(input->name));
+	input->type = (card_input->video_type == CX18_CARD_INPUT_VID_TUNER ?
+			V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
+	input->audioset = (1 << cx->nof_audio_inputs) - 1;
+	input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ?
+				cx->tuner_std : V4L2_STD_ALL;
+	return 0;
+}
+
+int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *audio)
+{
+	const struct cx18_card_audio_input *aud_input =
+		cx->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 >= cx->nof_audio_inputs)
+		return -EINVAL;
+	strlcpy(audio->name, input_strs[aud_input->audio_type - 1],
+			sizeof(audio->name));
+	audio->index = index;
+	audio->capability = V4L2_AUDCAP_STEREO;
+	return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h
new file mode 100644
index 0000000..bca249b
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-cards.h
@@ -0,0 +1,170 @@
+/*
+ *  cx18 functions to query card hardware
+ *
+ *  Derived from ivtv-cards.c
+ *
+ *  Copyright (C) 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 CX18_HW_TUNER     (1 << 0)
+#define CX18_HW_TVEEPROM  (1 << 1)
+#define CX18_HW_CS5345    (1 << 2)
+#define CX18_HW_GPIO      (1 << 3)
+#define CX18_HW_CX23418   (1 << 4)
+#define CX18_HW_DVB   	  (1 << 5)
+
+/* video inputs */
+#define	CX18_CARD_INPUT_VID_TUNER	1
+#define	CX18_CARD_INPUT_SVIDEO1 	2
+#define	CX18_CARD_INPUT_SVIDEO2 	3
+#define	CX18_CARD_INPUT_COMPOSITE1 	4
+#define	CX18_CARD_INPUT_COMPOSITE2 	5
+#define	CX18_CARD_INPUT_COMPOSITE3 	6
+
+enum cx34180_video_input {
+	/* Composite video inputs In1-In8 */
+	CX23418_COMPOSITE1 = 1,
+	CX23418_COMPOSITE2,
+	CX23418_COMPOSITE3,
+	CX23418_COMPOSITE4,
+	CX23418_COMPOSITE5,
+	CX23418_COMPOSITE6,
+	CX23418_COMPOSITE7,
+	CX23418_COMPOSITE8,
+
+	/* S-Video inputs consist of one luma input (In1-In4) ORed with one
+	   chroma input (In5-In8) */
+	CX23418_SVIDEO_LUMA1 = 0x10,
+	CX23418_SVIDEO_LUMA2 = 0x20,
+	CX23418_SVIDEO_LUMA3 = 0x30,
+	CX23418_SVIDEO_LUMA4 = 0x40,
+	CX23418_SVIDEO_CHROMA4 = 0x400,
+	CX23418_SVIDEO_CHROMA5 = 0x500,
+	CX23418_SVIDEO_CHROMA6 = 0x600,
+	CX23418_SVIDEO_CHROMA7 = 0x700,
+	CX23418_SVIDEO_CHROMA8 = 0x800,
+
+	/* S-Video aliases for common luma/chroma combinations */
+	CX23418_SVIDEO1 = 0x510,
+	CX23418_SVIDEO2 = 0x620,
+	CX23418_SVIDEO3 = 0x730,
+	CX23418_SVIDEO4 = 0x840,
+};
+
+/* audio inputs */
+#define	CX18_CARD_INPUT_AUD_TUNER	1
+#define	CX18_CARD_INPUT_LINE_IN1 	2
+#define	CX18_CARD_INPUT_LINE_IN2 	3
+
+#define CX18_CARD_MAX_VIDEO_INPUTS 6
+#define CX18_CARD_MAX_AUDIO_INPUTS 3
+#define CX18_CARD_MAX_TUNERS  	   2
+
+enum cx23418_audio_input {
+	/* Audio inputs: serial or In4-In8 */
+	CX23418_AUDIO_SERIAL,
+	CX23418_AUDIO4 = 4,
+	CX23418_AUDIO5,
+	CX23418_AUDIO6,
+	CX23418_AUDIO7,
+	CX23418_AUDIO8,
+};
+
+/* V4L2 capability aliases */
+#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
+			  V4L2_CAP_AUDIO | V4L2_CAP_READWRITE)
+/* | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */
+
+struct cx18_card_video_input {
+	u8  video_type; 	/* video input type */
+	u8  audio_index;	/* index in cx18_card_audio_input array */
+	u16 video_input;	/* hardware video input */
+};
+
+struct cx18_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 cx18_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 cx18_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 cx18_card_tuner {
+	v4l2_std_id std; 	/* standard for which the tuner is suitable */
+	int 	    tuner; 	/* tuner ID (from tuner.h) */
+};
+
+struct cx18_card_tuner_i2c {
+	unsigned short radio[2];/* radio tuner i2c address to probe */
+	unsigned short demod[2];/* demodulator i2c address to probe */
+	unsigned short tv[4];	/* tv tuner i2c addresses to probe */
+};
+
+struct cx18_ddr {		/* DDR config data */
+	u32 chip_config;
+	u32 refresh;
+	u32 timing1;
+	u32 timing2;
+	u32 tune_lane;
+	u32 initial_emrs;
+};
+
+/* for card information/parameters */
+struct cx18_card {
+	int type;
+	char *name;
+	char *comment;
+	u32 v4l2_capabilities;
+	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 cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS];
+	struct cx18_card_audio_input audio_inputs[CX18_CARD_MAX_AUDIO_INPUTS];
+	struct cx18_card_audio_input radio_input;
+
+	/* GPIO card-specific settings */
+	struct cx18_gpio_init 		gpio_init;
+
+	struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS];
+	struct cx18_card_tuner_i2c *i2c;
+
+	struct cx18_ddr ddr;
+
+	/* list of device and subsystem vendor/devices that
+	   correspond to this card type. */
+	const struct cx18_card_pci_info *pci_list;
+};
+
+int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input);
+int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *input);
+const struct cx18_card *cx18_get_card(u16 index);
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
new file mode 100644
index 0000000..2bdac5e
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -0,0 +1,306 @@
+/*
+ *  cx18 ioctl control functions
+ *
+ *  Derived from ivtv-controls.c
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-av-core.h"
+#include "cx18-cards.h"
+#include "cx18-ioctl.h"
+#include "cx18-audio.h"
+#include "cx18-i2c.h"
+#include "cx18-mailbox.h"
+#include "cx18-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 cx18_queryctrl(struct cx18 *cx, struct v4l2_queryctrl *qctrl)
+{
+	const char *name;
+
+	CX18_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 (cx18_av_cmd(cx, 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 (cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
+			qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+		return 0;
+
+	default:
+		if (cx2341x_ctrl_query(&cx->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 cx18_querymenu(struct cx18 *cx, struct v4l2_querymenu *qmenu)
+{
+	struct v4l2_queryctrl qctrl;
+
+	qctrl.id = qmenu->id;
+	cx18_queryctrl(cx, &qctrl);
+	return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
+}
+
+static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
+{
+	s32 v = vctrl->value;
+
+	CX18_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 cx18_av_cmd(cx, 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 cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
+
+	default:
+		CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
+{
+	CX18_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 cx18_av_cmd(cx, 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 cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
+	default:
+		CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt)
+{
+	if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
+		return -EINVAL;
+	if (atomic_read(&cx->capturing) > 0)
+		return -EBUSY;
+
+	/* First try to allocate sliced VBI buffers if needed. */
+	if (fmt && cx->vbi.sliced_mpeg_data[0] == NULL) {
+		int i;
+
+		for (i = 0; i < CX18_VBI_FRAMES; i++) {
+			/* Yuck, hardcoded. Needs to be a define */
+			cx->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL);
+			if (cx->vbi.sliced_mpeg_data[i] == NULL) {
+				while (--i >= 0) {
+					kfree(cx->vbi.sliced_mpeg_data[i]);
+					cx->vbi.sliced_mpeg_data[i] = NULL;
+				}
+				return -ENOMEM;
+			}
+		}
+	}
+
+	cx->vbi.insert_mpeg = fmt;
+
+	if (cx->vbi.insert_mpeg == 0)
+		return 0;
+	/* Need sliced data for mpeg insertion */
+	if (cx18_get_service_set(cx->vbi.sliced_in) == 0) {
+		if (cx->is_60hz)
+			cx->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
+		else
+			cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
+		cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz);
+	}
+	return 0;
+}
+
+int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+	struct v4l2_control ctrl;
+
+	switch (cmd) {
+	case VIDIOC_QUERYMENU:
+		CX18_DEBUG_IOCTL("VIDIOC_QUERYMENU\n");
+		return cx18_querymenu(cx, arg);
+
+	case VIDIOC_QUERYCTRL:
+		return cx18_queryctrl(cx, arg);
+
+	case VIDIOC_S_CTRL:
+		return cx18_s_ctrl(cx, arg);
+
+	case VIDIOC_G_CTRL:
+		return cx18_g_ctrl(cx, 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 = cx18_s_ctrl(cx, &ctrl);
+				c->controls[i].value = ctrl.value;
+				if (err) {
+					c->error_idx = i;
+					break;
+				}
+			}
+			return err;
+		}
+		CX18_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n");
+		if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+			struct cx2341x_mpeg_params p = cx->params;
+			int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->capturing), arg, cmd);
+
+			if (err)
+				return err;
+
+			if (p.video_encoding != cx->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 = cx->params.width / (is_mpeg1 ? 2 : 1);
+				fmt.fmt.pix.height = cx->params.height;
+				cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
+			}
+			err = cx2341x_update(cx, cx18_api_func, &cx->params, &p);
+			if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt)
+				err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
+			cx->params = p;
+			cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
+			cx18_audio_set_audio_clock_freq(cx, 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 = cx18_g_ctrl(cx, &ctrl);
+				c->controls[i].value = ctrl.value;
+				if (err) {
+					c->error_idx = i;
+					break;
+				}
+			}
+			return err;
+		}
+		CX18_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n");
+		if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+			return cx2341x_ext_ctrls(&cx->params, 0, arg, cmd);
+		return -EINVAL;
+	}
+
+	case VIDIOC_TRY_EXT_CTRLS:
+	{
+		struct v4l2_ext_controls *c = arg;
+
+		CX18_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n");
+		if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+			return cx2341x_ext_ctrls(&cx->params,
+					atomic_read(&cx->capturing), arg, cmd);
+		return -EINVAL;
+	}
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-controls.h b/drivers/media/video/cx18/cx18-controls.h
new file mode 100644
index 0000000..6e985cf
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-controls.h
@@ -0,0 +1,24 @@
+/*
+ *  cx18 ioctl control functions
+ *
+ *  Derived from ivtv-controls.h
+ *
+ *  Copyright (C) 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 cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg);
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
new file mode 100644
index 0000000..9f31bef
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -0,0 +1,971 @@
+/*
+ *  cx18 driver initialization and card probing
+ *
+ *  Derived from ivtv-driver.c
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-version.h"
+#include "cx18-cards.h"
+#include "cx18-i2c.h"
+#include "cx18-irq.h"
+#include "cx18-gpio.h"
+#include "cx18-firmware.h"
+#include "cx18-streams.h"
+#include "cx18-av-core.h"
+#include "cx18-scb.h"
+#include "cx18-mailbox.h"
+#include "cx18-ioctl.h"
+#include "tuner-xc2028.h"
+
+#include <media/tveeprom.h>
+
+
+/* var to keep track of the number of array elements in use */
+int cx18_cards_active;
+
+/* 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 Compro H900 with. Normally this would give a
+   video1 device together with a radio0 device for the Compro. By
+   setting this to 1 you ensure that radio0 is now also radio1. */
+int cx18_first_minor;
+
+/* Master variable for all cx18 info */
+struct cx18 *cx18_cards[CX18_MAX_CARDS];
+
+/* Protects cx18_cards_active */
+DEFINE_SPINLOCK(cx18_cards_lock);
+
+/* add your revision and whatnot here */
+static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
+	{PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, cx18_pci_tbl);
+
+/* Parameter declarations */
+static int cardtype[CX18_MAX_CARDS];
+static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1 };
+static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -1, -1, -1, -1,
+				     -1, -1, -1, -1, -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 = CX18_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
+static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
+static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
+static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
+
+static int cx18_pci_latency = 1;
+
+int cx18_debug;
+
+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, cx18_debug, int, 0644);
+module_param(cx18_pci_latency, int, 0644);
+module_param(cx18_first_minor, int, 0644);
+
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_ts_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_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 = Hauppauge HVR 1600 (ESMT memory)\n"
+		 "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n"
+		 "\t\t\t 3 = Compro VideoMate H900\n"
+		 "\t\t\t 4 = Yuan MPC718\n"
+		 "\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: 0\n"
+		 "\t\t\t  1/0x0001: warning\n"
+		 "\t\t\t  2/0x0002: info\n"
+		 "\t\t\t  4/0x0004: mailbox\n"
+		 "\t\t\t  8/0x0008: dma\n"
+		 "\t\t\t 16/0x0010: ioctl\n"
+		 "\t\t\t 32/0x0020: file\n"
+		 "\t\t\t 64/0x0040: i2c\n"
+		 "\t\t\t128/0x0080: irq\n"
+		 "\t\t\t256/0x0100: high volume\n");
+MODULE_PARM_DESC(cx18_pci_latency,
+		 "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+		 "\t\t\tDefault: Yes");
+MODULE_PARM_DESC(enc_mpg_buffers,
+		 "Encoder MPG Buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_ts_buffers,
+		 "Encoder TS Buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_buffers,
+		 "Encoder YUV Buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_buffers,
+		 "Encoder VBI Buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_buffers,
+		 "Encoder PCM buffers (in MB)\n"
+		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
+
+MODULE_PARM_DESC(cx18_first_minor, "Set minor assigned to first card");
+
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_DESCRIPTION("CX23418 driver");
+MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(CX18_VERSION);
+
+int cx18_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 cx18_msleep_timeout(unsigned int msecs, int intr)
+{
+	int timeout = msecs_to_jiffies(msecs);
+	int sig;
+
+	do {
+		set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+		timeout = schedule_timeout(timeout);
+		sig = intr ? signal_pending(current) : 0;
+	} while (!sig && timeout);
+	return sig;
+}
+
+/* Release ioremapped memory */
+static void cx18_iounmap(struct cx18 *cx)
+{
+	if (cx == NULL)
+		return;
+
+	/* Release io memory */
+	if (cx->enc_mem != NULL) {
+		CX18_DEBUG_INFO("releasing enc_mem\n");
+		iounmap(cx->enc_mem);
+		cx->enc_mem = NULL;
+	}
+}
+
+/* Hauppauge card? get values from tveeprom */
+void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
+{
+	u8 eedata[256];
+
+	cx->i2c_client[0].addr = 0xA0 >> 1;
+	tveeprom_read(&cx->i2c_client[0], eedata, sizeof(eedata));
+	tveeprom_hauppauge_analog(&cx->i2c_client[0], tv, eedata);
+}
+
+static void cx18_process_eeprom(struct cx18 *cx)
+{
+	struct tveeprom tv;
+
+	cx18_read_eeprom(cx, &tv);
+
+	/* Many thanks to Steven Toth from Hauppauge for providing the
+	   model numbers */
+	switch (tv.model) {
+	case 74000 ... 74099:
+		cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+		break;
+	case 74700 ... 74799:
+		cx->card = cx18_get_card(CX18_CARD_HVR_1600_SAMSUNG);
+		break;
+	case 0:
+		CX18_ERR("Invalid EEPROM\n");
+		return;
+	default:
+		CX18_ERR("Unknown model %d, defaulting to HVR-1600\n", tv.model);
+		cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+		break;
+	}
+
+	cx->v4l2_cap = cx->card->v4l2_capabilities;
+	cx->card_name = cx->card->name;
+	cx->card_i2c = cx->card->i2c;
+
+	CX18_INFO("Autodetected %s\n", cx->card_name);
+
+	if (tv.tuner_type == TUNER_ABSENT)
+		CX18_ERR("tveeprom cannot autodetect tuner!");
+
+	if (cx->options.tuner == -1)
+		cx->options.tuner = tv.tuner_type;
+	if (cx->options.radio == -1)
+		cx->options.radio = (tv.has_radio != 0);
+
+	if (cx->std != 0)
+		/* user specified tuner standard */
+		return;
+
+	/* autodetect tuner standard */
+	if (tv.tuner_formats & V4L2_STD_PAL) {
+		CX18_DEBUG_INFO("PAL tuner detected\n");
+		cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+	} else if (tv.tuner_formats & V4L2_STD_NTSC) {
+		CX18_DEBUG_INFO("NTSC tuner detected\n");
+		cx->std |= V4L2_STD_NTSC_M;
+	} else if (tv.tuner_formats & V4L2_STD_SECAM) {
+		CX18_DEBUG_INFO("SECAM tuner detected\n");
+		cx->std |= V4L2_STD_SECAM_L;
+	} else {
+		CX18_INFO("No tuner detected, default to NTSC-M\n");
+		cx->std |= V4L2_STD_NTSC_M;
+	}
+}
+
+static v4l2_std_id cx18_parse_std(struct cx18 *cx)
+{
+	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:
+		CX18_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:
+		CX18_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:
+		CX18_WARN("ntsc= argument not recognised\n");
+		return 0;
+	}
+
+	/* no match found */
+	return 0;
+}
+
+static void cx18_process_options(struct cx18 *cx)
+{
+	int i, j;
+
+	cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+	cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
+	cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
+	cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
+	cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+	cx->options.cardtype = cardtype[cx->num];
+	cx->options.tuner = tuner[cx->num];
+	cx->options.radio = radio[cx->num];
+
+	cx->std = cx18_parse_std(cx);
+	if (cx->options.cardtype == -1) {
+		CX18_INFO("Ignore card\n");
+		return;
+	}
+	cx->card = cx18_get_card(cx->options.cardtype - 1);
+	if (cx->card)
+		CX18_INFO("User specified %s card\n", cx->card->name);
+	else if (cx->options.cardtype != 0)
+		CX18_ERR("Unknown user specified type, trying to autodetect card\n");
+	if (cx->card == NULL) {
+		if (cx->dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
+			cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+			CX18_INFO("Autodetected Hauppauge card\n");
+		}
+	}
+	if (cx->card == NULL) {
+		for (i = 0; (cx->card = cx18_get_card(i)); i++) {
+			if (cx->card->pci_list == NULL)
+				continue;
+			for (j = 0; cx->card->pci_list[j].device; j++) {
+				if (cx->dev->device !=
+				    cx->card->pci_list[j].device)
+					continue;
+				if (cx->dev->subsystem_vendor !=
+				    cx->card->pci_list[j].subsystem_vendor)
+					continue;
+				if (cx->dev->subsystem_device !=
+				    cx->card->pci_list[j].subsystem_device)
+					continue;
+				CX18_INFO("Autodetected %s card\n", cx->card->name);
+				goto done;
+			}
+		}
+	}
+done:
+
+	if (cx->card == NULL) {
+		cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+		CX18_ERR("Unknown card: vendor/device: %04x/%04x\n",
+		     cx->dev->vendor, cx->dev->device);
+		CX18_ERR("              subsystem vendor/device: %04x/%04x\n",
+		     cx->dev->subsystem_vendor, cx->dev->subsystem_device);
+		CX18_ERR("Defaulting to %s card\n", cx->card->name);
+		CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+		CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+		CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n");
+	}
+	cx->v4l2_cap = cx->card->v4l2_capabilities;
+	cx->card_name = cx->card->name;
+	cx->card_i2c = cx->card->i2c;
+}
+
+/* Precondition: the cx18 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 cx18_init_struct2
+   for that).
+ */
+static int __devinit cx18_init_struct1(struct cx18 *cx)
+{
+	cx->base_addr = pci_resource_start(cx->dev, 0);
+
+	mutex_init(&cx->serialize_lock);
+	mutex_init(&cx->i2c_bus_lock[0]);
+	mutex_init(&cx->i2c_bus_lock[1]);
+
+	spin_lock_init(&cx->lock);
+	spin_lock_init(&cx->dma_reg_lock);
+
+	/* start counting open_id at 1 */
+	cx->open_id = 1;
+
+	/* Initial settings */
+	cx2341x_fill_defaults(&cx->params);
+	cx->temporal_strength = cx->params.video_temporal_filter;
+	cx->spatial_strength = cx->params.video_spatial_filter;
+	cx->filter_mode = cx->params.video_spatial_filter_mode |
+		(cx->params.video_temporal_filter_mode << 1) |
+		(cx->params.video_median_filter_type << 2);
+	cx->params.port = CX2341X_PORT_MEMORY;
+	cx->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
+	init_waitqueue_head(&cx->cap_w);
+	init_waitqueue_head(&cx->mb_apu_waitq);
+	init_waitqueue_head(&cx->mb_cpu_waitq);
+	init_waitqueue_head(&cx->mb_epu_waitq);
+	init_waitqueue_head(&cx->mb_hpu_waitq);
+	init_waitqueue_head(&cx->dma_waitq);
+
+	/* VBI */
+	cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+	cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
+	cx->vbi.raw_size = 1456;
+	cx->vbi.raw_decoder_line_size = 1456;
+	cx->vbi.raw_decoder_sav_odd_field = 0x20;
+	cx->vbi.raw_decoder_sav_even_field = 0x60;
+	cx->vbi.sliced_decoder_line_size = 272;
+	cx->vbi.sliced_decoder_sav_odd_field = 0xB0;
+	cx->vbi.sliced_decoder_sav_even_field = 0xF0;
+	return 0;
+}
+
+/* Second initialization part. Here the card type has been
+   autodetected. */
+static void __devinit cx18_init_struct2(struct cx18 *cx)
+{
+	int i;
+
+	for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++)
+		if (cx->card->video_inputs[i].video_type == 0)
+			break;
+	cx->nof_inputs = i;
+	for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++)
+		if (cx->card->audio_inputs[i].audio_type == 0)
+			break;
+	cx->nof_audio_inputs = i;
+
+	/* Find tuner input */
+	for (i = 0; i < cx->nof_inputs; i++) {
+		if (cx->card->video_inputs[i].video_type ==
+				CX18_CARD_INPUT_VID_TUNER)
+			break;
+	}
+	if (i == cx->nof_inputs)
+		i = 0;
+	cx->active_input = i;
+	cx->audio_input = cx->card->video_inputs[i].audio_index;
+	cx->av_state.vid_input = CX18_AV_COMPOSITE7;
+	cx->av_state.aud_input = CX18_AV_AUDIO8;
+	cx->av_state.audclk_freq = 48000;
+	cx->av_state.audmode = V4L2_TUNER_MODE_LANG1;
+	cx->av_state.vbi_line_offset = 8;
+}
+
+static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev,
+			  const struct pci_device_id *pci_id)
+{
+	u16 cmd;
+	unsigned char pci_latency;
+
+	CX18_DEBUG_INFO("Enabling pci device\n");
+
+	if (pci_enable_device(dev)) {
+		CX18_ERR("Can't enable device %d!\n", cx->num);
+		return -EIO;
+	}
+	if (pci_set_dma_mask(dev, 0xffffffff)) {
+		CX18_ERR("No suitable DMA available on card %d.\n", cx->num);
+		return -EIO;
+	}
+	if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
+		CX18_ERR("Cannot request encoder memory region on card %d.\n", cx->num);
+		return -EIO;
+	}
+
+	/* Check for bus mastering */
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+	pci_write_config_word(dev, PCI_COMMAND, cmd);
+
+	pci_read_config_byte(dev, PCI_CLASS_REVISION, &cx->card_rev);
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+
+	if (pci_latency < 64 && cx18_pci_latency) {
+		CX18_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);
+
+	CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, "
+		   "irq: %d, latency: %d, memory: 0x%lx\n",
+		   cx->dev->device, cx->card_rev, dev->bus->number,
+		   PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+		   cx->dev->irq, pci_latency, (unsigned long)cx->base_addr);
+
+	return 0;
+}
+
+static u32 cx18_request_module(struct cx18 *cx, u32 hw,
+		const char *name, u32 id)
+{
+	if ((hw & id) == 0)
+		return hw;
+	if (request_module(name) != 0) {
+		CX18_ERR("Failed to load module %s\n", name);
+		return hw & ~id;
+	}
+	CX18_DEBUG_INFO("Loaded module %s\n", name);
+	return hw;
+}
+
+static void cx18_load_and_init_modules(struct cx18 *cx)
+{
+	u32 hw = cx->card->hw_all;
+	int i;
+
+	/* load modules */
+#ifndef CONFIG_VIDEO_TUNER
+	hw = cx18_request_module(cx, hw, "tuner", CX18_HW_TUNER);
+#endif
+#ifndef CONFIG_VIDEO_CS5345
+	hw = cx18_request_module(cx, hw, "cs5345", CX18_HW_CS5345);
+#endif
+
+	/* check which i2c devices are actually found */
+	for (i = 0; i < 32; i++) {
+		u32 device = 1 << i;
+
+		if (!(device & hw))
+			continue;
+		if (device == CX18_HW_GPIO || device == CX18_HW_TVEEPROM ||
+		    device == CX18_HW_CX23418 || device == CX18_HW_DVB) {
+			/* These 'devices' do not use i2c probing */
+			cx->hw_flags |= device;
+			continue;
+		}
+		cx18_i2c_register(cx, i);
+		if (cx18_i2c_hw_addr(cx, device) > 0)
+			cx->hw_flags |= device;
+	}
+
+	hw = cx->hw_flags;
+}
+
+static int __devinit cx18_probe(struct pci_dev *dev,
+				const struct pci_device_id *pci_id)
+{
+	int retval = 0;
+	int vbi_buf_size;
+	u32 devtype;
+	struct cx18 *cx;
+
+	spin_lock(&cx18_cards_lock);
+
+	/* Make sure we've got a place for this card */
+	if (cx18_cards_active == CX18_MAX_CARDS) {
+		printk(KERN_ERR "cx18:  Maximum number of cards detected (%d).\n",
+			      cx18_cards_active);
+		spin_unlock(&cx18_cards_lock);
+		return -ENOMEM;
+	}
+
+	cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC);
+	if (cx == 0) {
+		spin_unlock(&cx18_cards_lock);
+		return -ENOMEM;
+	}
+	cx18_cards[cx18_cards_active] = cx;
+	cx->dev = dev;
+	cx->num = cx18_cards_active++;
+	snprintf(cx->name, sizeof(cx->name) - 1, "cx18-%d", cx->num);
+	CX18_INFO("Initializing card #%d\n", cx->num);
+
+	spin_unlock(&cx18_cards_lock);
+
+	cx18_process_options(cx);
+	if (cx->options.cardtype == -1) {
+		retval = -ENODEV;
+		goto err;
+	}
+	if (cx18_init_struct1(cx)) {
+		retval = -ENOMEM;
+		goto err;
+	}
+
+	CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr);
+
+	/* PCI Device Setup */
+	retval = cx18_setup_pci(cx, dev, pci_id);
+	if (retval != 0) {
+		if (retval == -EIO)
+			goto free_workqueue;
+		else if (retval == -ENXIO)
+			goto free_mem;
+	}
+	/* save cx in the pci struct for later use */
+	pci_set_drvdata(dev, cx);
+
+	/* map io memory */
+	CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+		   cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE);
+	cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET,
+				       CX18_MEM_SIZE);
+	if (!cx->enc_mem) {
+		CX18_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n");
+		CX18_ERR("or disabling CONFIG_HIGHMEM4G into the kernel would help\n");
+		retval = -ENOMEM;
+		goto free_mem;
+	}
+	cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET;
+	devtype = read_reg(0xC72028);
+	switch (devtype & 0xff000000) {
+	case 0xff000000:
+		CX18_INFO("cx23418 revision %08x (A)\n", devtype);
+		break;
+	case 0x01000000:
+		CX18_INFO("cx23418 revision %08x (B)\n", devtype);
+		break;
+	default:
+		CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype);
+		break;
+	}
+
+	cx18_init_power(cx, 1);
+	cx18_init_memory(cx);
+
+	cx->scb = (struct cx18_scb *)(cx->enc_mem + SCB_OFFSET);
+	cx18_init_scb(cx);
+
+	cx18_gpio_init(cx);
+
+	/* active i2c  */
+	CX18_DEBUG_INFO("activating i2c...\n");
+	if (init_cx18_i2c(cx)) {
+		CX18_ERR("Could not initialize i2c\n");
+		goto free_map;
+	}
+
+	CX18_DEBUG_INFO("Active card count: %d.\n", cx18_cards_active);
+
+	if (cx->card->hw_all & CX18_HW_TVEEPROM) {
+		/* Based on the model number the cardtype may be changed.
+		   The PCI IDs are not always reliable. */
+		cx18_process_eeprom(cx);
+	}
+	if (cx->card->comment)
+		CX18_INFO("%s", cx->card->comment);
+	if (cx->card->v4l2_capabilities == 0) {
+		retval = -ENODEV;
+		goto free_i2c;
+	}
+	cx18_init_memory(cx);
+
+	/* Register IRQ */
+	retval = request_irq(cx->dev->irq, cx18_irq_handler,
+			     IRQF_SHARED | IRQF_DISABLED, cx->name, (void *)cx);
+	if (retval) {
+		CX18_ERR("Failed to register irq %d\n", retval);
+		goto free_i2c;
+	}
+
+	if (cx->std == 0)
+		cx->std = V4L2_STD_NTSC_M;
+
+	if (cx->options.tuner == -1) {
+		int i;
+
+		for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
+			if ((cx->std & cx->card->tuners[i].std) == 0)
+				continue;
+			cx->options.tuner = cx->card->tuners[i].tuner;
+			break;
+		}
+	}
+	/* if no tuner was found, then pick the first tuner in the card list */
+	if (cx->options.tuner == -1 && cx->card->tuners[0].std) {
+		cx->std = cx->card->tuners[0].std;
+		cx->options.tuner = cx->card->tuners[0].tuner;
+	}
+	if (cx->options.radio == -1)
+		cx->options.radio = (cx->card->radio_input.audio_type != 0);
+
+	/* The card is now fully identified, continue with card-specific
+	   initialization. */
+	cx18_init_struct2(cx);
+
+	cx18_load_and_init_modules(cx);
+
+	if (cx->std & V4L2_STD_525_60) {
+		cx->is_60hz = 1;
+		cx->is_out_60hz = 1;
+	} else {
+		cx->is_50hz = 1;
+		cx->is_out_50hz = 1;
+	}
+	cx->params.video_gop_size = cx->is_60hz ? 15 : 12;
+
+	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = 0x08000;
+	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = 0x08000;
+	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = 0x01200;
+	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = 0x20000;
+	vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2;
+	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
+
+	if (cx->options.radio > 0)
+		cx->v4l2_cap |= V4L2_CAP_RADIO;
+
+	retval = cx18_streams_setup(cx);
+	if (retval) {
+		CX18_ERR("Error %d setting up streams\n", retval);
+		goto free_irq;
+	}
+	retval = cx18_streams_register(cx);
+	if (retval) {
+		CX18_ERR("Error %d registering devices\n", retval);
+		goto free_streams;
+	}
+
+	if (cx->options.tuner > -1) {
+		struct tuner_setup setup;
+
+		setup.addr = ADDR_UNSET;
+		setup.type = cx->options.tuner;
+		setup.mode_mask = T_ANALOG_TV;  /* matches TV tuners */
+		setup.tuner_callback = (setup.type == TUNER_XC2028) ?
+			cx18_reset_tuner_gpio : NULL;
+		cx18_call_i2c_clients(cx, TUNER_SET_TYPE_ADDR, &setup);
+		if (setup.type == TUNER_XC2028) {
+			static struct xc2028_ctrl ctrl = {
+				.fname = XC2028_DEFAULT_FIRMWARE,
+				.max_len = 64,
+			};
+			struct v4l2_priv_tun_config cfg = {
+				.tuner = cx->options.tuner,
+				.priv = &ctrl,
+			};
+			cx18_call_i2c_clients(cx, TUNER_SET_CONFIG, &cfg);
+		}
+	}
+
+	/* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+	   are not. */
+	cx->tuner_std = cx->std;
+
+	cx18_init_on_first_open(cx);
+
+	CX18_INFO("Initialized card #%d: %s\n", cx->num, cx->card_name);
+
+	return 0;
+
+free_streams:
+	cx18_streams_cleanup(cx);
+free_irq:
+	free_irq(cx->dev->irq, (void *)cx);
+free_i2c:
+	exit_cx18_i2c(cx);
+free_map:
+	cx18_iounmap(cx);
+free_mem:
+	release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+free_workqueue:
+err:
+	if (retval == 0)
+		retval = -ENODEV;
+	CX18_ERR("Error %d on initialization\n", retval);
+
+	kfree(cx18_cards[cx18_cards_active]);
+	cx18_cards[cx18_cards_active] = NULL;
+	return retval;
+}
+
+int cx18_init_on_first_open(struct cx18 *cx)
+{
+	int video_input;
+	int fw_retry_count = 3;
+	struct v4l2_frequency vf;
+
+	if (test_bit(CX18_F_I_FAILED, &cx->i_flags))
+		return -ENXIO;
+
+	if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags))
+		return 0;
+
+	while (--fw_retry_count > 0) {
+		/* load firmware */
+		if (cx18_firmware_init(cx) == 0)
+			break;
+		if (fw_retry_count > 1)
+			CX18_WARN("Retry loading firmware\n");
+	}
+
+	if (fw_retry_count == 0) {
+		set_bit(CX18_F_I_FAILED, &cx->i_flags);
+		return -ENXIO;
+	}
+	set_bit(CX18_F_I_LOADED_FW, &cx->i_flags);
+
+	/* Init the firmware twice to work around a silicon bug
+	 * transport related. */
+
+	fw_retry_count = 3;
+	while (--fw_retry_count > 0) {
+		/* load firmware */
+		if (cx18_firmware_init(cx) == 0)
+			break;
+		if (fw_retry_count > 1)
+			CX18_WARN("Retry loading firmware\n");
+	}
+
+	if (fw_retry_count == 0) {
+		set_bit(CX18_F_I_FAILED, &cx->i_flags);
+		return -ENXIO;
+	}
+
+	vf.tuner = 0;
+	vf.type = V4L2_TUNER_ANALOG_TV;
+	vf.frequency = 6400; /* the tuner 'baseline' frequency */
+
+	/* Set initial frequency. For PAL/SECAM broadcasts no
+	   'default' channel exists AFAIK. */
+	if (cx->std == V4L2_STD_NTSC_M_JP)
+		vf.frequency = 1460;	/* ch. 1 91250*16/1000 */
+	else if (cx->std & V4L2_STD_NTSC_M)
+		vf.frequency = 1076;	/* ch. 4 67250*16/1000 */
+
+	video_input = cx->active_input;
+	cx->active_input++;	/* Force update of input */
+	cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_INPUT, &video_input);
+
+	/* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+	   in one place. */
+	cx->std++;		/* Force full standard initialization */
+	cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_STD, &cx->tuner_std);
+	cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_FREQUENCY, &vf);
+	return 0;
+}
+
+static void cx18_remove(struct pci_dev *pci_dev)
+{
+	struct cx18 *cx = pci_get_drvdata(pci_dev);
+
+	CX18_DEBUG_INFO("Removing Card #%d\n", cx->num);
+
+	/* Stop all captures */
+	CX18_DEBUG_INFO("Stopping all streams\n");
+	if (atomic_read(&cx->capturing) > 0)
+		cx18_stop_all_captures(cx);
+
+	/* Interrupts */
+	sw1_irq_disable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+	sw2_irq_disable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+	cx18_halt_firmware(cx);
+
+	cx18_streams_cleanup(cx);
+
+	exit_cx18_i2c(cx);
+
+	free_irq(cx->dev->irq, (void *)cx);
+
+	if (cx->dev)
+		cx18_iounmap(cx);
+
+	release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+
+	pci_disable_device(cx->dev);
+
+	CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num);
+}
+
+/* define a pci_driver for card detection */
+static struct pci_driver cx18_pci_driver = {
+      .name =     "cx18",
+      .id_table = cx18_pci_tbl,
+      .probe =    cx18_probe,
+      .remove =   cx18_remove,
+};
+
+static int module_start(void)
+{
+	printk(KERN_INFO "cx18:  Start initialization, version %s\n", CX18_VERSION);
+
+	memset(cx18_cards, 0, sizeof(cx18_cards));
+
+	/* Validate parameters */
+	if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
+		printk(KERN_ERR "cx18:  Exiting, ivtv_first_minor must be between 0 and %d\n",
+		     CX18_MAX_CARDS - 1);
+		return -1;
+	}
+
+	if (cx18_debug < 0 || cx18_debug > 511) {
+		cx18_debug = 0;
+		printk(KERN_INFO "cx18:   Debug value must be >= 0 and <= 511!\n");
+	}
+
+	if (pci_register_driver(&cx18_pci_driver)) {
+		printk(KERN_ERR "cx18:   Error detecting PCI card\n");
+		return -ENODEV;
+	}
+	printk(KERN_INFO "cx18:  End initialization\n");
+	return 0;
+}
+
+static void module_cleanup(void)
+{
+	int i;
+
+	pci_unregister_driver(&cx18_pci_driver);
+
+	for (i = 0; i < cx18_cards_active; i++) {
+		if (cx18_cards[i] == NULL)
+			continue;
+		kfree(cx18_cards[i]);
+	}
+}
+
+module_init(module_start);
+module_exit(module_cleanup);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
new file mode 100644
index 0000000..2ee9391
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -0,0 +1,500 @@
+/*
+ *  cx18 driver internal defines and structures
+ *
+ *  Derived from ivtv-driver.h
+ *
+ *  Copyright (C) 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 CX18_DRIVER_H
+#define CX18_DRIVER_H
+
+#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 <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include "cx18-mailbox.h"
+#include "cx18-av-core.h"
+#include "cx23418.h"
+
+/* DVB */
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+
+#ifndef CONFIG_PCI
+#  error "This driver requires kernel PCI support."
+#endif
+
+#define CX18_MEM_OFFSET	0x00000000
+#define CX18_MEM_SIZE	0x04000000
+#define CX18_REG_OFFSET	0x02000000
+
+/* Maximum cx18 driver instances. */
+#define CX18_MAX_CARDS 32
+
+/* Supported cards */
+#define CX18_CARD_HVR_1600_ESMT	      0	/* Hauppauge HVR 1600 (ESMT memory) */
+#define CX18_CARD_HVR_1600_SAMSUNG    1	/* Hauppauge HVR 1600 (Samsung memory) */
+#define CX18_CARD_COMPRO_H900 	      2	/* Compro VideoMate H900 */
+#define CX18_CARD_YUAN_MPC718 	      3	/* Yuan MPC718 */
+#define CX18_CARD_LAST 		      3
+
+#define CX18_ENC_STREAM_TYPE_MPG  0
+#define CX18_ENC_STREAM_TYPE_TS   1
+#define CX18_ENC_STREAM_TYPE_YUV  2
+#define CX18_ENC_STREAM_TYPE_VBI  3
+#define CX18_ENC_STREAM_TYPE_PCM  4
+#define CX18_ENC_STREAM_TYPE_IDX  5
+#define CX18_ENC_STREAM_TYPE_RAD  6
+#define CX18_MAX_STREAMS	  7
+
+/* system vendor and device IDs */
+#define PCI_VENDOR_ID_CX      0x14f1
+#define PCI_DEVICE_ID_CX23418 0x5b7a
+
+/* subsystem vendor ID */
+#define CX18_PCI_ID_HAUPPAUGE 		0x0070
+#define CX18_PCI_ID_COMPRO 		0x185b
+#define CX18_PCI_ID_YUAN 		0x12ab
+
+/* ======================================================================== */
+/* ========================== START USER SETTABLE DMA VARIABLES =========== */
+/* ======================================================================== */
+
+/* DMA Buffers, Default size in MB allocated */
+#define CX18_DEFAULT_ENC_TS_BUFFERS  1
+#define CX18_DEFAULT_ENC_MPG_BUFFERS 2
+#define CX18_DEFAULT_ENC_IDX_BUFFERS 1
+#define CX18_DEFAULT_ENC_YUV_BUFFERS 2
+#define CX18_DEFAULT_ENC_VBI_BUFFERS 1
+#define CX18_DEFAULT_ENC_PCM_BUFFERS 1
+
+/* i2c stuff */
+#define I2C_CLIENTS_MAX 16
+
+/* debugging */
+
+/* Flag to turn on high volume debugging */
+#define CX18_DBGFLG_WARN  (1 << 0)
+#define CX18_DBGFLG_INFO  (1 << 1)
+#define CX18_DBGFLG_API   (1 << 2)
+#define CX18_DBGFLG_DMA   (1 << 3)
+#define CX18_DBGFLG_IOCTL (1 << 4)
+#define CX18_DBGFLG_FILE  (1 << 5)
+#define CX18_DBGFLG_I2C   (1 << 6)
+#define CX18_DBGFLG_IRQ   (1 << 7)
+/* Flag to turn on high volume debugging */
+#define CX18_DBGFLG_HIGHVOL (1 << 8)
+
+/* NOTE: extra space before comma in 'cx->num , ## args' is required for
+   gcc-2.95, otherwise it won't compile. */
+#define CX18_DEBUG(x, type, fmt, args...) \
+	do { \
+		if ((x) & cx18_debug) \
+			printk(KERN_INFO "cx18-%d " type ": " fmt, cx->num , ## args); \
+	} while (0)
+#define CX18_DEBUG_WARN(fmt, args...)  CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args)
+#define CX18_DEBUG_INFO(fmt, args...)  CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args)
+#define CX18_DEBUG_API(fmt, args...)   CX18_DEBUG(CX18_DBGFLG_API, "api", fmt , ## args)
+#define CX18_DEBUG_DMA(fmt, args...)   CX18_DEBUG(CX18_DBGFLG_DMA, "dma", fmt , ## args)
+#define CX18_DEBUG_IOCTL(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define CX18_DEBUG_FILE(fmt, args...)  CX18_DEBUG(CX18_DBGFLG_FILE, "file", fmt , ## args)
+#define CX18_DEBUG_I2C(fmt, args...)   CX18_DEBUG(CX18_DBGFLG_I2C, "i2c", fmt , ## args)
+#define CX18_DEBUG_IRQ(fmt, args...)   CX18_DEBUG(CX18_DBGFLG_IRQ, "irq", fmt , ## args)
+
+#define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \
+	do { \
+		if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \
+			printk(KERN_INFO "cx18%d " type ": " fmt, cx->num , ## args); \
+	} while (0)
+#define CX18_DEBUG_HI_WARN(fmt, args...)  CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args)
+#define CX18_DEBUG_HI_INFO(fmt, args...)  CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args)
+#define CX18_DEBUG_HI_API(fmt, args...)   CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_API, "api", fmt , ## args)
+#define CX18_DEBUG_HI_DMA(fmt, args...)   CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_DMA, "dma", fmt , ## args)
+#define CX18_DEBUG_HI_IOCTL(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define CX18_DEBUG_HI_FILE(fmt, args...)  CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_FILE, "file", fmt , ## args)
+#define CX18_DEBUG_HI_I2C(fmt, args...)   CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_I2C, "i2c", fmt , ## args)
+#define CX18_DEBUG_HI_IRQ(fmt, args...)   CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args)
+
+/* Standard kernel messages */
+#define CX18_ERR(fmt, args...)      printk(KERN_ERR  "cx18-%d: " fmt, cx->num , ## args)
+#define CX18_WARN(fmt, args...)     printk(KERN_WARNING "cx18-%d: " fmt, cx->num , ## args)
+#define CX18_INFO(fmt, args...)     printk(KERN_INFO "cx18-%d: " fmt, cx->num , ## args)
+
+/* Values for CX18_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
+
+#define CX18_MAX_PGM_INDEX (400)
+
+extern int cx18_debug;
+
+
+struct cx18_options {
+	int megabytes[CX18_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 */
+};
+
+/* per-buffer bit flags */
+#define CX18_F_B_NEED_BUF_SWAP  0	/* this buffer should be byte swapped */
+
+/* per-stream, s_flags */
+#define CX18_F_S_CLAIMED 	3	/* this stream is claimed */
+#define CX18_F_S_STREAMING      4	/* the fw is decoding/encoding this stream */
+#define CX18_F_S_INTERNAL_USE	5	/* this stream is used internally (sliced VBI processing) */
+#define CX18_F_S_STREAMOFF	7	/* signal end of stream EOS */
+#define CX18_F_S_APPL_IO        8	/* this stream is used read/written by an application */
+
+/* per-cx18, i_flags */
+#define CX18_F_I_LOADED_FW	0 	/* Loaded the firmware the first time */
+#define CX18_F_I_EOS		4 	/* End of encoder stream reached */
+#define CX18_F_I_RADIO_USER	5 	/* The radio tuner is selected */
+#define CX18_F_I_ENC_PAUSED	13 	/* the encoder is paused */
+#define CX18_F_I_INITED		21 	/* set after first open */
+#define CX18_F_I_FAILED		22 	/* set if first open failed */
+
+/* These are the VBI types as they appear in the embedded VBI private packets. */
+#define CX18_SLICED_TYPE_TELETEXT_B     (1)
+#define CX18_SLICED_TYPE_CAPTION_525    (4)
+#define CX18_SLICED_TYPE_WSS_625        (5)
+#define CX18_SLICED_TYPE_VPS            (7)
+
+struct cx18_buffer {
+	struct list_head list;
+	dma_addr_t dma_handle;
+	u32 id;
+	unsigned long b_flags;
+	char *buf;
+
+	u32 bytesused;
+	u32 readpos;
+};
+
+struct cx18_queue {
+	struct list_head list;
+	u32 buffers;
+	u32 length;
+	u32 bytesused;
+};
+
+struct cx18_dvb {
+	struct dmx_frontend hw_frontend;
+	struct dmx_frontend mem_frontend;
+	struct dmxdev dmxdev;
+	struct dvb_adapter dvb_adapter;
+	struct dvb_demux demux;
+	struct dvb_frontend *fe;
+	struct dvb_net dvbnet;
+	int enabled;
+	int feeding;
+
+	struct mutex feedlock;
+
+};
+
+struct cx18;	 /* forward reference */
+struct cx18_scb; /* forward reference */
+
+struct cx18_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 cx18 *cx; 		/* for ease of use */
+	const char *name;		/* name of the stream */
+	int type;			/* stream type */
+	u32 handle;			/* task handle */
+	unsigned mdl_offset;
+
+	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 */
+	u64 dma_pts;
+	wait_queue_head_t waitq;
+
+	/* Buffer Stats */
+	u32 buffers;
+	u32 buf_size;
+	u32 buffers_stolen;
+
+	/* Buffer Queues */
+	struct cx18_queue q_free;	/* free buffers */
+	struct cx18_queue q_full;	/* full buffers */
+	struct cx18_queue q_io;		/* waiting for I/O */
+
+	/* DVB / Digital Transport */
+	struct cx18_dvb dvb;
+};
+
+struct cx18_open_id {
+	u32 open_id;
+	int type;
+	enum v4l2_priority prio;
+	struct cx18 *cx;
+};
+
+/* forward declaration of struct defined in cx18-cards.h */
+struct cx18_card;
+
+
+#define CX18_VBI_FRAMES 32
+
+/* VBI data */
+struct vbi_info {
+	u32 enc_size;
+	u32 frame;
+	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;
+	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];
+
+	/* 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[CX18_VBI_FRAMES];
+	u32 sliced_mpeg_size[CX18_VBI_FRAMES];
+	struct cx18_buffer sliced_mpeg_buf;
+	u32 inserted_frame;
+
+	u32 start[2], count;
+	u32 raw_size;
+	u32 sliced_size;
+};
+
+/* Per cx23418, per I2C bus private algo callback data */
+struct cx18_i2c_algo_callback_data {
+	struct cx18 *cx;
+	int bus_index;   /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */
+};
+
+/* Struct to hold info about cx18 cards */
+struct cx18 {
+	int num;		/* board number, -1 during init! */
+	char name[8];		/* board name for printk and interrupts (e.g. 'cx180') */
+	struct pci_dev *dev;	/* PCI device */
+	const struct cx18_card *card;	/* card information */
+	const char *card_name;  /* full name of the card */
+	const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */
+	u8 is_50hz;
+	u8 is_60hz;
+	u8 is_out_50hz;
+	u8 is_out_60hz;
+	u8 nof_inputs;		/* number of video inputs */
+	u8 nof_audio_inputs;	/* number of audio inputs */
+	u16 buffer_id;		/* buffer ID counter */
+	u32 v4l2_cap;		/* V4L2 capabilities of card */
+	u32 hw_flags; 		/* Hardware description of the board */
+	unsigned mdl_offset;
+	struct cx18_scb *scb;   /* pointer to SCB */
+
+	struct cx18_av_state av_state;
+
+	/* codec settings */
+	struct cx2341x_mpeg_params params;
+	u32 filter_mode;
+	u32 temporal_strength;
+	u32 spatial_strength;
+
+	/* dualwatch */
+	unsigned long dualwatch_jiffies;
+	u16 dualwatch_stereo_mode;
+
+	/* Digitizer type */
+	int digitizer;		/* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */
+
+	struct mutex serialize_lock;    /* mutex used to serialize open/close/start/stop/ioctl operations */
+	struct cx18_options options; 	/* User options */
+	int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */
+	struct cx18_stream streams[CX18_MAX_STREAMS]; 	/* Stream data */
+	unsigned long i_flags;  /* global cx18 flags */
+	atomic_t capturing;	/* count number of active capture streams */
+	spinlock_t lock;        /* lock access to this struct */
+	int search_pack_header;
+
+	spinlock_t dma_reg_lock; /* lock access to DMA engine registers */
+
+	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;
+	struct v4l2_prio_state prio;
+
+	u8 card_rev;
+	void __iomem *enc_mem, *reg_mem;
+
+	struct vbi_info vbi;
+
+	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[CX18_MAX_PGM_INDEX];
+
+	u64 mpg_data_received;
+	u64 vbi_data_inserted;
+
+	wait_queue_head_t mb_apu_waitq;
+	wait_queue_head_t mb_cpu_waitq;
+	wait_queue_head_t mb_epu_waitq;
+	wait_queue_head_t mb_hpu_waitq;
+	wait_queue_head_t cap_w;
+	/* when the current DMA is finished this queue is woken up */
+	wait_queue_head_t dma_waitq;
+
+	/* i2c */
+	struct i2c_adapter i2c_adap[2];
+	struct i2c_algo_bit_data i2c_algo[2];
+	struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2];
+	struct i2c_client i2c_client[2];
+	struct mutex i2c_bus_lock[2];
+	struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
+
+	/* v4l2 and User settings */
+
+	/* codec settings */
+	u32 audio_input;
+	u32 active_input;
+	u32 active_output;
+	v4l2_std_id std;
+	v4l2_std_id tuner_std;	/* The norm of the tuner (fixed) */
+};
+
+/* Globals */
+extern struct cx18 *cx18_cards[];
+extern int cx18_cards_active;
+extern int cx18_first_minor;
+extern spinlock_t cx18_cards_lock;
+
+/*==============Prototypes==================*/
+
+/* Return non-zero if a signal is pending */
+int cx18_msleep_timeout(unsigned int msecs, int intr);
+
+/* Wait on queue, returns -EINTR if interrupted */
+int cx18_waitq(wait_queue_head_t *waitq);
+
+/* Read Hauppauge eeprom */
+struct tveeprom; /* forward reference */
+void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv);
+
+/* First-open initialization: load firmware, etc. */
+int cx18_init_on_first_open(struct cx18 *cx);
+
+/* 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(cx->reg_mem + (reg))
+#define write_reg(val, reg) writel(val, cx->reg_mem + (reg))
+#define write_reg_sync(val, reg) \
+	do { write_reg(val, reg); read_reg(reg); } while (0)
+
+#define read_enc(addr) readl(cx->enc_mem + (u32)(addr))
+#define write_enc(val, addr) writel(val, cx->enc_mem + (u32)(addr))
+#define write_enc_sync(val, addr) \
+	do { write_enc(val, addr); read_enc(addr); } while (0)
+
+#define sw1_irq_enable(val) do { \
+	write_reg(val, SW1_INT_STATUS); \
+	write_reg(read_reg(SW1_INT_ENABLE_PCI) | (val), SW1_INT_ENABLE_PCI); \
+} while (0)
+
+#define sw1_irq_disable(val) \
+	write_reg(read_reg(SW1_INT_ENABLE_PCI) & ~(val), SW1_INT_ENABLE_PCI);
+
+#define sw2_irq_enable(val) do { \
+	write_reg(val, SW2_INT_STATUS); \
+	write_reg(read_reg(SW2_INT_ENABLE_PCI) | (val), SW2_INT_ENABLE_PCI); \
+} while (0)
+
+#define sw2_irq_disable(val) \
+	write_reg(read_reg(SW2_INT_ENABLE_PCI) & ~(val), SW2_INT_ENABLE_PCI);
+
+#define setup_page(addr) do { \
+    u32 val = read_reg(0xD000F8) & ~0x1f00; \
+    write_reg(val | (((addr) >> 17) & 0x1f00), 0xD000F8); \
+} while (0)
+
+#endif /* CX18_DRIVER_H */
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
new file mode 100644
index 0000000..65efe69
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -0,0 +1,288 @@
+/*
+ *  cx18 functions for DVB support
+ *
+ *  Copyright (c) 2008 Steven Toth <stoth@hauppauge.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx18-version.h"
+#include "cx18-dvb.h"
+#include "cx18-streams.h"
+#include "cx18-cards.h"
+#include "s5h1409.h"
+
+/* Wait until the MXL500X driver is merged */
+#ifdef HAVE_MXL500X
+#include "mxl500x.h"
+#endif
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000
+
+#ifdef HAVE_MXL500X
+static struct mxl500x_config hauppauge_hvr1600_tuner = {
+	.delsys    = MXL500x_MODE_ATSC,
+	.octf      = MXL500x_OCTF_CH,
+	.xtal_freq = 16000000,
+	.iflo_freq = 5380000,
+	.ref_freq  = 322800000,
+	.rssi_ena  = MXL_RSSI_ENABLE,
+	.addr      = 0xC6 >> 1,
+};
+
+static struct s5h1409_config hauppauge_hvr1600_config = {
+	.demod_address = 0x32 >> 1,
+	.output_mode   = S5H1409_SERIAL_OUTPUT,
+	.gpio          = S5H1409_GPIO_ON,
+	.qam_if        = 44000,
+	.inversion     = S5H1409_INVERSION_OFF,
+	.status_mode   = S5H1409_DEMODLOCKING,
+	.mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
+
+};
+#endif
+
+static int dvb_register(struct cx18_stream *stream);
+
+/* Kernel DVB framework calls this when the feed needs to start.
+ * The CX18 framework should enable the transport DMA handling
+ * and queue processing.
+ */
+static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct cx18_stream *stream = (struct cx18_stream *) demux->priv;
+	struct cx18 *cx = stream->cx;
+	int ret = -EINVAL;
+	u32 v;
+
+	CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n",
+			feed->pid, feed->index);
+	switch (cx->card->type) {
+	case CX18_CARD_HVR_1600_ESMT:
+	case CX18_CARD_HVR_1600_SAMSUNG:
+		v = read_reg(CX18_REG_DMUX_NUM_PORT_0_CONTROL);
+		v |= 0x00400000; /* Serial Mode */
+		v |= 0x00002000; /* Data Length - Byte */
+		v |= 0x00010000; /* Error - Polarity */
+		v |= 0x00020000; /* Error - Passthru */
+		v |= 0x000c0000; /* Error - Ignore */
+		write_reg(v, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
+		break;
+
+	default:
+		/* Assumption - Parallel transport - Signalling
+		 * undefined or default.
+		 */
+		break;
+	}
+
+	if (!demux->dmx.frontend)
+		return -EINVAL;
+
+	if (stream) {
+		mutex_lock(&stream->dvb.feedlock);
+		if (stream->dvb.feeding++ == 0) {
+			CX18_DEBUG_INFO("Starting Transport DMA\n");
+			ret = cx18_start_v4l2_encode_stream(stream);
+		} else
+			ret = 0;
+		mutex_unlock(&stream->dvb.feedlock);
+	}
+
+	return ret;
+}
+
+/* Kernel DVB framework calls this when the feed needs to stop. */
+static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct cx18_stream *stream = (struct cx18_stream *)demux->priv;
+	struct cx18 *cx = stream->cx;
+	int ret = -EINVAL;
+
+	CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n",
+			feed->pid, feed->index);
+
+	if (stream) {
+		mutex_lock(&stream->dvb.feedlock);
+		if (--stream->dvb.feeding == 0) {
+			CX18_DEBUG_INFO("Stopping Transport DMA\n");
+			ret = cx18_stop_v4l2_encode_stream(stream, 0);
+		} else
+			ret = 0;
+		mutex_unlock(&stream->dvb.feedlock);
+	}
+
+	return ret;
+}
+
+int cx18_dvb_register(struct cx18_stream *stream)
+{
+	struct cx18 *cx = stream->cx;
+	struct cx18_dvb *dvb = &stream->dvb;
+	struct dvb_adapter *dvb_adapter;
+	struct dvb_demux *dvbdemux;
+	struct dmx_demux *dmx;
+	int ret;
+
+	if (!dvb)
+		return -EINVAL;
+
+	ret = dvb_register_adapter(&dvb->dvb_adapter,
+			CX18_DRIVER_NAME,
+			THIS_MODULE, &cx->dev->dev, adapter_nr);
+	if (ret < 0)
+		goto err_out;
+
+	dvb_adapter = &dvb->dvb_adapter;
+
+	dvbdemux = &dvb->demux;
+
+	dvbdemux->priv = (void *)stream;
+
+	dvbdemux->filternum = 256;
+	dvbdemux->feednum = 256;
+	dvbdemux->start_feed = cx18_dvb_start_feed;
+	dvbdemux->stop_feed = cx18_dvb_stop_feed;
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+		DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+	ret = dvb_dmx_init(dvbdemux);
+	if (ret < 0)
+		goto err_dvb_unregister_adapter;
+
+	dmx = &dvbdemux->dmx;
+
+	dvb->hw_frontend.source = DMX_FRONTEND_0;
+	dvb->mem_frontend.source = DMX_MEMORY_FE;
+	dvb->dmxdev.filternum = 256;
+	dvb->dmxdev.demux = dmx;
+
+	ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter);
+	if (ret < 0)
+		goto err_dvb_dmx_release;
+
+	ret = dmx->add_frontend(dmx, &dvb->hw_frontend);
+	if (ret < 0)
+		goto err_dvb_dmxdev_release;
+
+	ret = dmx->add_frontend(dmx, &dvb->mem_frontend);
+	if (ret < 0)
+		goto err_remove_hw_frontend;
+
+	ret = dmx->connect_frontend(dmx, &dvb->hw_frontend);
+	if (ret < 0)
+		goto err_remove_mem_frontend;
+
+	ret = dvb_register(stream);
+	if (ret < 0)
+		goto err_disconnect_frontend;
+
+	dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
+
+	CX18_INFO("DVB Frontend registered\n");
+	mutex_init(&dvb->feedlock);
+	dvb->enabled = 1;
+	return ret;
+
+err_disconnect_frontend:
+	dmx->disconnect_frontend(dmx);
+err_remove_mem_frontend:
+	dmx->remove_frontend(dmx, &dvb->mem_frontend);
+err_remove_hw_frontend:
+	dmx->remove_frontend(dmx, &dvb->hw_frontend);
+err_dvb_dmxdev_release:
+	dvb_dmxdev_release(&dvb->dmxdev);
+err_dvb_dmx_release:
+	dvb_dmx_release(dvbdemux);
+err_dvb_unregister_adapter:
+	dvb_unregister_adapter(dvb_adapter);
+err_out:
+	return ret;
+}
+
+void cx18_dvb_unregister(struct cx18_stream *stream)
+{
+	struct cx18 *cx = stream->cx;
+	struct cx18_dvb *dvb = &stream->dvb;
+	struct dvb_adapter *dvb_adapter;
+	struct dvb_demux *dvbdemux;
+	struct dmx_demux *dmx;
+
+	CX18_INFO("unregister DVB\n");
+
+	dvb_adapter = &dvb->dvb_adapter;
+	dvbdemux = &dvb->demux;
+	dmx = &dvbdemux->dmx;
+
+	dmx->close(dmx);
+	dvb_net_release(&dvb->dvbnet);
+	dmx->remove_frontend(dmx, &dvb->mem_frontend);
+	dmx->remove_frontend(dmx, &dvb->hw_frontend);
+	dvb_dmxdev_release(&dvb->dmxdev);
+	dvb_dmx_release(dvbdemux);
+	dvb_unregister_frontend(dvb->fe);
+	dvb_frontend_detach(dvb->fe);
+	dvb_unregister_adapter(dvb_adapter);
+}
+
+/* All the DVB attach calls go here, this function get's modified
+ * for each new card. No other function in this file needs
+ * to change.
+ */
+static int dvb_register(struct cx18_stream *stream)
+{
+	struct cx18_dvb *dvb = &stream->dvb;
+	struct cx18 *cx = stream->cx;
+	int ret = 0;
+
+	switch (cx->card->type) {
+/* Wait until the MXL500X driver is merged */
+#ifdef HAVE_MXL500X
+	case CX18_CARD_HVR_1600_ESMT:
+	case CX18_CARD_HVR_1600_SAMSUNG:
+		dvb->fe = dvb_attach(s5h1409_attach,
+			&hauppauge_hvr1600_config,
+			&cx->i2c_adap[0]);
+		if (dvb->fe != NULL) {
+			dvb_attach(mxl500x_attach, dvb->fe,
+				&hauppauge_hvr1600_tuner,
+				&cx->i2c_adap[0]);
+			ret = 0;
+		}
+		break;
+#endif
+	default:
+		/* No Digital Tv Support */
+		break;
+	}
+
+	if (dvb->fe == NULL) {
+		CX18_ERR("frontend initialization failed\n");
+		return -1;
+	}
+
+	ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe);
+	if (ret < 0) {
+		if (dvb->fe->ops.release)
+			dvb->fe->ops.release(dvb->fe);
+		return ret;
+	}
+
+	return ret;
+}
diff --git a/drivers/media/video/cx18/cx18-dvb.h b/drivers/media/video/cx18/cx18-dvb.h
new file mode 100644
index 0000000..d6a6ccd
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-dvb.h
@@ -0,0 +1,25 @@
+/*
+ *  cx18 functions for DVB support
+ *
+ *  Copyright (c) 2008 Steven Toth <stoth@hauppauge.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx18-driver.h"
+
+int cx18_dvb_register(struct cx18_stream *stream);
+void cx18_dvb_unregister(struct cx18_stream *stream);
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
new file mode 100644
index 0000000..6930306
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -0,0 +1,711 @@
+/*
+ *  cx18 file operation functions
+ *
+ *  Derived from ivtv-fileops.c
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-fileops.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-vbi.h"
+#include "cx18-audio.h"
+#include "cx18-mailbox.h"
+#include "cx18-scb.h"
+#include "cx18-streams.h"
+#include "cx18-controls.h"
+#include "cx18-ioctl.h"
+#include "cx18-cards.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 cx18_claim_stream(struct cx18_open_id *id, int type)
+{
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[type];
+	struct cx18_stream *s_vbi;
+	int vbi_type;
+
+	if (test_and_set_bit(CX18_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 == CX18_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;
+			CX18_DEBUG_INFO("Start Read VBI\n");
+			return 0;
+		}
+		/* someone else is using this stream already */
+		CX18_DEBUG_INFO("Stream %d is busy\n", type);
+		return -EBUSY;
+	}
+	s->id = id->open_id;
+
+	/* CX18_DEC_STREAM_TYPE_MPG needs to claim CX18_DEC_STREAM_TYPE_VBI,
+	   CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
+	   (provided VBI insertion is on and sliced VBI is selected), for all
+	   other streams we're done */
+	if (type == CX18_ENC_STREAM_TYPE_MPG &&
+		   cx->vbi.insert_mpeg && cx->vbi.sliced_in->service_set) {
+		vbi_type = CX18_ENC_STREAM_TYPE_VBI;
+	} else {
+		return 0;
+	}
+	s_vbi = &cx->streams[vbi_type];
+
+	set_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);
+
+	/* mark that it is used internally */
+	set_bit(CX18_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 cx18_release_stream(struct cx18_stream *s)
+{
+	struct cx18 *cx = s->cx;
+	struct cx18_stream *s_vbi;
+
+	s->id = -1;
+	if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
+		test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) {
+		/* this stream is still in use internally */
+		return;
+	}
+	if (!test_and_clear_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
+		CX18_DEBUG_WARN("Release stream %s not in use!\n", s->name);
+		return;
+	}
+
+	cx18_flush_queues(s);
+
+	/* CX18_ENC_STREAM_TYPE_MPG needs to release CX18_ENC_STREAM_TYPE_VBI,
+	   for all other streams we're done */
+	if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+		s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+	else
+		return;
+
+	/* clear internal use flag */
+	if (!test_and_clear_bit(CX18_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;
+	}
+	clear_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);
+	cx18_flush_queues(s_vbi);
+}
+
+static void cx18_dualwatch(struct cx18 *cx)
+{
+	struct v4l2_tuner vt;
+	u16 new_bitmap;
+	u16 new_stereo_mode;
+	const u16 stereo_mask = 0x0300;
+	const u16 dual = 0x0200;
+
+	new_stereo_mode = cx->params.audio_properties & stereo_mask;
+	memset(&vt, 0, sizeof(vt));
+	cx18_call_i2c_clients(cx, 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 == cx->dualwatch_stereo_mode)
+		return;
+
+	new_bitmap = new_stereo_mode | (cx->params.audio_properties & ~stereo_mask);
+
+	CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
+			   cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
+
+	if (cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,
+				cx18_find_handle(cx), new_bitmap) == 0) {
+		cx->dualwatch_stereo_mode = new_stereo_mode;
+		return;
+	}
+	CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+}
+
+
+static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, int *err)
+{
+	struct cx18 *cx = s->cx;
+	struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+	struct cx18_buffer *buf;
+	DEFINE_WAIT(wait);
+
+	*err = 0;
+	while (1) {
+		if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
+
+			if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) {
+				cx->dualwatch_jiffies = jiffies;
+				cx18_dualwatch(cx);
+			}
+			if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+			    !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
+				while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) {
+					/* byteswap and process VBI data */
+/*					cx18_process_vbi_data(cx, buf, s_vbi->dma_pts, s_vbi->type); */
+					cx18_enqueue(s_vbi, buf, &s_vbi->q_free);
+				}
+			}
+			buf = &cx->vbi.sliced_mpeg_buf;
+			if (buf->readpos != buf->bytesused)
+				return buf;
+		}
+
+		/* do we have leftover data? */
+		buf = cx18_dequeue(s, &s->q_io);
+		if (buf)
+			return buf;
+
+		/* do we have new data? */
+		buf = cx18_dequeue(s, &s->q_full);
+		if (buf) {
+			if (!test_and_clear_bit(CX18_F_B_NEED_BUF_SWAP,
+						&buf->b_flags))
+				return buf;
+			if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+				/* byteswap MPG data */
+				cx18_buf_swap(buf);
+			else {
+				/* byteswap and process VBI data */
+				cx18_process_vbi_data(cx, buf,
+						s->dma_pts, s->type);
+			}
+			return buf;
+		}
+
+		/* return if end of stream */
+		if (!test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+			CX18_DEBUG_INFO("EOS %s\n", s->name);
+			return NULL;
+		}
+
+		/* return if file was opened with O_NONBLOCK */
+		if (non_block) {
+			*err = -EAGAIN;
+			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 */
+			CX18_DEBUG_INFO("User stopped %s\n", s->name);
+			*err = -EINTR;
+			return NULL;
+		}
+	}
+}
+
+static void cx18_setup_sliced_vbi_buf(struct cx18 *cx)
+{
+	int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
+
+	cx->vbi.sliced_mpeg_buf.buf = cx->vbi.sliced_mpeg_data[idx];
+	cx->vbi.sliced_mpeg_buf.bytesused = cx->vbi.sliced_mpeg_size[idx];
+	cx->vbi.sliced_mpeg_buf.readpos = 0;
+}
+
+static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
+		struct cx18_buffer *buf, char __user *ubuf, size_t ucount)
+{
+	struct cx18 *cx = s->cx;
+	size_t len = buf->bytesused - buf->readpos;
+
+	if (len > ucount)
+		len = ucount;
+	if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
+	    cx->vbi.sliced_in->service_set && buf != &cx->vbi.sliced_mpeg_buf) {
+		const char *start = buf->buf + buf->readpos;
+		const char *p = start + 1;
+		const u8 *q;
+		u8 ch = cx->search_pack_header ? 0xba : 0xe0;
+		int stuffing, i;
+
+		while (start + len > p) {
+			q = memchr(p, 0, start + len - p);
+			if (q == NULL)
+				break;
+			p = q + 1;
+			if ((char *)q + 15 >= buf->buf + buf->bytesused ||
+			    q[1] != 0 || q[2] != 1 || q[3] != ch)
+				continue;
+			if (!cx->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;
+					cx->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) {
+				cx->search_pack_header = 0;
+				len = (char *)q - start;
+				cx18_setup_sliced_vbi_buf(cx);
+				break;
+			}
+		}
+	}
+	if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
+		CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n",
+				len, s->name);
+		return -EFAULT;
+	}
+	buf->readpos += len;
+	if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
+	    buf != &cx->vbi.sliced_mpeg_buf)
+		cx->mpg_data_received += len;
+	return len;
+}
+
+static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
+		size_t tot_count, int non_block)
+{
+	struct cx18 *cx = s->cx;
+	size_t tot_written = 0;
+	int single_frame = 0;
+
+	if (atomic_read(&cx->capturing) == 0 && s->id == -1) {
+		/* shouldn't happen */
+		CX18_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 == CX18_ENC_STREAM_TYPE_VBI &&
+	    cx->vbi.sliced_in->service_set)
+		single_frame = 1;
+
+	for (;;) {
+		struct cx18_buffer *buf;
+		int rc;
+
+		buf = cx18_get_buffer(s, non_block, &rc);
+		/* if there is no data available... */
+		if (buf == NULL) {
+			/* if we got data, then return that regardless */
+			if (tot_written)
+				break;
+			/* EOS condition */
+			if (rc == 0) {
+				clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+				clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
+				cx18_release_stream(s);
+			}
+			/* set errno */
+			return rc;
+		}
+
+		rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written,
+				tot_count - tot_written);
+
+		if (buf != &cx->vbi.sliced_mpeg_buf) {
+			if (buf->readpos == buf->bytesused) {
+				cx18_buf_sync_for_device(s, buf);
+				cx18_enqueue(s, buf, &s->q_free);
+				cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5,
+					s->handle,
+					(void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
+					1, buf->id, s->buf_size);
+			} else
+				cx18_enqueue(s, buf, &s->q_io);
+		} else if (buf->readpos == buf->bytesused) {
+			int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
+
+			cx->vbi.sliced_mpeg_size[idx] = 0;
+			cx->vbi.inserted_frame++;
+			cx->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 cx18_read_pos(struct cx18_stream *s, char __user *ubuf,
+		size_t count, loff_t *pos, int non_block)
+{
+	ssize_t rc = count ? cx18_read(s, ubuf, count, non_block) : 0;
+	struct cx18 *cx = s->cx;
+
+	CX18_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc);
+	if (rc > 0)
+		pos += rc;
+	return rc;
+}
+
+int cx18_start_capture(struct cx18_open_id *id)
+{
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+	struct cx18_stream *s_vbi;
+
+	if (s->type == CX18_ENC_STREAM_TYPE_RAD) {
+		/* you cannot read from these stream types. */
+		return -EPERM;
+	}
+
+	/* Try to claim this stream. */
+	if (cx18_claim_stream(id, s->type))
+		return -EBUSY;
+
+	/* If capture is already in progress, then we also have to
+	   do nothing extra. */
+	if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) ||
+	    test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+		set_bit(CX18_F_S_APPL_IO, &s->s_flags);
+		return 0;
+	}
+
+	/* Start VBI capture if required */
+	s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+	if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
+	    test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+	    !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
+		/* Note: the CX18_ENC_STREAM_TYPE_VBI is claimed
+		   automatically when the MPG stream is claimed.
+		   We only need to start the VBI capturing. */
+		if (cx18_start_v4l2_encode_stream(s_vbi)) {
+			CX18_DEBUG_WARN("VBI capture start failed\n");
+
+			/* Failure, clean up and return an error */
+			clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
+			clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+			/* also releases the associated VBI stream */
+			cx18_release_stream(s);
+			return -EIO;
+		}
+		CX18_DEBUG_INFO("VBI insertion started\n");
+	}
+
+	/* Tell the card to start capturing */
+	if (!cx18_start_v4l2_encode_stream(s)) {
+		/* We're done */
+		set_bit(CX18_F_S_APPL_IO, &s->s_flags);
+		/* Resume a possibly paused encoder */
+		if (test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+			cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, s->handle);
+		return 0;
+	}
+
+	/* failure, clean up */
+	CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
+
+	/* Note: the CX18_ENC_STREAM_TYPE_VBI is released
+	   automatically when the MPG stream is released.
+	   We only need to stop the VBI capturing. */
+	if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
+	    test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
+		cx18_stop_v4l2_encode_stream(s_vbi, 0);
+		clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
+	}
+	clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+	cx18_release_stream(s);
+	return -EIO;
+}
+
+ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
+		loff_t *pos)
+{
+	struct cx18_open_id *id = filp->private_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+	int rc;
+
+	CX18_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name);
+
+	mutex_lock(&cx->serialize_lock);
+	rc = cx18_start_capture(id);
+	mutex_unlock(&cx->serialize_lock);
+	if (rc)
+		return rc;
+	return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
+}
+
+unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
+{
+	struct cx18_open_id *id = filp->private_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+	int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+	/* Start a capture if there is none */
+	if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+		int rc;
+
+		mutex_lock(&cx->serialize_lock);
+		rc = cx18_start_capture(id);
+		mutex_unlock(&cx->serialize_lock);
+		if (rc) {
+			CX18_DEBUG_INFO("Could not start capture for %s (%d)\n",
+					s->name, rc);
+			return POLLERR;
+		}
+		CX18_DEBUG_FILE("Encoder poll started capture\n");
+	}
+
+	/* add stream's waitq to the poll list */
+	CX18_DEBUG_HI_FILE("Encoder poll\n");
+	poll_wait(filp, &s->waitq, wait);
+
+	if (s->q_full.length || s->q_io.length)
+		return POLLIN | POLLRDNORM;
+	if (eof)
+		return POLLHUP;
+	return 0;
+}
+
+void cx18_stop_capture(struct cx18_open_id *id, int gop_end)
+{
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	CX18_DEBUG_IOCTL("close() of %s\n", s->name);
+
+	/* 'Unclaim' this stream */
+
+	/* Stop capturing */
+	if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+		struct cx18_stream *s_vbi =
+			&cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+
+		CX18_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 == CX18_ENC_STREAM_TYPE_MPG &&
+		    test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
+		    !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
+			CX18_DEBUG_INFO("close stopping embedded VBI capture\n");
+			cx18_stop_v4l2_encode_stream(s_vbi, 0);
+		}
+		if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
+		    test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags))
+			/* Also used internally, don't stop capturing */
+			s->id = -1;
+		else
+			cx18_stop_v4l2_encode_stream(s, gop_end);
+	}
+	if (!gop_end) {
+		clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
+		clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+		cx18_release_stream(s);
+	}
+}
+
+int cx18_v4l2_close(struct inode *inode, struct file *filp)
+{
+	struct cx18_open_id *id = filp->private_data;
+	struct cx18 *cx = id->cx;
+	struct cx18_stream *s = &cx->streams[id->type];
+
+	CX18_DEBUG_IOCTL("close() of %s\n", s->name);
+
+	v4l2_prio_close(&cx->prio, &id->prio);
+
+	/* 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 */
+	mutex_lock(&cx->serialize_lock);
+	if (id->type == CX18_ENC_STREAM_TYPE_RAD) {
+		/* Closing radio device, return to TV mode */
+		cx18_mute(cx);
+		/* Mark that the radio is no longer in use */
+		clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
+		/* Switch tuner to TV */
+		cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
+		/* Select correct audio input (i.e. TV tuner or Line in) */
+		cx18_audio_set_io(cx);
+		if (atomic_read(&cx->capturing) > 0) {
+			/* Undo video mute */
+			cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
+				cx->params.video_mute |
+					(cx->params.video_mute_yuv << 8));
+		}
+		/* Done! Unmute and continue. */
+		cx18_unmute(cx);
+		cx18_release_stream(s);
+	} else {
+		cx18_stop_capture(id, 0);
+	}
+	kfree(id);
+	mutex_unlock(&cx->serialize_lock);
+	return 0;
+}
+
+static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
+{
+	struct cx18 *cx = s->cx;
+	struct cx18_open_id *item;
+
+	CX18_DEBUG_FILE("open %s\n", s->name);
+
+	/* Allocate memory */
+	item = kmalloc(sizeof(struct cx18_open_id), GFP_KERNEL);
+	if (NULL == item) {
+		CX18_DEBUG_WARN("nomem on v4l2 open\n");
+		return -ENOMEM;
+	}
+	item->cx = cx;
+	item->type = s->type;
+	v4l2_prio_open(&cx->prio, &item->prio);
+
+	item->open_id = cx->open_id++;
+	filp->private_data = item;
+
+	if (item->type == CX18_ENC_STREAM_TYPE_RAD) {
+		/* Try to claim this stream */
+		if (cx18_claim_stream(item, item->type)) {
+			/* No, it's already in use */
+			kfree(item);
+			return -EBUSY;
+		}
+
+		if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
+			if (atomic_read(&cx->capturing) > 0) {
+				/* switching to radio while capture is
+				   in progress is not polite */
+				cx18_release_stream(s);
+				kfree(item);
+				return -EBUSY;
+			}
+		}
+
+		/* Mark that the radio is being used. */
+		set_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
+		/* We have the radio */
+		cx18_mute(cx);
+		/* Switch tuner to radio */
+		cx18_call_i2c_clients(cx, AUDC_SET_RADIO, NULL);
+		/* Select the correct audio input (i.e. radio tuner) */
+		cx18_audio_set_io(cx);
+		/* Done! Unmute and continue. */
+		cx18_unmute(cx);
+	}
+	return 0;
+}
+
+int cx18_v4l2_open(struct inode *inode, struct file *filp)
+{
+	int res, x, y = 0;
+	struct cx18 *cx = NULL;
+	struct cx18_stream *s = NULL;
+	int minor = iminor(inode);
+
+	/* Find which card this open was on */
+	spin_lock(&cx18_cards_lock);
+	for (x = 0; cx == NULL && x < cx18_cards_active; x++) {
+		/* find out which stream this open was on */
+		for (y = 0; y < CX18_MAX_STREAMS; y++) {
+			s = &cx18_cards[x]->streams[y];
+			if (s->v4l2dev && s->v4l2dev->minor == minor) {
+				cx = cx18_cards[x];
+				break;
+			}
+		}
+	}
+	spin_unlock(&cx18_cards_lock);
+
+	if (cx == NULL) {
+		/* Couldn't find a device registered
+		   on that minor, shouldn't happen! */
+		printk(KERN_WARNING "No cx18 device found on minor %d\n",
+				minor);
+		return -ENXIO;
+	}
+
+	mutex_lock(&cx->serialize_lock);
+	if (cx18_init_on_first_open(cx)) {
+		CX18_ERR("Failed to initialize on minor %d\n", minor);
+		mutex_unlock(&cx->serialize_lock);
+		return -ENXIO;
+	}
+	res = cx18_serialized_open(s, filp);
+	mutex_unlock(&cx->serialize_lock);
+	return res;
+}
+
+void cx18_mute(struct cx18 *cx)
+{
+	if (atomic_read(&cx->capturing))
+		cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
+				cx18_find_handle(cx), 1);
+	CX18_DEBUG_INFO("Mute\n");
+}
+
+void cx18_unmute(struct cx18 *cx)
+{
+	if (atomic_read(&cx->capturing)) {
+		cx18_msleep_timeout(100, 0);
+		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
+				cx18_find_handle(cx), 12);
+		cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
+				cx18_find_handle(cx), 0);
+	}
+	CX18_DEBUG_INFO("Unmute\n");
+}
diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/video/cx18/cx18-fileops.h
new file mode 100644
index 0000000..16cdafb
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-fileops.h
@@ -0,0 +1,45 @@
+/*
+ *  cx18 file operation functions
+ *
+ *  Derived from ivtv-fileops.h
+ *
+ *  Copyright (C) 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 cx18_v4l2_open(struct inode *inode, struct file *filp);
+ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
+		      loff_t *pos);
+ssize_t cx18_v4l2_write(struct file *filp, const char __user *buf, size_t count,
+		       loff_t *pos);
+int cx18_v4l2_close(struct inode *inode, struct file *filp);
+unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait);
+int cx18_start_capture(struct cx18_open_id *id);
+void cx18_stop_capture(struct cx18_open_id *id, int gop_end);
+void cx18_mute(struct cx18 *cx);
+void cx18_unmute(struct cx18 *cx);
+
+/* 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 cx18_claim_stream(struct cx18_open_id *id, int type);
+
+/* Release a previously claimed stream. */
+void cx18_release_stream(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
new file mode 100644
index 0000000..2694ce3
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-firmware.c
@@ -0,0 +1,373 @@
+/*
+ *  cx18 firmware functions
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-scb.h"
+#include "cx18-irq.h"
+#include "cx18-firmware.h"
+#include "cx18-cards.h"
+#include <linux/firmware.h>
+
+#define CX18_PROC_SOFT_RESET 		0xc70010
+#define CX18_DDR_SOFT_RESET          	0xc70014
+#define CX18_CLOCK_SELECT1           	0xc71000
+#define CX18_CLOCK_SELECT2           	0xc71004
+#define CX18_HALF_CLOCK_SELECT1      	0xc71008
+#define CX18_HALF_CLOCK_SELECT2      	0xc7100C
+#define CX18_CLOCK_POLARITY1         	0xc71010
+#define CX18_CLOCK_POLARITY2         	0xc71014
+#define CX18_ADD_DELAY_ENABLE1       	0xc71018
+#define CX18_ADD_DELAY_ENABLE2       	0xc7101C
+#define CX18_CLOCK_ENABLE1           	0xc71020
+#define CX18_CLOCK_ENABLE2           	0xc71024
+
+#define CX18_REG_BUS_TIMEOUT_EN      	0xc72024
+
+#define CX18_AUDIO_ENABLE            	0xc72014
+#define CX18_REG_BUS_TIMEOUT_EN      	0xc72024
+
+#define CX18_FAST_CLOCK_PLL_INT      	0xc78000
+#define CX18_FAST_CLOCK_PLL_FRAC     	0xc78004
+#define CX18_FAST_CLOCK_PLL_POST     	0xc78008
+#define CX18_FAST_CLOCK_PLL_PRESCALE 	0xc7800C
+#define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010
+
+#define CX18_SLOW_CLOCK_PLL_INT      	0xc78014
+#define CX18_SLOW_CLOCK_PLL_FRAC     	0xc78018
+#define CX18_SLOW_CLOCK_PLL_POST     	0xc7801C
+#define CX18_MPEG_CLOCK_PLL_INT		0xc78040
+#define CX18_MPEG_CLOCK_PLL_FRAC	0xc78044
+#define CX18_MPEG_CLOCK_PLL_POST	0xc78048
+#define CX18_PLL_POWER_DOWN          	0xc78088
+#define CX18_SW1_INT_STATUS             0xc73104
+#define CX18_SW1_INT_ENABLE_PCI         0xc7311C
+#define CX18_SW2_INT_SET                0xc73140
+#define CX18_SW2_INT_STATUS             0xc73144
+#define CX18_ADEC_CONTROL            	0xc78120
+
+#define CX18_DDR_REQUEST_ENABLE      	0xc80000
+#define CX18_DDR_CHIP_CONFIG         	0xc80004
+#define CX18_DDR_REFRESH            	0xc80008
+#define CX18_DDR_TIMING1             	0xc8000C
+#define CX18_DDR_TIMING2             	0xc80010
+#define CX18_DDR_POWER_REG		0xc8001C
+
+#define CX18_DDR_TUNE_LANE           	0xc80048
+#define CX18_DDR_INITIAL_EMRS        	0xc80054
+#define CX18_DDR_MB_PER_ROW_7        	0xc8009C
+#define CX18_DDR_BASE_63_ADDR        	0xc804FC
+
+#define CX18_WMB_CLIENT02            	0xc90108
+#define CX18_WMB_CLIENT05            	0xc90114
+#define CX18_WMB_CLIENT06            	0xc90118
+#define CX18_WMB_CLIENT07            	0xc9011C
+#define CX18_WMB_CLIENT08            	0xc90120
+#define CX18_WMB_CLIENT09            	0xc90124
+#define CX18_WMB_CLIENT10            	0xc90128
+#define CX18_WMB_CLIENT11            	0xc9012C
+#define CX18_WMB_CLIENT12            	0xc90130
+#define CX18_WMB_CLIENT13            	0xc90134
+#define CX18_WMB_CLIENT14            	0xc90138
+
+#define CX18_DSP0_INTERRUPT_MASK     	0xd0004C
+
+/* Encoder/decoder firmware sizes */
+#define CX18_FW_CPU_SIZE 		(174716)
+#define CX18_FW_APU_SIZE 		(141200)
+
+#define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */
+#define APU_ROM_SYNC2 0x72646548 /* "rdeH" */
+
+struct cx18_apu_rom_seghdr {
+	u32 sync1;
+	u32 sync2;
+	u32 addr;
+	u32 size;
+};
+
+static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx, long size)
+{
+	const struct firmware *fw = NULL;
+	int retries = 3;
+	int i, j;
+	u32 __iomem *dst = (u32 __iomem *)mem;
+	const u32 *src;
+
+retry:
+	if (!retries || request_firmware(&fw, fn, &cx->dev->dev)) {
+		CX18_ERR("Unable to open firmware %s (must be %ld bytes)\n",
+				fn, size);
+		CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n");
+		return -ENOMEM;
+	}
+
+	src = (const u32 *)fw->data;
+
+	if (fw->size != size) {
+		/* 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. */
+		CX18_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 += 4096) {
+		setup_page(i);
+		for (j = i; j < fw->size && j < i + 4096; j += 4) {
+			/* no need for endianness conversion on the ppc */
+			__raw_writel(*src, dst);
+			if (__raw_readl(dst) != *src) {
+				CX18_ERR("Mismatch at offset %x\n", i);
+				release_firmware(fw);
+				return -EIO;
+			}
+			dst++;
+			src++;
+		}
+	}
+	if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
+		CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
+	release_firmware(fw);
+	return size;
+}
+
+static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, long size)
+{
+	const struct firmware *fw = NULL;
+	int retries = 3;
+	int i, j;
+	const u32 *src;
+	struct cx18_apu_rom_seghdr seghdr;
+	const u8 *vers;
+	u32 offset = 0;
+	u32 apu_version = 0;
+	int sz;
+
+retry:
+	if (!retries || request_firmware(&fw, fn, &cx->dev->dev)) {
+		CX18_ERR("unable to open firmware %s (must be %ld bytes)\n",
+				fn, size);
+		CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
+		return -ENOMEM;
+	}
+
+	src = (const u32 *)fw->data;
+	vers = fw->data + sizeof(seghdr);
+	sz = fw->size;
+
+	if (fw->size != size) {
+		/* 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. */
+		CX18_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n",
+			       fn, size, fw->size);
+		release_firmware(fw);
+		retries--;
+		goto retry;
+	}
+	apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32];
+	while (offset + sizeof(seghdr) < size) {
+		/* TODO: byteswapping */
+		memcpy(&seghdr, src + offset / 4, sizeof(seghdr));
+		offset += sizeof(seghdr);
+		if (seghdr.sync1 != APU_ROM_SYNC1 ||
+		    seghdr.sync2 != APU_ROM_SYNC2) {
+			offset += seghdr.size;
+			continue;
+		}
+		CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr,
+				seghdr.addr + seghdr.size - 1);
+		if (offset + seghdr.size > sz)
+			break;
+		for (i = 0; i < seghdr.size; i += 4096) {
+			setup_page(offset + i);
+			for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
+				/* no need for endianness conversion on the ppc */
+				__raw_writel(src[(offset + j) / 4], dst + seghdr.addr + j);
+				if (__raw_readl(dst + seghdr.addr + j) != src[(offset + j) / 4]) {
+					CX18_ERR("Mismatch at offset %x\n", offset + j);
+					release_firmware(fw);
+					return -EIO;
+				}
+			}
+		}
+		offset += seghdr.size;
+	}
+	if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
+		CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n",
+				fn, apu_version, fw->size);
+	release_firmware(fw);
+	/* Clear bit0 for APU to start from 0 */
+	write_reg(read_reg(0xc72030) & ~1, 0xc72030);
+	return size;
+}
+
+void cx18_halt_firmware(struct cx18 *cx)
+{
+	CX18_DEBUG_INFO("Preparing for firmware halt.\n");
+	write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
+	write_reg(0x00020002, CX18_ADEC_CONTROL);
+}
+
+void cx18_init_power(struct cx18 *cx, int lowpwr)
+{
+	/* power-down Spare and AOM PLLs */
+	/* power-up fast, slow and mpeg PLLs */
+	write_reg(0x00000008, CX18_PLL_POWER_DOWN);
+
+	/* ADEC out of sleep */
+	write_reg(0x00020000, CX18_ADEC_CONTROL);
+
+	/* The fast clock is at 200/245 MHz */
+	write_reg(lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
+	write_reg(lowpwr ? 0x1EFBF37 : 0x038E3D7, CX18_FAST_CLOCK_PLL_FRAC);
+
+	write_reg(2, CX18_FAST_CLOCK_PLL_POST);
+	write_reg(1, CX18_FAST_CLOCK_PLL_PRESCALE);
+	write_reg(4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
+
+	/* set slow clock to 125/120 MHz */
+	write_reg(lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT);
+	write_reg(lowpwr ? 0xEBAF05 : 0x18618A8, CX18_SLOW_CLOCK_PLL_FRAC);
+	write_reg(4, CX18_SLOW_CLOCK_PLL_POST);
+
+	/* mpeg clock pll 54MHz */
+	write_reg(0xF, CX18_MPEG_CLOCK_PLL_INT);
+	write_reg(0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC);
+	write_reg(8, CX18_MPEG_CLOCK_PLL_POST);
+
+	/* Defaults */
+	/* APU = SC or SC/2 = 125/62.5 */
+	/* EPU = SC = 125 */
+	/* DDR = FC = 180 */
+	/* ENC = SC = 125 */
+	/* AI1 = SC = 125 */
+	/* VIM2 = disabled */
+	/* PCI = FC/2 = 90 */
+	/* AI2 = disabled */
+	/* DEMUX = disabled */
+	/* AO = SC/2 = 62.5 */
+	/* SER = 54MHz */
+	/* VFC = disabled */
+	/* USB = disabled */
+
+	write_reg(lowpwr ? 0xFFFF0020 : 0x00060004, CX18_CLOCK_SELECT1);
+	write_reg(lowpwr ? 0xFFFF0004 : 0x00060006, CX18_CLOCK_SELECT2);
+
+	write_reg(0xFFFF0002, CX18_HALF_CLOCK_SELECT1);
+	write_reg(0xFFFF0104, CX18_HALF_CLOCK_SELECT2);
+
+	write_reg(0xFFFF9026, CX18_CLOCK_ENABLE1);
+	write_reg(0xFFFF3105, CX18_CLOCK_ENABLE2);
+}
+
+void cx18_init_memory(struct cx18 *cx)
+{
+	cx18_msleep_timeout(10, 0);
+	write_reg(0x10000, CX18_DDR_SOFT_RESET);
+	cx18_msleep_timeout(10, 0);
+
+	write_reg(cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
+
+	cx18_msleep_timeout(10, 0);
+
+	write_reg(cx->card->ddr.refresh, CX18_DDR_REFRESH);
+	write_reg(cx->card->ddr.timing1, CX18_DDR_TIMING1);
+	write_reg(cx->card->ddr.timing2, CX18_DDR_TIMING2);
+
+	cx18_msleep_timeout(10, 0);
+
+	/* Initialize DQS pad time */
+	write_reg(cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE);
+	write_reg(cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS);
+
+	cx18_msleep_timeout(10, 0);
+
+	write_reg(0x20000, CX18_DDR_SOFT_RESET);
+	cx18_msleep_timeout(10, 0);
+
+	/* use power-down mode when idle */
+	write_reg(0x00000010, CX18_DDR_POWER_REG);
+
+	write_reg(0x10001, CX18_REG_BUS_TIMEOUT_EN);
+
+	write_reg(0x48, CX18_DDR_MB_PER_ROW_7);
+	write_reg(0xE0000, CX18_DDR_BASE_63_ADDR);
+
+	write_reg(0x00000101, CX18_WMB_CLIENT02);  /* AO */
+	write_reg(0x00000101, CX18_WMB_CLIENT09);  /* AI2 */
+	write_reg(0x00000101, CX18_WMB_CLIENT05);  /* VIM1 */
+	write_reg(0x00000101, CX18_WMB_CLIENT06);  /* AI1 */
+	write_reg(0x00000101, CX18_WMB_CLIENT07);  /* 3D comb */
+	write_reg(0x00000101, CX18_WMB_CLIENT10);  /* ME */
+	write_reg(0x00000101, CX18_WMB_CLIENT12);  /* ENC */
+	write_reg(0x00000101, CX18_WMB_CLIENT13);  /* PK */
+	write_reg(0x00000101, CX18_WMB_CLIENT11);  /* RC */
+	write_reg(0x00000101, CX18_WMB_CLIENT14);  /* AVO */
+}
+
+int cx18_firmware_init(struct cx18 *cx)
+{
+	/* Allow chip to control CLKRUN */
+	write_reg(0x5, CX18_DSP0_INTERRUPT_MASK);
+
+	write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
+
+	cx18_msleep_timeout(1, 0);
+
+	sw1_irq_enable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+	sw2_irq_enable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+	/* Only if the processor is not running */
+	if (read_reg(CX18_PROC_SOFT_RESET) & 8) {
+		int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
+			       cx->enc_mem, cx, CX18_FW_APU_SIZE);
+
+		sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
+					cx->enc_mem, cx, CX18_FW_CPU_SIZE);
+
+		if (sz > 0) {
+			int retries = 0;
+
+			/* start the CPU */
+			write_reg(0x00080000, CX18_PROC_SOFT_RESET);
+			while (retries++ < 50) { /* Loop for max 500mS */
+				if ((read_reg(CX18_PROC_SOFT_RESET) & 1) == 0)
+					break;
+				cx18_msleep_timeout(10, 0);
+			}
+			cx18_msleep_timeout(200, 0);
+			if (retries == 51) {
+				CX18_ERR("Could not start the CPU\n");
+				return -EIO;
+			}
+		}
+		if (sz <= 0)
+			return -EIO;
+	}
+	/* initialize GPIO */
+	write_reg(0x14001400, 0xC78110);
+	return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-firmware.h b/drivers/media/video/cx18/cx18-firmware.h
new file mode 100644
index 0000000..38d4c05
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-firmware.h
@@ -0,0 +1,25 @@
+/*
+ *  cx18 firmware functions
+ *
+ *  Copyright (C) 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 cx18_firmware_init(struct cx18 *cx);
+void cx18_halt_firmware(struct cx18 *cx);
+void cx18_init_memory(struct cx18 *cx);
+void cx18_init_power(struct cx18 *cx, int lowpwr);
diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c
new file mode 100644
index 0000000..19253e6
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-gpio.c
@@ -0,0 +1,74 @@
+/*
+ *  cx18 gpio functions
+ *
+ *  Derived from ivtv-gpio.c
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-cards.h"
+#include "cx18-gpio.h"
+#include "tuner-xc2028.h"
+
+/********************* GPIO stuffs *********************/
+
+/* GPIO registers */
+#define CX18_REG_GPIO_IN     0xc72010
+#define CX18_REG_GPIO_OUT1   0xc78100
+#define CX18_REG_GPIO_DIR1   0xc78108
+#define CX18_REG_GPIO_OUT2   0xc78104
+#define CX18_REG_GPIO_DIR2   0xc7810c
+
+/*
+ * HVR-1600 GPIO pins, courtesy of Hauppauge:
+ *
+ * gpio0: zilog ir process reset pin
+ * gpio1: zilog programming pin (you should never use this)
+ * gpio12: cx24227 reset pin
+ * gpio13: cs5345 reset pin
+*/
+
+void cx18_gpio_init(struct cx18 *cx)
+{
+	if (cx->card->gpio_init.direction == 0)
+		return;
+
+	CX18_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
+		   read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_OUT1));
+
+	/* init output data then direction */
+	write_reg(cx->card->gpio_init.direction << 16, CX18_REG_GPIO_DIR1);
+	write_reg(0, CX18_REG_GPIO_DIR2);
+	write_reg((cx->card->gpio_init.direction << 16) |
+			cx->card->gpio_init.initial_value, CX18_REG_GPIO_OUT1);
+	write_reg(0, CX18_REG_GPIO_OUT2);
+}
+
+/* Xceive tuner reset function */
+int cx18_reset_tuner_gpio(void *dev, int cmd, int value)
+{
+	struct i2c_algo_bit_data *algo = dev;
+	struct cx18 *cx = algo->data;
+/*	int curdir, curout;*/
+
+	if (cmd != XC2028_TUNER_RESET)
+		return 0;
+	CX18_DEBUG_INFO("Resetting tuner\n");
+	return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h
new file mode 100644
index 0000000..41bac88
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-gpio.h
@@ -0,0 +1,24 @@
+/*
+ *  cx18 gpio functions
+ *
+ *  Derived from ivtv-gpio.h
+ *
+ *  Copyright (C) 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 cx18_gpio_init(struct cx18 *cx);
+int cx18_reset_tuner_gpio(void *dev, int cmd, int value);
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
new file mode 100644
index 0000000..18c88d1
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -0,0 +1,431 @@
+/*
+ *  cx18 I2C functions
+ *
+ *  Derived from ivtv-i2c.c
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-cards.h"
+#include "cx18-gpio.h"
+#include "cx18-av-core.h"
+
+#include <media/ir-kbd-i2c.h>
+
+#define CX18_REG_I2C_1_WR   0xf15000
+#define CX18_REG_I2C_1_RD   0xf15008
+#define CX18_REG_I2C_2_WR   0xf25100
+#define CX18_REG_I2C_2_RD   0xf25108
+
+#define SETSCL_BIT      0x0001
+#define SETSDL_BIT      0x0002
+#define GETSCL_BIT      0x0004
+#define GETSDL_BIT      0x0008
+
+#ifndef I2C_ADAP_CLASS_TV_ANALOG
+#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG
+#endif
+
+#define CX18_CS5345_I2C_ADDR		0x4c
+
+/* This array should match the CX18_HW_ defines */
+static const u8 hw_driverids[] = {
+	I2C_DRIVERID_TUNER,
+	I2C_DRIVERID_TVEEPROM,
+	I2C_DRIVERID_CS5345,
+	0, 		/* CX18_HW_GPIO dummy driver ID */
+	0 		/* CX18_HW_CX23418 dummy driver ID */
+};
+
+/* This array should match the CX18_HW_ defines */
+static const u8 hw_addrs[] = {
+	0,
+	0,
+	CX18_CS5345_I2C_ADDR,
+	0, 		/* CX18_HW_GPIO dummy driver ID */
+	0,		/* CX18_HW_CX23418 dummy driver ID */
+};
+
+/* This array should match the CX18_HW_ defines */
+/* This might well become a card-specific array */
+static const u8 hw_bus[] = {
+	0,
+	0,
+	0,
+	0, 		/* CX18_HW_GPIO dummy driver ID */
+	0,		/* CX18_HW_CX23418 dummy driver ID */
+};
+
+/* This array should match the CX18_HW_ defines */
+static const char * const hw_drivernames[] = {
+	"tuner",
+	"tveeprom",
+	"cs5345",
+	"gpio",
+	"cx23418",
+};
+
+int cx18_i2c_register(struct cx18 *cx, unsigned idx)
+{
+	struct i2c_board_info info;
+	struct i2c_client *c;
+	u8 id, bus;
+	int i;
+
+	CX18_DEBUG_I2C("i2c client register\n");
+	if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
+		return -1;
+	id = hw_driverids[idx];
+	bus = hw_bus[idx];
+	memset(&info, 0, sizeof(info));
+	strlcpy(info.driver_name, hw_drivernames[idx],
+			sizeof(info.driver_name));
+	info.addr = hw_addrs[idx];
+	for (i = 0; i < I2C_CLIENTS_MAX; i++)
+		if (cx->i2c_clients[i] == NULL)
+			break;
+
+	if (i == I2C_CLIENTS_MAX) {
+		CX18_ERR("insufficient room for new I2C client!\n");
+		return -ENOMEM;
+	}
+
+	if (id != I2C_DRIVERID_TUNER) {
+		c = i2c_new_device(&cx->i2c_adap[bus], &info);
+		if (c->driver == NULL)
+			i2c_unregister_device(c);
+		else
+			cx->i2c_clients[i] = c;
+		return cx->i2c_clients[i] ? 0 : -ENODEV;
+	}
+
+	/* special tuner handling */
+	c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio);
+	if (c && c->driver == NULL)
+		i2c_unregister_device(c);
+	else if (c)
+		cx->i2c_clients[i++] = c;
+	c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod);
+	if (c && c->driver == NULL)
+		i2c_unregister_device(c);
+	else if (c)
+		cx->i2c_clients[i++] = c;
+	c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv);
+	if (c && c->driver == NULL)
+		i2c_unregister_device(c);
+	else if (c)
+		cx->i2c_clients[i++] = c;
+	return 0;
+}
+
+static int attach_inform(struct i2c_client *client)
+{
+	return 0;
+}
+
+static int detach_inform(struct i2c_client *client)
+{
+	int i;
+	struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter);
+
+	CX18_DEBUG_I2C("i2c client detach\n");
+	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+		if (cx->i2c_clients[i] == client) {
+			cx->i2c_clients[i] = NULL;
+			break;
+		}
+	}
+	CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n",
+		   client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
+
+	return 0;
+}
+
+static void cx18_setscl(void *data, int state)
+{
+	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+	int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+	u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
+	u32 r = read_reg(addr);
+
+	if (state)
+		write_reg_sync(r | SETSCL_BIT, addr);
+	else
+		write_reg_sync(r & ~SETSCL_BIT, addr);
+}
+
+static void cx18_setsda(void *data, int state)
+{
+	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+	int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+	u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
+	u32 r = read_reg(addr);
+
+	if (state)
+		write_reg_sync(r | SETSDL_BIT, addr);
+	else
+		write_reg_sync(r & ~SETSDL_BIT, addr);
+}
+
+static int cx18_getscl(void *data)
+{
+	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+	int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+	u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
+
+	return read_reg(addr) & GETSCL_BIT;
+}
+
+static int cx18_getsda(void *data)
+{
+	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+	int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+	u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
+
+	return read_reg(addr) & GETSDL_BIT;
+}
+
+/* template for i2c-bit-algo */
+static struct i2c_adapter cx18_i2c_adap_template = {
+	.name = "cx18 i2c driver",
+	.id = I2C_HW_B_CX2341X,
+	.algo = NULL,                   /* set by i2c-algo-bit */
+	.algo_data = NULL,              /* filled from template */
+	.client_register = attach_inform,
+	.client_unregister = detach_inform,
+	.owner = THIS_MODULE,
+};
+
+#define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */
+#define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */
+
+static struct i2c_algo_bit_data cx18_i2c_algo_template = {
+	.setsda		= cx18_setsda,
+	.setscl		= cx18_setscl,
+	.getsda		= cx18_getsda,
+	.getscl		= cx18_getscl,
+	.udelay		= CX18_SCL_PERIOD/2,       /* 1/2 clock period in usec*/
+	.timeout	= CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */
+};
+
+static struct i2c_client cx18_i2c_client_template = {
+	.name = "cx18 internal",
+};
+
+int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg)
+{
+	struct i2c_client *client;
+	int retval;
+	int i;
+
+	CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
+	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+		client = cx->i2c_clients[i];
+		if (client == NULL || client->driver == NULL ||
+				client->driver->command == NULL)
+			continue;
+		if (addr == client->addr) {
+			retval = client->driver->command(client, cmd, arg);
+			return retval;
+		}
+	}
+	if (cmd != VIDIOC_G_CHIP_IDENT)
+		CX18_ERR("i2c addr 0x%02x not found for cmd 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. */
+static int cx18_i2c_id_addr(struct cx18 *cx, u32 id)
+{
+	struct i2c_client *client;
+	int retval = -ENODEV;
+	int i;
+
+	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+		client = cx->i2c_clients[i];
+		if (client == NULL || client->driver == NULL)
+			continue;
+		if (id == client->driver->id) {
+			retval = client->addr;
+			break;
+		}
+	}
+	return retval;
+}
+
+/* Find the i2c device name matching the DRIVERID */
+static const char *cx18_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 CX18_HW_ flag */
+static const char *cx18_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 CX18_HW_ flag and return
+   its i2c address or -ENODEV if no matching device was found. */
+int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+		if (1 << i == hw)
+			return cx18_i2c_id_addr(cx, hw_driverids[i]);
+	return -ENODEV;
+}
+
+/* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing.
+   If hw == CX18_HW_GPIO then call the gpio handler. */
+int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg)
+{
+	int addr;
+
+	if (hw == CX18_HW_GPIO || hw == 0)
+		return 0;
+	if (hw == CX18_HW_CX23418)
+		return cx18_av_cmd(cx, cmd, arg);
+
+	addr = cx18_i2c_hw_addr(cx, hw);
+	if (addr < 0) {
+		CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n",
+			       hw, cx18_i2c_hw_name(hw), cmd);
+		return addr;
+	}
+	return cx18_call_i2c_client(cx, addr, cmd, arg);
+}
+
+/* Calls i2c device based on I2C driver ID. */
+int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg)
+{
+	int addr;
+
+	addr = cx18_i2c_id_addr(cx, id);
+	if (addr < 0) {
+		if (cmd != VIDIOC_G_CHIP_IDENT)
+			CX18_ERR("i2c ID 0x%08x (%s) not found for cmd 0x%x!\n",
+				id, cx18_i2c_id_name(id), cmd);
+		return addr;
+	}
+	return cx18_call_i2c_client(cx, addr, cmd, arg);
+}
+
+/* broadcast cmd for all I2C clients and for the gpio subsystem */
+void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+	if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) {
+		CX18_ERR("adapter is not set\n");
+		return;
+	}
+	cx18_av_cmd(cx, cmd, arg);
+	i2c_clients_command(&cx->i2c_adap[0], cmd, arg);
+	i2c_clients_command(&cx->i2c_adap[1], cmd, arg);
+}
+
+/* init + register i2c algo-bit adapter */
+int init_cx18_i2c(struct cx18 *cx)
+{
+	int i;
+	CX18_DEBUG_I2C("i2c init\n");
+
+	for (i = 0; i < 2; i++) {
+		memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
+			sizeof(struct i2c_adapter));
+		memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template,
+			sizeof(struct i2c_algo_bit_data));
+		cx->i2c_algo_cb_data[i].cx = cx;
+		cx->i2c_algo_cb_data[i].bus_index = i;
+		cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i];
+		cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
+
+		sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name),
+				" #%d-%d", cx->num, i);
+		i2c_set_adapdata(&cx->i2c_adap[i], cx);
+
+		memcpy(&cx->i2c_client[i], &cx18_i2c_client_template,
+			sizeof(struct i2c_client));
+		sprintf(cx->i2c_client[i].name +
+				strlen(cx->i2c_client[i].name), "%d", i);
+		cx->i2c_client[i].adapter = &cx->i2c_adap[i];
+		cx->i2c_adap[i].dev.parent = &cx->dev->dev;
+	}
+
+	if (read_reg(CX18_REG_I2C_2_WR) != 0x0003c02f) {
+		/* Reset/Unreset I2C hardware block */
+		write_reg(0x10000000, 0xc71004); /* Clock select 220MHz */
+		write_reg_sync(0x10001000, 0xc71024); /* Clock Enable */
+	}
+	/* courtesy of Steven Toth <stoth@hauppauge.com> */
+	write_reg_sync(0x00c00000, 0xc7001c);
+	mdelay(10);
+	write_reg_sync(0x00c000c0, 0xc7001c);
+	mdelay(10);
+	write_reg_sync(0x00c00000, 0xc7001c);
+
+	write_reg_sync(0x00c00000, 0xc730c8); /* Set to edge-triggered intrs. */
+	write_reg_sync(0x00c00000, 0xc730c4); /* Clear any stale intrs */
+
+	/* Hw I2C1 Clock Freq ~100kHz */
+	write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_1_WR);
+	cx18_setscl(&cx->i2c_algo_cb_data[0], 1);
+	cx18_setsda(&cx->i2c_algo_cb_data[0], 1);
+
+	/* Hw I2C2 Clock Freq ~100kHz */
+	write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_2_WR);
+	cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
+	cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
+
+	return i2c_bit_add_bus(&cx->i2c_adap[0]) ||
+		i2c_bit_add_bus(&cx->i2c_adap[1]);
+}
+
+void exit_cx18_i2c(struct cx18 *cx)
+{
+	int i;
+	CX18_DEBUG_I2C("i2c exit\n");
+	write_reg(read_reg(CX18_REG_I2C_1_WR) | 4, CX18_REG_I2C_1_WR);
+	write_reg(read_reg(CX18_REG_I2C_2_WR) | 4, CX18_REG_I2C_2_WR);
+
+	for (i = 0; i < 2; i++) {
+		i2c_del_adapter(&cx->i2c_adap[i]);
+	}
+}
+
+/*
+   Hauppauge HVR1600 should have:
+   32 cx24227
+   98 unknown
+   a0 eeprom
+   c2 tuner
+   e? zilog ir
+   */
diff --git a/drivers/media/video/cx18/cx18-i2c.h b/drivers/media/video/cx18/cx18-i2c.h
new file mode 100644
index 0000000..113c3f9
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-i2c.h
@@ -0,0 +1,33 @@
+/*
+ *  cx18 I2C functions
+ *
+ *  Derived from ivtv-i2c.h
+ *
+ *  Copyright (C) 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 cx18_i2c_hw_addr(struct cx18 *cx, u32 hw);
+int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg);
+int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg);
+int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg);
+void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg);
+int cx18_i2c_register(struct cx18 *cx, unsigned idx);
+
+/* init + register i2c algo-bit adapter */
+int init_cx18_i2c(struct cx18 *cx);
+void exit_cx18_i2c(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
new file mode 100644
index 0000000..dbdcb86
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -0,0 +1,851 @@
+/*
+ *  cx18 ioctl system call
+ *
+ *  Derived from ivtv-ioctl.c
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-version.h"
+#include "cx18-mailbox.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-fileops.h"
+#include "cx18-vbi.h"
+#include "cx18-audio.h"
+#include "cx18-video.h"
+#include "cx18-streams.h"
+#include "cx18-ioctl.h"
+#include "cx18-gpio.h"
+#include "cx18-controls.h"
+#include "cx18-cards.h"
+#include "cx18-av-core.h"
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/i2c-id.h>
+
+u16 cx18_service2vbi(int type)
+{
+	switch (type) {
+	case V4L2_SLICED_TELETEXT_B:
+		return CX18_SLICED_TYPE_TELETEXT_B;
+	case V4L2_SLICED_CAPTION_525:
+		return CX18_SLICED_TYPE_CAPTION_525;
+	case V4L2_SLICED_WSS_625:
+		return CX18_SLICED_TYPE_WSS_625;
+	case V4L2_SLICED_VPS:
+		return CX18_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 cx18_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 cx18_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 cx18_std_60hz = {
+	.frameperiod = {.numerator = 1001, .denominator = 30000},
+	.framelines = 525,
+};
+
+static const struct v4l2_standard cx18_std_50hz = {
+	.frameperiod = { .numerator = 1, .denominator = 25 },
+	.framelines = 625,
+};
+
+static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+	struct v4l2_register *regs = arg;
+	unsigned long flags;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
+		return -EINVAL;
+
+	spin_lock_irqsave(&cx18_cards_lock, flags);
+	if (cmd == VIDIOC_DBG_G_REGISTER)
+		regs->val = read_enc(regs->reg);
+	else
+		write_enc(regs->val, regs->reg);
+	spin_unlock_irqrestore(&cx18_cards_lock, flags);
+	return 0;
+}
+
+static int cx18_get_fmt(struct cx18 *cx, int streamtype, struct v4l2_format *fmt)
+{
+	switch (fmt->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		fmt->fmt.pix.width = cx->params.width;
+		fmt->fmt.pix.height = cx->params.height;
+		fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+		fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+		if (streamtype == CX18_ENC_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_VBI_CAPTURE:
+		fmt->fmt.vbi.sampling_rate = 27000000;
+		fmt->fmt.vbi.offset = 248;
+		fmt->fmt.vbi.samples_per_line = cx->vbi.raw_decoder_line_size - 4;
+		fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+		fmt->fmt.vbi.start[0] = cx->vbi.start[0];
+		fmt->fmt.vbi.start[1] = cx->vbi.start[1];
+		fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = cx->vbi.count;
+		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));
+
+		cx18_av_cmd(cx, VIDIOC_G_FMT, fmt);
+		vbifmt->service_set = cx18_get_service_set(vbifmt);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int cx18_try_or_set_fmt(struct cx18 *cx, int streamtype,
+		struct v4l2_format *fmt, int set_fmt)
+{
+	struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+	u16 set;
+
+	/* 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 > (cx->is_50hz ? 576 : 480))
+			h = (cx->is_50hz ? 576 : 480);
+		else if (h < 2)
+			h = 2;
+		cx18_get_fmt(cx, streamtype, fmt);
+		fmt->fmt.pix.width = w;
+		fmt->fmt.pix.height = h;
+
+		if (!set_fmt || (cx->params.width == w && cx->params.height == h))
+			return 0;
+		if (atomic_read(&cx->capturing) > 0)
+			return -EBUSY;
+
+		cx->params.width = w;
+		cx->params.height = h;
+		if (w != 720 || h != (cx->is_50hz ? 576 : 480))
+			cx->params.video_temporal_filter = 0;
+		else
+			cx->params.video_temporal_filter = 8;
+		cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+		return cx18_get_fmt(cx, streamtype, fmt);
+	}
+
+	/* set raw VBI format */
+	if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+		if (set_fmt && streamtype == CX18_ENC_STREAM_TYPE_VBI &&
+		    cx->vbi.sliced_in->service_set &&
+		    atomic_read(&cx->capturing) > 0)
+			return -EBUSY;
+		if (set_fmt) {
+			cx->vbi.sliced_in->service_set = 0;
+			cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
+		}
+		return cx18_get_fmt(cx, streamtype, fmt);
+	}
+
+	/* any else but sliced VBI capture is an error */
+	if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+		return -EINVAL;
+
+	/* TODO: implement sliced VBI, for now silently return 0 */
+	return 0;
+
+	/* 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)
+		cx18_expand_service_set(vbifmt, cx->is_50hz);
+	set = check_service_set(vbifmt, cx->is_50hz);
+	vbifmt->service_set = cx18_get_service_set(vbifmt);
+
+	if (!set_fmt)
+		return 0;
+	if (set == 0)
+		return -EINVAL;
+	if (atomic_read(&cx->capturing) > 0 && cx->vbi.sliced_in->service_set == 0)
+		return -EBUSY;
+	cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+	memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
+	return 0;
+}
+
+static int cx18_debug_ioctls(struct file *filp, unsigned int cmd, void *arg)
+{
+	struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
+	struct cx18 *cx = id->cx;
+	struct v4l2_register *reg = arg;
+
+	switch (cmd) {
+	/* ioctls to allow direct access to the encoder registers for testing */
+	case VIDIOC_DBG_G_REGISTER:
+		if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+			return cx18_cxc(cx, cmd, arg);
+		if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+			return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
+		return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
+
+	case VIDIOC_DBG_S_REGISTER:
+		if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+			return cx18_cxc(cx, cmd, arg);
+		if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+			return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
+		return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
+
+	case VIDIOC_G_CHIP_IDENT: {
+		struct v4l2_chip_ident *chip = arg;
+
+		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 = V4L2_IDENT_CX23418;
+			}
+			return 0;
+		}
+		if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+			return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
+		if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
+			return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
+		return -EINVAL;
+	}
+
+	case VIDIOC_INT_S_AUDIO_ROUTING: {
+		struct v4l2_routing *route = arg;
+
+		cx18_audio_set_route(cx, route);
+		break;
+	}
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd, void *arg)
+{
+	struct cx18_open_id *id = NULL;
+
+	if (filp)
+		id = (struct cx18_open_id *)filp->private_data;
+
+	switch (cmd) {
+	case VIDIOC_G_PRIORITY:
+	{
+		enum v4l2_priority *p = arg;
+
+		*p = v4l2_prio_max(&cx->prio);
+		break;
+	}
+
+	case VIDIOC_S_PRIORITY:
+	{
+		enum v4l2_priority *prio = arg;
+
+		return v4l2_prio_change(&cx->prio, &id->prio, *prio);
+	}
+
+	case VIDIOC_QUERYCAP:{
+		struct v4l2_capability *vcap = arg;
+
+		memset(vcap, 0, sizeof(*vcap));
+		strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
+		strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
+		strlcpy(vcap->bus_info, pci_name(cx->dev), sizeof(vcap->bus_info));
+		vcap->version = CX18_DRIVER_VERSION; 	    /* version */
+		vcap->capabilities = cx->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;
+
+		return cx18_get_audio_input(cx, vin->index, vin);
+	}
+
+	case VIDIOC_G_AUDIO:{
+		struct v4l2_audio *vin = arg;
+
+		vin->index = cx->audio_input;
+		return cx18_get_audio_input(cx, vin->index, vin);
+	}
+
+	case VIDIOC_S_AUDIO:{
+		struct v4l2_audio *vout = arg;
+
+		if (vout->index >= cx->nof_audio_inputs)
+			return -EINVAL;
+		cx->audio_input = vout->index;
+		cx18_audio_set_io(cx);
+		break;
+	}
+
+	case VIDIOC_ENUMINPUT:{
+		struct v4l2_input *vin = arg;
+
+		/* set it to defaults from our table */
+		return cx18_get_input(cx, vin->index, vin);
+	}
+
+	case VIDIOC_TRY_FMT:
+	case VIDIOC_S_FMT: {
+		struct v4l2_format *fmt = arg;
+
+		return cx18_try_or_set_fmt(cx, id->type, fmt, cmd == VIDIOC_S_FMT);
+	}
+
+	case VIDIOC_G_FMT: {
+		struct v4l2_format *fmt = arg;
+		int type = fmt->type;
+
+		memset(fmt, 0, sizeof(*fmt));
+		fmt->type = type;
+		return cx18_get_fmt(cx, id->type, fmt);
+	}
+
+	case VIDIOC_CROPCAP: {
+		struct v4l2_cropcap *cropcap = arg;
+
+		if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		cropcap->bounds.top = cropcap->bounds.left = 0;
+		cropcap->bounds.width = 720;
+		cropcap->bounds.height = cx->is_50hz ? 576 : 480;
+		cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;
+		cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;
+		cropcap->defrect = cropcap->bounds;
+		return 0;
+	}
+
+	case VIDIOC_S_CROP: {
+		struct v4l2_crop *crop = arg;
+
+		if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		return cx18_av_cmd(cx, VIDIOC_S_CROP, arg);
+	}
+
+	case VIDIOC_G_CROP: {
+		struct v4l2_crop *crop = arg;
+
+		if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		return cx18_av_cmd(cx, 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;
+		default:
+			return -EINVAL;
+		}
+		if (fmt->index > 1)
+			return -EINVAL;
+		*fmt = formats[fmt->index];
+		fmt->type = type;
+		return 0;
+	}
+
+	case VIDIOC_G_INPUT:{
+		*(int *)arg = cx->active_input;
+		break;
+	}
+
+	case VIDIOC_S_INPUT:{
+		int inp = *(int *)arg;
+
+		if (inp < 0 || inp >= cx->nof_inputs)
+			return -EINVAL;
+
+		if (inp == cx->active_input) {
+			CX18_DEBUG_INFO("Input unchanged\n");
+			break;
+		}
+		CX18_DEBUG_INFO("Changing input from %d to %d\n",
+				cx->active_input, inp);
+
+		cx->active_input = inp;
+		/* Set the audio input to whatever is appropriate for the
+		   input type. */
+		cx->audio_input = cx->card->video_inputs[inp].audio_index;
+
+		/* prevent others from messing with the streams until
+		   we're finished changing inputs. */
+		cx18_mute(cx);
+		cx18_video_set_io(cx);
+		cx18_audio_set_io(cx);
+		cx18_unmute(cx);
+		break;
+	}
+
+	case VIDIOC_G_FREQUENCY:{
+		struct v4l2_frequency *vf = arg;
+
+		if (vf->tuner != 0)
+			return -EINVAL;
+		cx18_call_i2c_clients(cx, cmd, arg);
+		break;
+	}
+
+	case VIDIOC_S_FREQUENCY:{
+		struct v4l2_frequency vf = *(struct v4l2_frequency *)arg;
+
+		if (vf.tuner != 0)
+			return -EINVAL;
+
+		cx18_mute(cx);
+		CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency);
+		cx18_call_i2c_clients(cx, cmd, &vf);
+		cx18_unmute(cx);
+		break;
+	}
+
+	case VIDIOC_ENUMSTD:{
+		struct v4l2_standard *vs = arg;
+		int idx = vs->index;
+
+		if (idx < 0 || idx >= ARRAY_SIZE(enum_stds))
+			return -EINVAL;
+
+		*vs = (enum_stds[idx].std & V4L2_STD_525_60) ?
+				cx18_std_60hz : cx18_std_50hz;
+		vs->index = idx;
+		vs->id = enum_stds[idx].std;
+		strlcpy(vs->name, enum_stds[idx].name, sizeof(vs->name));
+		break;
+	}
+
+	case VIDIOC_G_STD:{
+		*(v4l2_std_id *) arg = cx->std;
+		break;
+	}
+
+	case VIDIOC_S_STD: {
+		v4l2_std_id std = *(v4l2_std_id *) arg;
+
+		if ((std & V4L2_STD_ALL) == 0)
+			return -EINVAL;
+
+		if (std == cx->std)
+			break;
+
+		if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
+		    atomic_read(&cx->capturing) > 0) {
+			/* Switching standard would turn off the radio or mess
+			   with already running streams, prevent that by
+			   returning EBUSY. */
+			return -EBUSY;
+		}
+
+		cx->std = std;
+		cx->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0;
+		cx->params.is_50hz = cx->is_50hz = !cx->is_60hz;
+		cx->params.width = 720;
+		cx->params.height = cx->is_50hz ? 576 : 480;
+		cx->vbi.count = cx->is_50hz ? 18 : 12;
+		cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
+		cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
+		cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284;
+		CX18_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)cx->std);
+
+		/* Tuner */
+		cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
+		break;
+	}
+
+	case VIDIOC_S_TUNER: {	/* Setting tuner can only set audio mode */
+		struct v4l2_tuner *vt = arg;
+
+		if (vt->index != 0)
+			return -EINVAL;
+
+		cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt);
+		break;
+	}
+
+	case VIDIOC_G_TUNER: {
+		struct v4l2_tuner *vt = arg;
+
+		if (vt->index != 0)
+			return -EINVAL;
+
+		memset(vt, 0, sizeof(*vt));
+		cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt);
+
+		if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
+			strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
+			vt->type = V4L2_TUNER_RADIO;
+		} else {
+			strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
+			vt->type = V4L2_TUNER_ANALOG_TV;
+		}
+		break;
+	}
+
+	case VIDIOC_G_SLICED_VBI_CAP: {
+		struct v4l2_sliced_vbi_cap *cap = arg;
+		int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+		int f, l;
+		enum v4l2_buf_type type = cap->type;
+
+		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, cx->is_50hz))
+						cap->service_lines[f][l] = set;
+				}
+			}
+			return 0;
+		}
+		return -EINVAL;
+	}
+
+	case VIDIOC_ENCODER_CMD:
+	case VIDIOC_TRY_ENCODER_CMD: {
+		struct v4l2_encoder_cmd *enc = arg;
+		int try = cmd == VIDIOC_TRY_ENCODER_CMD;
+
+		memset(&enc->raw, 0, sizeof(enc->raw));
+		switch (enc->cmd) {
+		case V4L2_ENC_CMD_START:
+			enc->flags = 0;
+			if (try)
+				return 0;
+			return cx18_start_capture(id);
+
+		case V4L2_ENC_CMD_STOP:
+			enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+			if (try)
+				return 0;
+			cx18_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
+			return 0;
+
+		case V4L2_ENC_CMD_PAUSE:
+			enc->flags = 0;
+			if (try)
+				return 0;
+			if (!atomic_read(&cx->capturing))
+				return -EPERM;
+			if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+				return 0;
+			cx18_mute(cx);
+			cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, cx18_find_handle(cx));
+			break;
+
+		case V4L2_ENC_CMD_RESUME:
+			enc->flags = 0;
+			if (try)
+				return 0;
+			if (!atomic_read(&cx->capturing))
+				return -EPERM;
+			if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+				return 0;
+			cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, cx18_find_handle(cx));
+			cx18_unmute(cx);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	}
+
+	case VIDIOC_LOG_STATUS:
+	{
+		struct v4l2_input vidin;
+		struct v4l2_audio audin;
+		int i;
+
+		CX18_INFO("=================  START STATUS CARD #%d  =================\n", cx->num);
+		if (cx->hw_flags & CX18_HW_TVEEPROM) {
+			struct tveeprom tv;
+
+			cx18_read_eeprom(cx, &tv);
+		}
+		cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL);
+		cx18_get_input(cx, cx->active_input, &vidin);
+		cx18_get_audio_input(cx, cx->audio_input, &audin);
+		CX18_INFO("Video Input: %s\n", vidin.name);
+		CX18_INFO("Audio Input: %s\n", audin.name);
+		CX18_INFO("Tuner: %s\n",
+			test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ?
+			"Radio" : "TV");
+		cx2341x_log_status(&cx->params, cx->name);
+		CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
+		for (i = 0; i < CX18_MAX_STREAMS; i++) {
+			struct cx18_stream *s = &cx->streams[i];
+
+			if (s->v4l2dev == NULL || s->buffers == 0)
+				continue;
+			CX18_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);
+		}
+		CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
+				(long long)cx->mpg_data_received,
+				(long long)cx->vbi_data_inserted);
+		CX18_INFO("==================  END STATUS CARD #%d  ==================\n", cx->num);
+		break;
+	}
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int cx18_v4l2_do_ioctl(struct inode *inode, struct file *filp,
+			      unsigned int cmd, void *arg)
+{
+	struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
+	struct cx18 *cx = id->cx;
+	int ret;
+
+	/* check priority */
+	switch (cmd) {
+	case VIDIOC_S_CTRL:
+	case VIDIOC_S_STD:
+	case VIDIOC_S_INPUT:
+	case VIDIOC_S_TUNER:
+	case VIDIOC_S_FREQUENCY:
+	case VIDIOC_S_FMT:
+	case VIDIOC_S_CROP:
+	case VIDIOC_S_EXT_CTRLS:
+		ret = v4l2_prio_check(&cx->prio, &id->prio);
+		if (ret)
+			return ret;
+	}
+
+	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:
+		if (cx18_debug & CX18_DBGFLG_IOCTL) {
+			printk(KERN_INFO "cx18%d ioctl: ", cx->num);
+			v4l_printk_ioctl(cmd);
+		}
+		return cx18_debug_ioctls(filp, cmd, arg);
+
+	case VIDIOC_G_PRIORITY:
+	case VIDIOC_S_PRIORITY:
+	case VIDIOC_QUERYCAP:
+	case VIDIOC_ENUMINPUT:
+	case VIDIOC_G_INPUT:
+	case VIDIOC_S_INPUT:
+	case VIDIOC_G_FMT:
+	case VIDIOC_S_FMT:
+	case VIDIOC_TRY_FMT:
+	case VIDIOC_ENUM_FMT:
+	case VIDIOC_CROPCAP:
+	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_G_SLICED_VBI_CAP:
+	case VIDIOC_LOG_STATUS:
+	case VIDIOC_G_ENC_INDEX:
+	case VIDIOC_ENCODER_CMD:
+	case VIDIOC_TRY_ENCODER_CMD:
+		if (cx18_debug & CX18_DBGFLG_IOCTL) {
+			printk(KERN_INFO "cx18%d ioctl: ", cx->num);
+			v4l_printk_ioctl(cmd);
+		}
+		return cx18_v4l2_ioctls(cx, 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:
+		if (cx18_debug & CX18_DBGFLG_IOCTL) {
+			printk(KERN_INFO "cx18%d ioctl: ", cx->num);
+			v4l_printk_ioctl(cmd);
+		}
+		return cx18_control_ioctls(cx, cmd, arg);
+
+	case 0x00005401:	/* Handle isatty() calls */
+		return -EINVAL;
+	default:
+		return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
+						   cx18_v4l2_do_ioctl);
+	}
+	return 0;
+}
+
+int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
+	struct cx18 *cx = id->cx;
+	int res;
+
+	mutex_lock(&cx->serialize_lock);
+	res = video_usercopy(inode, filp, cmd, arg, cx18_v4l2_do_ioctl);
+	mutex_unlock(&cx->serialize_lock);
+	return res;
+}
diff --git a/drivers/media/video/cx18/cx18-ioctl.h b/drivers/media/video/cx18/cx18-ioctl.h
new file mode 100644
index 0000000..9f4c7eb
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-ioctl.h
@@ -0,0 +1,30 @@
+/*
+ *  cx18 ioctl system call
+ *
+ *  Derived from ivtv-ioctl.h
+ *
+ *  Copyright (C) 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 cx18_service2vbi(int type);
+void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
+u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt);
+int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg);
+int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd,
+		     void *arg);
diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c
new file mode 100644
index 0000000..6e14f8b
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-irq.c
@@ -0,0 +1,179 @@
+/*
+ *  cx18 interrupt handling
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-firmware.h"
+#include "cx18-fileops.h"
+#include "cx18-queue.h"
+#include "cx18-irq.h"
+#include "cx18-ioctl.h"
+#include "cx18-mailbox.h"
+#include "cx18-vbi.h"
+#include "cx18-scb.h"
+
+#define DMA_MAGIC_COOKIE 0x000001fe
+
+static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb)
+{
+	u32 handle = mb->args[0];
+	struct cx18_stream *s = NULL;
+	struct cx18_buffer *buf;
+	u32 off;
+	int i;
+	int id;
+
+	for (i = 0; i < CX18_MAX_STREAMS; i++) {
+		s = &cx->streams[i];
+		if ((handle == s->handle) && (s->dvb.enabled))
+			break;
+		if (s->v4l2dev && handle == s->handle)
+			break;
+	}
+	if (i == CX18_MAX_STREAMS) {
+		CX18_WARN("DMA done for unknown handle %d for stream %s\n",
+			handle, s->name);
+		mb->error = CXERR_NOT_OPEN;
+		mb->cmd = 0;
+		cx18_mb_ack(cx, mb);
+		return;
+	}
+
+	off = mb->args[1];
+	if (mb->args[2] != 1)
+		CX18_WARN("Ack struct = %d for %s\n",
+			mb->args[2], s->name);
+	id = read_enc(off);
+	buf = cx18_queue_find_buf(s, id, read_enc(off + 4));
+	CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
+	if (buf) {
+		cx18_buf_sync_for_cpu(s, buf);
+		if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
+			/* process the buffer here */
+			CX18_DEBUG_HI_DMA("TS recv and sent bytesused=%d\n",
+					buf->bytesused);
+
+			dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
+					buf->bytesused);
+
+			cx18_buf_sync_for_device(s, buf);
+			cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
+			    (void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
+			    1, buf->id, s->buf_size);
+		} else
+			set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
+	} else {
+		CX18_WARN("Could not find buf %d for stream %s\n",
+				read_enc(off), s->name);
+	}
+	mb->error = 0;
+	mb->cmd = 0;
+	cx18_mb_ack(cx, mb);
+	wake_up(&cx->dma_waitq);
+	if (s->id != -1)
+		wake_up(&s->waitq);
+}
+
+static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb)
+{
+	char str[256] = { 0 };
+	char *p;
+
+	if (mb->args[1]) {
+		setup_page(mb->args[1]);
+		memcpy_fromio(str, cx->enc_mem + mb->args[1], 252);
+		str[252] = 0;
+	}
+	cx18_mb_ack(cx, mb);
+	CX18_DEBUG_INFO("%x %s\n", mb->args[0], str);
+	p = strchr(str, '.');
+	if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
+		CX18_INFO("FW version: %s\n", p - 1);
+}
+
+static void hpu_cmd(struct cx18 *cx, u32 sw1)
+{
+	struct cx18_mailbox mb;
+
+	if (sw1 & IRQ_CPU_TO_EPU) {
+		memcpy_fromio(&mb, &cx->scb->cpu2epu_mb, sizeof(mb));
+		mb.error = 0;
+
+		switch (mb.cmd) {
+		case CX18_EPU_DMA_DONE:
+			epu_dma_done(cx, &mb);
+			break;
+		case CX18_EPU_DEBUG:
+			epu_debug(cx, &mb);
+			break;
+		default:
+			CX18_WARN("Unexpected mailbox command %08x\n", mb.cmd);
+			break;
+		}
+	}
+	if (sw1 & (IRQ_APU_TO_EPU | IRQ_HPU_TO_EPU))
+		CX18_WARN("Unexpected interrupt %08x\n", sw1);
+}
+
+irqreturn_t cx18_irq_handler(int irq, void *dev_id)
+{
+	struct cx18 *cx = (struct cx18 *)dev_id;
+	u32 sw1, sw1_mask;
+	u32 sw2, sw2_mask;
+	u32 hw2, hw2_mask;
+
+	spin_lock(&cx->dma_reg_lock);
+
+	hw2_mask = read_reg(HW2_INT_MASK5_PCI);
+	hw2 = read_reg(HW2_INT_CLR_STATUS) & hw2_mask;
+	sw2_mask = read_reg(SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK;
+	sw2 = read_reg(SW2_INT_STATUS) & sw2_mask;
+	sw1_mask = read_reg(SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU;
+	sw1 = read_reg(SW1_INT_STATUS) & sw1_mask;
+
+	write_reg(sw2&sw2_mask, SW2_INT_STATUS);
+	write_reg(sw1&sw1_mask, SW1_INT_STATUS);
+	write_reg(hw2&hw2_mask, HW2_INT_CLR_STATUS);
+
+	if (sw1 || sw2 || hw2)
+		CX18_DEBUG_HI_IRQ("SW1: %x  SW2: %x  HW2: %x\n", sw1, sw2, hw2);
+
+	/* To do: interrupt-based I2C handling
+	if (hw2 & 0x00c00000) {
+	}
+	*/
+
+	if (sw2) {
+		if (sw2 & (cx->scb->cpu2hpu_irq_ack | cx->scb->cpu2epu_irq_ack))
+			wake_up(&cx->mb_cpu_waitq);
+		if (sw2 & (cx->scb->apu2hpu_irq_ack | cx->scb->apu2epu_irq_ack))
+			wake_up(&cx->mb_apu_waitq);
+		if (sw2 & cx->scb->epu2hpu_irq_ack)
+			wake_up(&cx->mb_epu_waitq);
+		if (sw2 & cx->scb->hpu2epu_irq_ack)
+			wake_up(&cx->mb_hpu_waitq);
+	}
+
+	if (sw1)
+		hpu_cmd(cx, sw1);
+	spin_unlock(&cx->dma_reg_lock);
+
+	return (hw2 | sw1 | sw2) ? IRQ_HANDLED : IRQ_NONE;
+}
diff --git a/drivers/media/video/cx18/cx18-irq.h b/drivers/media/video/cx18/cx18-irq.h
new file mode 100644
index 0000000..379f704
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-irq.h
@@ -0,0 +1,37 @@
+/*
+ *  cx18 interrupt handling
+ *
+ *  Copyright (C) 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 HW2_I2C1_INT			(1 << 22)
+#define HW2_I2C2_INT			(1 << 23)
+#define HW2_INT_CLR_STATUS		0xc730c4
+#define HW2_INT_MASK5_PCI		0xc730e4
+#define SW1_INT_SET                     0xc73100
+#define SW1_INT_STATUS                  0xc73104
+#define SW1_INT_ENABLE_PCI              0xc7311c
+#define SW2_INT_SET                     0xc73140
+#define SW2_INT_STATUS                  0xc73144
+#define SW2_INT_ENABLE_PCI              0xc7315c
+
+irqreturn_t cx18_irq_handler(int irq, void *dev_id);
+
+void cx18_irq_work_handler(struct work_struct *work);
+void cx18_dma_stream_dec_prepare(struct cx18_stream *s, u32 offset, int lock);
+void cx18_unfinished_dma(unsigned long arg);
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
new file mode 100644
index 0000000..0c5f328
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -0,0 +1,372 @@
+/*
+ *  cx18 mailbox functions
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-scb.h"
+#include "cx18-irq.h"
+#include "cx18-mailbox.h"
+
+#define API_FAST (1 << 2) /* Short timeout */
+#define API_SLOW (1 << 3) /* Additional 300ms timeout */
+
+#define APU 0
+#define CPU 1
+#define EPU 2
+#define HPU 3
+
+struct cx18_api_info {
+	u32 cmd;
+	u8 flags;		/* Flags, see above */
+	u8 rpu;			/* Processing unit */
+	const char *name; 	/* The name of the command */
+};
+
+#define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x }
+
+static const struct cx18_api_info api_info[] = {
+	/* MPEG encoder API */
+	API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE,		0),
+	API_ENTRY(CPU, CX18_EPU_DEBUG, 				0),
+	API_ENTRY(CPU, CX18_CREATE_TASK, 			0),
+	API_ENTRY(CPU, CX18_DESTROY_TASK, 			0),
+	API_ENTRY(CPU, CX18_CPU_CAPTURE_START,                  API_SLOW),
+	API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP,                   API_SLOW),
+	API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE,                  0),
+	API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME,                 0),
+	API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE,               0),
+	API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE,         0),
+	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN,                   0),
+	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE,                 0),
+	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION,           0),
+	API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM,               0),
+	API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE,        0),
+	API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING,              0),
+	API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE,                 0),
+	API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS,           0),
+	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE,                 0),
+	API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE,                 0),
+	API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS,            0),
+	API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM,              API_SLOW),
+	API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO,            0),
+	API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT,                  0),
+	API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID,                  0),
+	API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID,                  0),
+	API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE,              0),
+	API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE,              0),
+	API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION,     0),
+	API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO,               0),
+	API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME,           0),
+	API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM,           0),
+	API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER,      0),
+	API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS,                    0),
+	API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK,			0),
+	API_ENTRY(CPU, CX18_CPU_DE_SET_MDL,			API_FAST),
+	API_ENTRY(0, 0,						0),
+};
+
+static const struct cx18_api_info *find_api_info(u32 cmd)
+{
+	int i;
+
+	for (i = 0; api_info[i].cmd; i++)
+		if (api_info[i].cmd == cmd)
+			return &api_info[i];
+	return NULL;
+}
+
+static struct cx18_mailbox *cx18_mb_is_complete(struct cx18 *cx, int rpu,
+		u32 *state, u32 *irq, u32 *req)
+{
+	struct cx18_mailbox *mb = NULL;
+	int wait_count = 0;
+	u32 ack;
+
+	switch (rpu) {
+	case APU:
+		mb = &cx->scb->epu2apu_mb;
+		*state = readl(&cx->scb->apu_state);
+		*irq = readl(&cx->scb->epu2apu_irq);
+		break;
+
+	case CPU:
+		mb = &cx->scb->epu2cpu_mb;
+		*state = readl(&cx->scb->cpu_state);
+		*irq = readl(&cx->scb->epu2cpu_irq);
+		break;
+
+	case HPU:
+		mb = &cx->scb->epu2hpu_mb;
+		*state = readl(&cx->scb->hpu_state);
+		*irq = readl(&cx->scb->epu2hpu_irq);
+		break;
+	}
+
+	if (mb == NULL)
+		return mb;
+
+	do {
+		*req = readl(&mb->request);
+		ack = readl(&mb->ack);
+		wait_count++;
+	} while (*req != ack && wait_count < 600);
+
+	if (*req == ack) {
+		(*req)++;
+		if (*req == 0 || *req == 0xffffffff)
+			*req = 1;
+		return mb;
+	}
+	return NULL;
+}
+
+long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb)
+{
+	const struct cx18_api_info *info = find_api_info(mb->cmd);
+	struct cx18_mailbox *ack_mb;
+	u32 ack_irq;
+	u8 rpu = CPU;
+
+	if (info == NULL && mb->cmd) {
+		CX18_WARN("Cannot ack unknown command %x\n", mb->cmd);
+		return -EINVAL;
+	}
+	if (info)
+		rpu = info->rpu;
+
+	switch (rpu) {
+	case HPU:
+		ack_irq = IRQ_EPU_TO_HPU_ACK;
+		ack_mb = &cx->scb->hpu2epu_mb;
+		break;
+	case APU:
+		ack_irq = IRQ_EPU_TO_APU_ACK;
+		ack_mb = &cx->scb->apu2epu_mb;
+		break;
+	case CPU:
+		ack_irq = IRQ_EPU_TO_CPU_ACK;
+		ack_mb = &cx->scb->cpu2epu_mb;
+		break;
+	default:
+		CX18_WARN("Unknown RPU for command %x\n", mb->cmd);
+		return -EINVAL;
+	}
+
+	setup_page(SCB_OFFSET);
+	write_sync(mb->request, &ack_mb->ack);
+	write_reg(ack_irq, SW2_INT_SET);
+	return 0;
+}
+
+
+static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
+{
+	const struct cx18_api_info *info = find_api_info(cmd);
+	u32 state = 0, irq = 0, req, oldreq, err;
+	struct cx18_mailbox *mb;
+	wait_queue_head_t *waitq;
+	int timeout = 100;
+	int cnt = 0;
+	int sig = 0;
+	int i;
+
+	if (info == NULL) {
+		CX18_WARN("unknown cmd %x\n", cmd);
+		return -EINVAL;
+	}
+
+	if (cmd == CX18_CPU_DE_SET_MDL)
+		CX18_DEBUG_HI_API("%s\n", info->name);
+	else
+		CX18_DEBUG_API("%s\n", info->name);
+	setup_page(SCB_OFFSET);
+	mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req);
+
+	if (mb == NULL) {
+		CX18_ERR("mb %s busy\n", info->name);
+		return -EBUSY;
+	}
+
+	oldreq = req - 1;
+	writel(cmd, &mb->cmd);
+	for (i = 0; i < args; i++)
+		writel(data[i], &mb->args[i]);
+	writel(0, &mb->error);
+	writel(req, &mb->request);
+
+	switch (info->rpu) {
+	case APU: waitq = &cx->mb_apu_waitq; break;
+	case CPU: waitq = &cx->mb_cpu_waitq; break;
+	case EPU: waitq = &cx->mb_epu_waitq; break;
+	case HPU: waitq = &cx->mb_hpu_waitq; break;
+	default: return -EINVAL;
+	}
+	if (info->flags & API_FAST)
+		timeout /= 2;
+	write_reg(irq, SW1_INT_SET);
+
+	while (!sig && readl(&mb->ack) != readl(&mb->request) && cnt < 660) {
+		if (cnt > 200 && !in_atomic())
+			sig = cx18_msleep_timeout(10, 1);
+		cnt++;
+	}
+	if (sig)
+		return -EINTR;
+	if (cnt == 660) {
+		writel(oldreq, &mb->request);
+		CX18_ERR("mb %s failed\n", info->name);
+		return -EINVAL;
+	}
+	for (i = 0; i < MAX_MB_ARGUMENTS; i++)
+		data[i] = readl(&mb->args[i]);
+	err = readl(&mb->error);
+	if (!in_atomic() && (info->flags & API_SLOW))
+		cx18_msleep_timeout(300, 0);
+	if (err)
+		CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
+				info->name);
+	return err ? -EIO : 0;
+}
+
+int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])
+{
+	int res = cx18_api_call(cx, 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) ? cx18_api_call(cx, cmd, args, data) : res;
+}
+
+static int cx18_set_filter_param(struct cx18_stream *s)
+{
+	struct cx18 *cx = s->cx;
+	u32 mode;
+	int ret;
+
+	mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0);
+	ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
+			s->handle, 1, mode, cx->spatial_strength);
+	mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0);
+	ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
+			s->handle, 0, mode, cx->temporal_strength);
+	ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
+			s->handle, 2, cx->filter_mode >> 2, 0);
+	return ret;
+}
+
+int cx18_api_func(void *priv, u32 cmd, int in, int out,
+		u32 data[CX2341X_MBOX_MAX_DATA])
+{
+	struct cx18 *cx = priv;
+	struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+
+	switch (cmd) {
+	case CX2341X_ENC_SET_OUTPUT_PORT:
+		return 0;
+	case CX2341X_ENC_SET_FRAME_RATE:
+		return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6,
+				s->handle, 0, 0, 0, 0, data[0]);
+	case CX2341X_ENC_SET_FRAME_SIZE:
+		return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3,
+				s->handle, data[1], data[0]);
+	case CX2341X_ENC_SET_STREAM_TYPE:
+		return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2,
+				s->handle, data[0]);
+	case CX2341X_ENC_SET_ASPECT_RATIO:
+		return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2,
+				s->handle, data[0]);
+
+	case CX2341X_ENC_SET_GOP_PROPERTIES:
+		return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3,
+				s->handle, data[0], data[1]);
+	case CX2341X_ENC_SET_GOP_CLOSURE:
+		return 0;
+	case CX2341X_ENC_SET_AUDIO_PROPERTIES:
+		return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,
+				s->handle, data[0]);
+	case CX2341X_ENC_MUTE_AUDIO:
+		return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
+				s->handle, data[0]);
+	case CX2341X_ENC_SET_BIT_RATE:
+		return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5,
+				s->handle, data[0], data[1], data[2], data[3]);
+	case CX2341X_ENC_MUTE_VIDEO:
+		return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
+				s->handle, data[0]);
+	case CX2341X_ENC_SET_FRAME_DROP_RATE:
+		return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2,
+				s->handle, data[0]);
+	case CX2341X_ENC_MISC:
+		return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4,
+				s->handle, data[0], data[1], data[2]);
+	case CX2341X_ENC_SET_DNR_FILTER_MODE:
+		cx->filter_mode = (data[0] & 3) | (data[1] << 2);
+		return cx18_set_filter_param(s);
+	case CX2341X_ENC_SET_DNR_FILTER_PROPS:
+		cx->spatial_strength = data[0];
+		cx->temporal_strength = data[1];
+		return cx18_set_filter_param(s);
+	case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
+		return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3,
+				s->handle, data[0], data[1]);
+	case CX2341X_ENC_SET_CORING_LEVELS:
+		return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5,
+				s->handle, data[0], data[1], data[2], data[3]);
+	}
+	CX18_WARN("Unknown cmd %x\n", cmd);
+	return 0;
+}
+
+int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS],
+		u32 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 cx18_api(cx, cmd, args, data);
+}
+
+int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...)
+{
+	u32 data[MAX_MB_ARGUMENTS];
+	va_list ap;
+	int i;
+
+	if (cx == NULL) {
+		CX18_ERR("cx == NULL (cmd=%x)\n", cmd);
+		return 0;
+	}
+	if (args > MAX_MB_ARGUMENTS) {
+		CX18_ERR("args too big (cmd=%x)\n", cmd);
+		args = MAX_MB_ARGUMENTS;
+	}
+	va_start(ap, args);
+	for (i = 0; i < args; i++)
+		data[i] = va_arg(ap, u32);
+	va_end(ap);
+	return cx18_api(cx, cmd, args, data);
+}
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h
new file mode 100644
index 0000000..d995641
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-mailbox.h
@@ -0,0 +1,73 @@
+/*
+ *  cx18 mailbox functions
+ *
+ *  Copyright (C) 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 _CX18_MAILBOX_H_
+#define _CX18_MAILBOX_H_
+
+/* mailbox max args */
+#define MAX_MB_ARGUMENTS 6
+/* compatibility, should be same as the define in cx2341x.h */
+#define CX2341X_MBOX_MAX_DATA 16
+
+#define MB_RESERVED_HANDLE_0 0
+#define MB_RESERVED_HANDLE_1 0xFFFFFFFF
+
+struct cx18;
+
+/* The cx18_mailbox struct is the mailbox structure which is used for passing
+   messages between processors */
+struct cx18_mailbox {
+    /* The sender sets a handle in 'request' after he fills the command. The
+       'request' should be different than 'ack'. The sender, also, generates
+       an interrupt on XPU2YPU_irq where XPU is the sender and YPU is the
+       receiver. */
+    u32       request;
+    /* The receiver detects a new command when 'req' is different than 'ack'.
+       He sets 'ack' to the same value as 'req' to clear the command. He, also,
+       generates an interrupt on YPU2XPU_irq where XPU is the sender and YPU
+       is the receiver. */
+    u32       ack;
+    u32       reserved[6];
+    /* 'cmd' identifies the command. The list of these commands are in
+       cx23418.h */
+    u32       cmd;
+    /* Each command can have up to 6 arguments */
+    u32       args[MAX_MB_ARGUMENTS];
+    /* The return code can be one of the codes in the file cx23418.h. If the
+       command is completed successfuly, the error will be ERR_SYS_SUCCESS.
+       If it is pending, the code is ERR_SYS_PENDING. If it failed, the error
+       code would indicate the task from which the error originated and will
+       be one of the errors in cx23418.h. In that case, the following
+       applies ((error & 0xff) != 0).
+       If the command is pending, the return will be passed in a MB from the
+       receiver to the sender. 'req' will be returned in args[0] */
+    u32       error;
+};
+
+int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]);
+int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd,
+		int args, ...);
+int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...);
+int cx18_api_func(void *priv, u32 cmd, int in, int out,
+		u32 data[CX2341X_MBOX_MAX_DATA]);
+long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb);
+
+#endif
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
new file mode 100644
index 0000000..65af1bb
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -0,0 +1,282 @@
+/*
+ *  cx18 buffer queues
+ *
+ *  Derived from ivtv-queue.c
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-streams.h"
+#include "cx18-queue.h"
+#include "cx18-scb.h"
+
+int cx18_buf_copy_from_user(struct cx18_stream *s, struct cx18_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 cx18_buf_swap(struct cx18_buffer *buf)
+{
+	int i;
+
+	for (i = 0; i < buf->bytesused; i += 4)
+		swab32s((u32 *)(buf->buf + i));
+}
+
+void cx18_queue_init(struct cx18_queue *q)
+{
+	INIT_LIST_HEAD(&q->list);
+	q->buffers = 0;
+	q->length = 0;
+	q->bytesused = 0;
+}
+
+void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+		struct cx18_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 cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
+{
+	struct cx18_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 cx18_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;
+}
+
+struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id,
+	u32 bytesused)
+{
+	struct cx18 *cx = s->cx;
+	struct list_head *p;
+
+	list_for_each(p, &s->q_free.list) {
+		struct cx18_buffer *buf =
+			list_entry(p, struct cx18_buffer, list);
+
+		if (buf->id != id)
+			continue;
+		buf->bytesused = bytesused;
+		/* the transport buffers are handled differently,
+		   so there is no need to move them to the full queue */
+		if (s->type == CX18_ENC_STREAM_TYPE_TS)
+			return buf;
+		s->q_free.buffers--;
+		s->q_free.length -= s->buf_size;
+		s->q_full.buffers++;
+		s->q_full.length += s->buf_size;
+		s->q_full.bytesused += buf->bytesused;
+		list_move_tail(&buf->list, &s->q_full.list);
+		return buf;
+	}
+	CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
+	return NULL;
+}
+
+static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from,
+		struct cx18_queue *to, int clear, int full)
+{
+	struct cx18_buffer *buf =
+		list_entry(from->list.next, struct cx18_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 cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
+	struct cx18_queue *steal, struct cx18_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 */
+			cx18_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 */
+			cx18_queue_move_buf(s, from, to, to_free, rc);
+		}
+	}
+	spin_unlock_irqrestore(&s->qlock, flags);
+	return rc;
+}
+
+void cx18_flush_queues(struct cx18_stream *s)
+{
+	cx18_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
+	cx18_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
+}
+
+int cx18_stream_alloc(struct cx18_stream *s)
+{
+	struct cx18 *cx = s->cx;
+	int i;
+
+	if (s->buffers == 0)
+		return 0;
+
+	CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n",
+		s->name, s->buffers, s->buf_size,
+		s->buffers * s->buf_size / 1024);
+
+	if (((char *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] -
+				(char *)cx->scb) > SCB_RESERVED_SIZE) {
+		unsigned bufsz = (((char *)cx->scb) + SCB_RESERVED_SIZE -
+					((char *)cx->scb->cpu_mdl));
+
+		CX18_ERR("Too many buffers, cannot fit in SCB area\n");
+		CX18_ERR("Max buffers = %zd\n",
+			bufsz / sizeof(struct cx18_mdl));
+		return -ENOMEM;
+	}
+
+	s->mdl_offset = cx->mdl_offset;
+
+	/* allocate stream buffers. Initially all buffers are in q_free. */
+	for (i = 0; i < s->buffers; i++) {
+		struct cx18_buffer *buf =
+			kzalloc(sizeof(struct cx18_buffer), GFP_KERNEL);
+
+		if (buf == NULL)
+			break;
+		buf->buf = kmalloc(s->buf_size, GFP_KERNEL);
+		if (buf->buf == NULL) {
+			kfree(buf);
+			break;
+		}
+		buf->id = cx->buffer_id++;
+		INIT_LIST_HEAD(&buf->list);
+		buf->dma_handle = pci_map_single(s->cx->dev,
+				buf->buf, s->buf_size, s->dma);
+		cx18_buf_sync_for_cpu(s, buf);
+		cx18_enqueue(s, buf, &s->q_free);
+	}
+	if (i == s->buffers) {
+		cx->mdl_offset += s->buffers;
+		return 0;
+	}
+	CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
+	cx18_stream_free(s);
+	return -ENOMEM;
+}
+
+void cx18_stream_free(struct cx18_stream *s)
+{
+	struct cx18_buffer *buf;
+
+	/* move all buffers to q_free */
+	cx18_flush_queues(s);
+
+	/* empty q_free */
+	while ((buf = cx18_dequeue(s, &s->q_free))) {
+		pci_unmap_single(s->cx->dev, buf->dma_handle,
+				s->buf_size, s->dma);
+		kfree(buf->buf);
+		kfree(buf);
+	}
+}
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h
new file mode 100644
index 0000000..f86c8a6
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-queue.h
@@ -0,0 +1,59 @@
+/*
+ *  cx18 buffer queues
+ *
+ *  Derived from ivtv-queue.h
+ *
+ *  Copyright (C) 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 CX18_DMA_UNMAPPED	((u32) -1)
+
+/* cx18_buffer utility functions */
+
+static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s,
+	struct cx18_buffer *buf)
+{
+	pci_dma_sync_single_for_cpu(s->cx->dev, buf->dma_handle,
+				s->buf_size, s->dma);
+}
+
+static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
+	struct cx18_buffer *buf)
+{
+	pci_dma_sync_single_for_device(s->cx->dev, buf->dma_handle,
+				s->buf_size, s->dma);
+}
+
+int cx18_buf_copy_from_user(struct cx18_stream *s, struct cx18_buffer *buf,
+	const char __user *src, int copybytes);
+void cx18_buf_swap(struct cx18_buffer *buf);
+
+/* cx18_queue utility functions */
+void cx18_queue_init(struct cx18_queue *q);
+void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+	struct cx18_queue *q);
+struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
+int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
+       struct cx18_queue *steal, struct cx18_queue *to, int needed_bytes);
+struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id,
+	u32 bytesused);
+void cx18_flush_queues(struct cx18_stream *s);
+
+/* cx18_stream utility functions */
+int cx18_stream_alloc(struct cx18_stream *s);
+void cx18_stream_free(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-scb.c b/drivers/media/video/cx18/cx18-scb.c
new file mode 100644
index 0000000..30bc803
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-scb.c
@@ -0,0 +1,121 @@
+/*
+ *  cx18 System Control Block initialization
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-scb.h"
+
+void cx18_init_scb(struct cx18 *cx)
+{
+	setup_page(SCB_OFFSET);
+	memset_io(cx->scb, 0, 0x10000);
+
+	writel(IRQ_APU_TO_CPU,     &cx->scb->apu2cpu_irq);
+	writel(IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack);
+	writel(IRQ_HPU_TO_CPU,     &cx->scb->hpu2cpu_irq);
+	writel(IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack);
+	writel(IRQ_PPU_TO_CPU,     &cx->scb->ppu2cpu_irq);
+	writel(IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack);
+	writel(IRQ_EPU_TO_CPU,     &cx->scb->epu2cpu_irq);
+	writel(IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack);
+
+	writel(IRQ_CPU_TO_APU,     &cx->scb->cpu2apu_irq);
+	writel(IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack);
+	writel(IRQ_HPU_TO_APU,     &cx->scb->hpu2apu_irq);
+	writel(IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack);
+	writel(IRQ_PPU_TO_APU,     &cx->scb->ppu2apu_irq);
+	writel(IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack);
+	writel(IRQ_EPU_TO_APU,     &cx->scb->epu2apu_irq);
+	writel(IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack);
+
+	writel(IRQ_CPU_TO_HPU,     &cx->scb->cpu2hpu_irq);
+	writel(IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack);
+	writel(IRQ_APU_TO_HPU,     &cx->scb->apu2hpu_irq);
+	writel(IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack);
+	writel(IRQ_PPU_TO_HPU,     &cx->scb->ppu2hpu_irq);
+	writel(IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack);
+	writel(IRQ_EPU_TO_HPU,     &cx->scb->epu2hpu_irq);
+	writel(IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack);
+
+	writel(IRQ_CPU_TO_PPU,     &cx->scb->cpu2ppu_irq);
+	writel(IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack);
+	writel(IRQ_APU_TO_PPU,     &cx->scb->apu2ppu_irq);
+	writel(IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack);
+	writel(IRQ_HPU_TO_PPU,     &cx->scb->hpu2ppu_irq);
+	writel(IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack);
+	writel(IRQ_EPU_TO_PPU,     &cx->scb->epu2ppu_irq);
+	writel(IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack);
+
+	writel(IRQ_CPU_TO_EPU,     &cx->scb->cpu2epu_irq);
+	writel(IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack);
+	writel(IRQ_APU_TO_EPU,     &cx->scb->apu2epu_irq);
+	writel(IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack);
+	writel(IRQ_HPU_TO_EPU,     &cx->scb->hpu2epu_irq);
+	writel(IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack);
+	writel(IRQ_PPU_TO_EPU,     &cx->scb->ppu2epu_irq);
+	writel(IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack);
+
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb),
+			&cx->scb->apu2cpu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb),
+			&cx->scb->hpu2cpu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb),
+			&cx->scb->ppu2cpu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb),
+			&cx->scb->epu2cpu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb),
+			&cx->scb->cpu2apu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb),
+			&cx->scb->hpu2apu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb),
+			&cx->scb->ppu2apu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb),
+			&cx->scb->epu2apu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb),
+			&cx->scb->cpu2hpu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb),
+			&cx->scb->apu2hpu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb),
+			&cx->scb->ppu2hpu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb),
+			&cx->scb->epu2hpu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb),
+			&cx->scb->cpu2ppu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb),
+			&cx->scb->apu2ppu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb),
+			&cx->scb->hpu2ppu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb),
+			&cx->scb->epu2ppu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb),
+			&cx->scb->cpu2epu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb),
+			&cx->scb->apu2epu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb),
+			&cx->scb->hpu2epu_mb_offset);
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb),
+			&cx->scb->ppu2epu_mb_offset);
+
+	writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
+			&cx->scb->ipc_offset);
+
+	writel(1, &cx->scb->hpu_state);
+	writel(1, &cx->scb->epu_state);
+}
diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h
new file mode 100644
index 0000000..86b4cb1
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-scb.h
@@ -0,0 +1,285 @@
+/*
+ *  cx18 System Control Block initialization
+ *
+ *  Copyright (C) 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 CX18_SCB_H
+#define CX18_SCB_H
+
+#include "cx18-mailbox.h"
+
+/* NOTE: All ACK interrupts are in the SW2 register.  All non-ACK interrupts
+   are in the SW1 register. */
+
+#define IRQ_APU_TO_CPU         0x00000001
+#define IRQ_CPU_TO_APU_ACK     0x00000001
+#define IRQ_HPU_TO_CPU         0x00000002
+#define IRQ_CPU_TO_HPU_ACK     0x00000002
+#define IRQ_PPU_TO_CPU         0x00000004
+#define IRQ_CPU_TO_PPU_ACK     0x00000004
+#define IRQ_EPU_TO_CPU         0x00000008
+#define IRQ_CPU_TO_EPU_ACK     0x00000008
+
+#define IRQ_CPU_TO_APU         0x00000010
+#define IRQ_APU_TO_CPU_ACK     0x00000010
+#define IRQ_HPU_TO_APU         0x00000020
+#define IRQ_APU_TO_HPU_ACK     0x00000020
+#define IRQ_PPU_TO_APU         0x00000040
+#define IRQ_APU_TO_PPU_ACK     0x00000040
+#define IRQ_EPU_TO_APU         0x00000080
+#define IRQ_APU_TO_EPU_ACK     0x00000080
+
+#define IRQ_CPU_TO_HPU         0x00000100
+#define IRQ_HPU_TO_CPU_ACK     0x00000100
+#define IRQ_APU_TO_HPU         0x00000200
+#define IRQ_HPU_TO_APU_ACK     0x00000200
+#define IRQ_PPU_TO_HPU         0x00000400
+#define IRQ_HPU_TO_PPU_ACK     0x00000400
+#define IRQ_EPU_TO_HPU         0x00000800
+#define IRQ_HPU_TO_EPU_ACK     0x00000800
+
+#define IRQ_CPU_TO_PPU         0x00001000
+#define IRQ_PPU_TO_CPU_ACK     0x00001000
+#define IRQ_APU_TO_PPU         0x00002000
+#define IRQ_PPU_TO_APU_ACK     0x00002000
+#define IRQ_HPU_TO_PPU         0x00004000
+#define IRQ_PPU_TO_HPU_ACK     0x00004000
+#define IRQ_EPU_TO_PPU         0x00008000
+#define IRQ_PPU_TO_EPU_ACK     0x00008000
+
+#define IRQ_CPU_TO_EPU         0x00010000
+#define IRQ_EPU_TO_CPU_ACK     0x00010000
+#define IRQ_APU_TO_EPU         0x00020000
+#define IRQ_EPU_TO_APU_ACK     0x00020000
+#define IRQ_HPU_TO_EPU         0x00040000
+#define IRQ_EPU_TO_HPU_ACK     0x00040000
+#define IRQ_PPU_TO_EPU         0x00080000
+#define IRQ_EPU_TO_PPU_ACK     0x00080000
+
+#define SCB_OFFSET  0xDC0000
+
+/* If Firmware uses fixed memory map, it shall not allocate the area
+   between SCB_OFFSET and SCB_OFFSET+SCB_RESERVED_SIZE-1 inclusive */
+#define SCB_RESERVED_SIZE 0x10000
+
+
+/* This structure is used by EPU to provide memory descriptors in its memory */
+struct cx18_mdl {
+    u32 paddr;  /* Physical address of a buffer segment */
+    u32 length; /* Length of the buffer segment */
+};
+
+/* This structure is used by CPU to provide completed buffers information */
+struct cx18_mdl_ack {
+    u32 id;        /* ID of a completed MDL */
+    u32 data_used; /* Total data filled in the MDL for buffer 'id' */
+};
+
+struct cx18_scb {
+	/* These fields form the System Control Block which is used at boot time
+	   for localizing the IPC data as well as the code positions for all
+	   processors. The offsets are from the start of this struct. */
+
+	/* Offset where to find the Inter-Processor Communication data */
+	u32 ipc_offset;
+	u32 reserved01[7];
+	/* Offset where to find the start of the CPU code */
+	u32 cpu_code_offset;
+	u32 reserved02[3];
+	/* Offset where to find the start of the APU code */
+	u32 apu_code_offset;
+	u32 reserved03[3];
+	/* Offset where to find the start of the HPU code */
+	u32 hpu_code_offset;
+	u32 reserved04[3];
+	/* Offset where to find the start of the PPU code */
+	u32 ppu_code_offset;
+	u32 reserved05[3];
+
+	/* These fields form Inter-Processor Communication data which is used
+	   by all processors to locate the information needed for communicating
+	   with other processors */
+
+	/* Fields for CPU: */
+
+	/* bit 0: 1/0 processor ready/not ready. Set other bits to 0. */
+	u32 cpu_state;
+	u32 reserved1[7];
+	/* Offset to the mailbox used for sending commands from APU to CPU */
+	u32 apu2cpu_mb_offset;
+	/* Value to write to register SW1 register set (0xC7003100) after the
+	   command is ready */
+	u32 apu2cpu_irq;
+	/* Value to write to register SW2 register set (0xC7003140) after the
+	   command is cleared */
+	u32 apu2cpu_irq_ack;
+	u32 reserved2[13];
+
+	u32 hpu2cpu_mb_offset;
+	u32 hpu2cpu_irq;
+	u32 hpu2cpu_irq_ack;
+	u32 reserved3[13];
+
+	u32 ppu2cpu_mb_offset;
+	u32 ppu2cpu_irq;
+	u32 ppu2cpu_irq_ack;
+	u32 reserved4[13];
+
+	u32 epu2cpu_mb_offset;
+	u32 epu2cpu_irq;
+	u32 epu2cpu_irq_ack;
+	u32 reserved5[13];
+	u32 reserved6[8];
+
+	/* Fields for APU: */
+
+	u32 apu_state;
+	u32 reserved11[7];
+	u32 cpu2apu_mb_offset;
+	u32 cpu2apu_irq;
+	u32 cpu2apu_irq_ack;
+	u32 reserved12[13];
+
+	u32 hpu2apu_mb_offset;
+	u32 hpu2apu_irq;
+	u32 hpu2apu_irq_ack;
+	u32 reserved13[13];
+
+	u32 ppu2apu_mb_offset;
+	u32 ppu2apu_irq;
+	u32 ppu2apu_irq_ack;
+	u32 reserved14[13];
+
+	u32 epu2apu_mb_offset;
+	u32 epu2apu_irq;
+	u32 epu2apu_irq_ack;
+	u32 reserved15[13];
+	u32 reserved16[8];
+
+	/* Fields for HPU: */
+
+	u32 hpu_state;
+	u32 reserved21[7];
+	u32 cpu2hpu_mb_offset;
+	u32 cpu2hpu_irq;
+	u32 cpu2hpu_irq_ack;
+	u32 reserved22[13];
+
+	u32 apu2hpu_mb_offset;
+	u32 apu2hpu_irq;
+	u32 apu2hpu_irq_ack;
+	u32 reserved23[13];
+
+	u32 ppu2hpu_mb_offset;
+	u32 ppu2hpu_irq;
+	u32 ppu2hpu_irq_ack;
+	u32 reserved24[13];
+
+	u32 epu2hpu_mb_offset;
+	u32 epu2hpu_irq;
+	u32 epu2hpu_irq_ack;
+	u32 reserved25[13];
+	u32 reserved26[8];
+
+	/* Fields for PPU: */
+
+	u32 ppu_state;
+	u32 reserved31[7];
+	u32 cpu2ppu_mb_offset;
+	u32 cpu2ppu_irq;
+	u32 cpu2ppu_irq_ack;
+	u32 reserved32[13];
+
+	u32 apu2ppu_mb_offset;
+	u32 apu2ppu_irq;
+	u32 apu2ppu_irq_ack;
+	u32 reserved33[13];
+
+	u32 hpu2ppu_mb_offset;
+	u32 hpu2ppu_irq;
+	u32 hpu2ppu_irq_ack;
+	u32 reserved34[13];
+
+	u32 epu2ppu_mb_offset;
+	u32 epu2ppu_irq;
+	u32 epu2ppu_irq_ack;
+	u32 reserved35[13];
+	u32 reserved36[8];
+
+	/* Fields for EPU: */
+
+	u32 epu_state;
+	u32 reserved41[7];
+	u32 cpu2epu_mb_offset;
+	u32 cpu2epu_irq;
+	u32 cpu2epu_irq_ack;
+	u32 reserved42[13];
+
+	u32 apu2epu_mb_offset;
+	u32 apu2epu_irq;
+	u32 apu2epu_irq_ack;
+	u32 reserved43[13];
+
+	u32 hpu2epu_mb_offset;
+	u32 hpu2epu_irq;
+	u32 hpu2epu_irq_ack;
+	u32 reserved44[13];
+
+	u32 ppu2epu_mb_offset;
+	u32 ppu2epu_irq;
+	u32 ppu2epu_irq_ack;
+	u32 reserved45[13];
+	u32 reserved46[8];
+
+	u32 semaphores[8];  /* Semaphores */
+
+	u32 reserved50[32]; /* Reserved for future use */
+
+	struct cx18_mailbox  apu2cpu_mb;
+	struct cx18_mailbox  hpu2cpu_mb;
+	struct cx18_mailbox  ppu2cpu_mb;
+	struct cx18_mailbox  epu2cpu_mb;
+
+	struct cx18_mailbox  cpu2apu_mb;
+	struct cx18_mailbox  hpu2apu_mb;
+	struct cx18_mailbox  ppu2apu_mb;
+	struct cx18_mailbox  epu2apu_mb;
+
+	struct cx18_mailbox  cpu2hpu_mb;
+	struct cx18_mailbox  apu2hpu_mb;
+	struct cx18_mailbox  ppu2hpu_mb;
+	struct cx18_mailbox  epu2hpu_mb;
+
+	struct cx18_mailbox  cpu2ppu_mb;
+	struct cx18_mailbox  apu2ppu_mb;
+	struct cx18_mailbox  hpu2ppu_mb;
+	struct cx18_mailbox  epu2ppu_mb;
+
+	struct cx18_mailbox  cpu2epu_mb;
+	struct cx18_mailbox  apu2epu_mb;
+	struct cx18_mailbox  hpu2epu_mb;
+	struct cx18_mailbox  ppu2epu_mb;
+
+	struct cx18_mdl_ack  cpu_mdl_ack[CX18_MAX_STREAMS][2];
+	struct cx18_mdl      cpu_mdl[1];
+};
+
+void cx18_init_scb(struct cx18 *cx);
+
+#endif
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
new file mode 100644
index 0000000..afb141b
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -0,0 +1,566 @@
+/*
+ *  cx18 init/start/stop/exit stream functions
+ *
+ *  Derived from ivtv-streams.c
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-fileops.h"
+#include "cx18-mailbox.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-ioctl.h"
+#include "cx18-streams.h"
+#include "cx18-cards.h"
+#include "cx18-scb.h"
+#include "cx18-av-core.h"
+#include "cx18-dvb.h"
+
+#define CX18_DSP0_INTERRUPT_MASK     	0xd0004C
+
+static struct file_operations cx18_v4l2_enc_fops = {
+      .owner = THIS_MODULE,
+      .read = cx18_v4l2_read,
+      .open = cx18_v4l2_open,
+      .ioctl = cx18_v4l2_ioctl,
+      .release = cx18_v4l2_close,
+      .poll = cx18_v4l2_enc_poll,
+};
+
+/* offset from 0 to register ts v4l2 minors on */
+#define CX18_V4L2_ENC_TS_OFFSET   16
+/* offset from 0 to register pcm v4l2 minors on */
+#define CX18_V4L2_ENC_PCM_OFFSET  24
+/* offset from 0 to register yuv v4l2 minors on */
+#define CX18_V4L2_ENC_YUV_OFFSET  32
+
+static struct {
+	const char *name;
+	int vfl_type;
+	int minor_offset;
+	int dma;
+	enum v4l2_buf_type buf_type;
+	struct file_operations *fops;
+} cx18_stream_info[] = {
+	{	/* CX18_ENC_STREAM_TYPE_MPG */
+		"encoder MPEG",
+		VFL_TYPE_GRABBER, 0,
+		PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		&cx18_v4l2_enc_fops
+	},
+	{	/* CX18_ENC_STREAM_TYPE_TS */
+		"TS",
+		VFL_TYPE_GRABBER, -1,
+		PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		&cx18_v4l2_enc_fops
+	},
+	{	/* CX18_ENC_STREAM_TYPE_YUV */
+		"encoder YUV",
+		VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET,
+		PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		&cx18_v4l2_enc_fops
+	},
+	{	/* CX18_ENC_STREAM_TYPE_VBI */
+		"encoder VBI",
+		VFL_TYPE_VBI, 0,
+		PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VBI_CAPTURE,
+		&cx18_v4l2_enc_fops
+	},
+	{	/* CX18_ENC_STREAM_TYPE_PCM */
+		"encoder PCM audio",
+		VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET,
+		PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_PRIVATE,
+		&cx18_v4l2_enc_fops
+	},
+	{	/* CX18_ENC_STREAM_TYPE_IDX */
+		"encoder IDX",
+		VFL_TYPE_GRABBER, -1,
+		PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		&cx18_v4l2_enc_fops
+	},
+	{	/* CX18_ENC_STREAM_TYPE_RAD */
+		"encoder radio",
+		VFL_TYPE_RADIO, 0,
+		PCI_DMA_NONE, V4L2_BUF_TYPE_PRIVATE,
+		&cx18_v4l2_enc_fops
+	},
+};
+
+static void cx18_stream_init(struct cx18 *cx, int type)
+{
+	struct cx18_stream *s = &cx->streams[type];
+	struct video_device *dev = s->v4l2dev;
+	u32 max_size = cx->options.megabytes[type] * 1024 * 1024;
+
+	/* we need to keep v4l2dev, so restore it afterwards */
+	memset(s, 0, sizeof(*s));
+	s->v4l2dev = dev;
+
+	/* initialize cx18_stream fields */
+	s->cx = cx;
+	s->type = type;
+	s->name = cx18_stream_info[type].name;
+	s->handle = 0xffffffff;
+
+	s->dma = cx18_stream_info[type].dma;
+	s->buf_size = cx->stream_buf_size[type];
+	if (s->buf_size)
+		s->buffers = max_size / s->buf_size;
+	if (s->buffers > 63) {
+		/* Each stream has a maximum of 63 buffers,
+		   ensure we do not exceed that. */
+		s->buffers = 63;
+		s->buf_size = (max_size / s->buffers) & ~0xfff;
+	}
+	spin_lock_init(&s->qlock);
+	init_waitqueue_head(&s->waitq);
+	s->id = -1;
+	cx18_queue_init(&s->q_free);
+	cx18_queue_init(&s->q_full);
+	cx18_queue_init(&s->q_io);
+}
+
+static int cx18_prep_dev(struct cx18 *cx, int type)
+{
+	struct cx18_stream *s = &cx->streams[type];
+	u32 cap = cx->v4l2_cap;
+	int minor_offset = cx18_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->cx = cx;
+	s->type = type;
+	s->name = cx18_stream_info[type].name;
+
+	/* Check whether the radio is supported */
+	if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO))
+		return 0;
+
+	/* Check whether VBI is supported */
+	if (type == CX18_ENC_STREAM_TYPE_VBI &&
+	    !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)))
+		return 0;
+
+	/* card number + user defined offset + device offset */
+	minor = cx->num + cx18_first_minor + minor_offset;
+
+	/* User explicitly selected 0 buffers for these streams, so don't
+	   create them. */
+	if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
+	    cx->options.megabytes[type] == 0) {
+		CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
+		return 0;
+	}
+
+	cx18_stream_init(cx, type);
+
+	if (minor_offset == -1)
+		return 0;
+
+	/* allocate and initialize the v4l2 video device structure */
+	s->v4l2dev = video_device_alloc();
+	if (s->v4l2dev == NULL) {
+		CX18_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;
+	snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18%d %s",
+			cx->num, s->name);
+
+	s->v4l2dev->minor = minor;
+	s->v4l2dev->dev = &cx->dev->dev;
+	s->v4l2dev->fops = cx18_stream_info[type].fops;
+	s->v4l2dev->release = video_device_release;
+
+	return 0;
+}
+
+/* Initialize v4l2 variables and register v4l2 devices */
+int cx18_streams_setup(struct cx18 *cx)
+{
+	int type;
+
+	/* Setup V4L2 Devices */
+	for (type = 0; type < CX18_MAX_STREAMS; type++) {
+		/* Prepare device */
+		if (cx18_prep_dev(cx, type))
+			break;
+
+		/* Allocate Stream */
+		if (cx18_stream_alloc(&cx->streams[type]))
+			break;
+	}
+	if (type == CX18_MAX_STREAMS)
+		return 0;
+
+	/* One or more streams could not be initialized. Clean 'em all up. */
+	cx18_streams_cleanup(cx);
+	return -ENOMEM;
+}
+
+static int cx18_reg_dev(struct cx18 *cx, int type)
+{
+	struct cx18_stream *s = &cx->streams[type];
+	int vfl_type = cx18_stream_info[type].vfl_type;
+	int minor;
+
+	/* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something?
+	 * We need a VFL_TYPE_TS defined.
+	 */
+	if (strcmp("TS", s->name) == 0) {
+		/* just return if no DVB is supported */
+		if ((cx->card->hw_all & CX18_HW_DVB) == 0)
+			return 0;
+		if (cx18_dvb_register(s) < 0) {
+			CX18_ERR("DVB failed to register\n");
+			return -EINVAL;
+		}
+	}
+
+	if (s->v4l2dev == NULL)
+		return 0;
+
+	minor = s->v4l2dev->minor;
+
+	/* 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)) {
+		CX18_ERR("Couldn't register v4l2 device for %s minor %d\n",
+			s->name, minor);
+		video_device_release(s->v4l2dev);
+		s->v4l2dev = NULL;
+		return -ENOMEM;
+	}
+	minor = s->v4l2dev->minor;
+
+	switch (vfl_type) {
+	case VFL_TYPE_GRABBER:
+		CX18_INFO("Registered device video%d for %s (%d MB)\n",
+			minor, s->name, cx->options.megabytes[type]);
+		break;
+
+	case VFL_TYPE_RADIO:
+		CX18_INFO("Registered device radio%d for %s\n",
+			minor - MINOR_VFL_TYPE_RADIO_MIN, s->name);
+		break;
+
+	case VFL_TYPE_VBI:
+		if (cx->options.megabytes[type])
+			CX18_INFO("Registered device vbi%d for %s (%d MB)\n",
+				minor - MINOR_VFL_TYPE_VBI_MIN,
+				s->name, cx->options.megabytes[type]);
+		else
+			CX18_INFO("Registered device vbi%d for %s\n",
+				minor - MINOR_VFL_TYPE_VBI_MIN, s->name);
+		break;
+	}
+
+	return 0;
+}
+
+/* Register v4l2 devices */
+int cx18_streams_register(struct cx18 *cx)
+{
+	int type;
+	int err = 0;
+
+	/* Register V4L2 devices */
+	for (type = 0; type < CX18_MAX_STREAMS; type++)
+		err |= cx18_reg_dev(cx, type);
+
+	if (err == 0)
+		return 0;
+
+	/* One or more streams could not be initialized. Clean 'em all up. */
+	cx18_streams_cleanup(cx);
+	return -ENOMEM;
+}
+
+/* Unregister v4l2 devices */
+void cx18_streams_cleanup(struct cx18 *cx)
+{
+	struct video_device *vdev;
+	int type;
+
+	/* Teardown all streams */
+	for (type = 0; type < CX18_MAX_STREAMS; type++) {
+		if (cx->streams[type].dvb.enabled)
+			cx18_dvb_unregister(&cx->streams[type]);
+
+		vdev = cx->streams[type].v4l2dev;
+
+		cx->streams[type].v4l2dev = NULL;
+		if (vdev == NULL)
+			continue;
+
+		cx18_stream_free(&cx->streams[type]);
+
+		/* Unregister device */
+		video_unregister_device(vdev);
+	}
+}
+
+static void cx18_vbi_setup(struct cx18_stream *s)
+{
+	struct cx18 *cx = s->cx;
+	int raw = cx->vbi.sliced_in->service_set == 0;
+	u32 data[CX2341X_MBOX_MAX_DATA];
+	int lines;
+
+	if (cx->is_60hz) {
+		cx->vbi.count = 12;
+		cx->vbi.start[0] = 10;
+		cx->vbi.start[1] = 273;
+	} else {        /* PAL/SECAM */
+		cx->vbi.count = 18;
+		cx->vbi.start[0] = 6;
+		cx->vbi.start[1] = 318;
+	}
+
+	/* setup VBI registers */
+	cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->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 = cx->vbi.count * 2;
+	} else {
+		lines = cx->is_60hz ? 24 : 38;
+		if (cx->is_60hz)
+			lines += 2;
+	}
+
+	cx->vbi.enc_size = lines *
+		(raw ? cx->vbi.raw_size : cx->vbi.sliced_size);
+
+	data[0] = s->handle;
+	/* Lines per field */
+	data[1] = (lines / 2) | ((lines / 2) << 16);
+	/* bytes per line */
+	data[2] = (raw ? cx->vbi.raw_size : cx->vbi.sliced_size);
+	/* Every X number of frames a VBI interrupt arrives
+	   (frames as in 25 or 30 fps) */
+	data[3] = 1;
+	/* Setup VBI for the cx25840 digitizer */
+	if (raw) {
+		data[4] = 0x20602060;
+		data[5] = 0x30703070;
+	} else {
+		data[4] = 0xB0F0B0F0;
+		data[5] = 0xA0E0A0E0;
+	}
+
+	CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n",
+			data[0], data[1], data[2], data[3], data[4], data[5]);
+
+	if (s->type == CX18_ENC_STREAM_TYPE_VBI)
+		cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
+}
+
+int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
+{
+	u32 data[MAX_MB_ARGUMENTS];
+	struct cx18 *cx = s->cx;
+	struct list_head *p;
+	int ts = 0;
+	int captype = 0;
+
+	if (s->v4l2dev == NULL && s->dvb.enabled == 0)
+		return -EINVAL;
+
+	CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
+
+	switch (s->type) {
+	case CX18_ENC_STREAM_TYPE_MPG:
+		captype = CAPTURE_CHANNEL_TYPE_MPEG;
+		cx->mpg_data_received = cx->vbi_data_inserted = 0;
+		cx->dualwatch_jiffies = jiffies;
+		cx->dualwatch_stereo_mode = cx->params.audio_properties & 0x300;
+		cx->search_pack_header = 0;
+		break;
+
+	case CX18_ENC_STREAM_TYPE_TS:
+		captype = CAPTURE_CHANNEL_TYPE_TS;
+		ts = 1;
+		break;
+	case CX18_ENC_STREAM_TYPE_YUV:
+		captype = CAPTURE_CHANNEL_TYPE_YUV;
+		break;
+	case CX18_ENC_STREAM_TYPE_PCM:
+		captype = CAPTURE_CHANNEL_TYPE_PCM;
+		break;
+	case CX18_ENC_STREAM_TYPE_VBI:
+		captype = cx->vbi.sliced_in->service_set ?
+		    CAPTURE_CHANNEL_TYPE_SLICED_VBI : CAPTURE_CHANNEL_TYPE_VBI;
+		cx->vbi.frame = 0;
+		cx->vbi.inserted_frame = 0;
+		memset(cx->vbi.sliced_mpeg_size,
+			0, sizeof(cx->vbi.sliced_mpeg_size));
+		break;
+	default:
+		return -EINVAL;
+	}
+	s->buffers_stolen = 0;
+
+	/* mute/unmute video */
+	cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
+		  s->handle, !!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags));
+
+	/* Clear Streamoff flags in case left from last capture */
+	clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+	cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE);
+	s->handle = data[0];
+	cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
+
+	if (atomic_read(&cx->capturing) == 0 && !ts) {
+		/* Stuff from Windows, we don't know what it is */
+		cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
+		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
+		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0);
+		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1);
+		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12);
+
+		cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3,
+			       s->handle, cx->digitizer, cx->digitizer);
+
+		/* Setup VBI */
+		if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE)
+			cx18_vbi_setup(s);
+
+		/* assign program index info.
+		   Mask 7: select I/P/B, Num_req: 400 max */
+		cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0);
+
+		/* Setup API for Stream */
+		cx2341x_update(cx, cx18_api_func, NULL, &cx->params);
+	}
+
+	if (atomic_read(&cx->capturing) == 0) {
+		clear_bit(CX18_F_I_EOS, &cx->i_flags);
+		write_reg(7, CX18_DSP0_INTERRUPT_MASK);
+	}
+
+	cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle,
+		(void *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
+		(void *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
+
+	list_for_each(p, &s->q_free.list) {
+		struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list);
+
+		writel(buf->dma_handle, &cx->scb->cpu_mdl[buf->id].paddr);
+		writel(s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
+		cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
+			(void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 1,
+			buf->id, s->buf_size);
+	}
+	/* begin_capture */
+	if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
+		CX18_DEBUG_WARN("Error starting capture!\n");
+		cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
+		return -EINVAL;
+	}
+
+	/* you're live! sit back and await interrupts :) */
+	atomic_inc(&cx->capturing);
+	return 0;
+}
+
+void cx18_stop_all_captures(struct cx18 *cx)
+{
+	int i;
+
+	for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
+		struct cx18_stream *s = &cx->streams[i];
+
+		if (s->v4l2dev == NULL && s->dvb.enabled == 0)
+			continue;
+		if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
+			cx18_stop_v4l2_encode_stream(s, 0);
+	}
+}
+
+int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
+{
+	struct cx18 *cx = s->cx;
+	unsigned long then;
+
+	if (s->v4l2dev == NULL && s->dvb.enabled == 0)
+		return -EINVAL;
+
+	/* This function assumes that you are allowed to stop the capture
+	   and that we are actually capturing */
+
+	CX18_DEBUG_INFO("Stop Capture\n");
+
+	if (atomic_read(&cx->capturing) == 0)
+		return 0;
+
+	if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+		cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end);
+	else
+		cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+
+	then = jiffies;
+
+	if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) {
+		CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
+	}
+
+	atomic_dec(&cx->capturing);
+
+	/* Clear capture and no-read bits */
+	clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+
+	cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
+	s->handle = 0xffffffff;
+
+	if (atomic_read(&cx->capturing) > 0)
+		return 0;
+
+	write_reg(5, CX18_DSP0_INTERRUPT_MASK);
+	wake_up(&s->waitq);
+
+	return 0;
+}
+
+u32 cx18_find_handle(struct cx18 *cx)
+{
+	int i;
+
+	/* find first available handle to be used for global settings */
+	for (i = 0; i < CX18_MAX_STREAMS; i++) {
+		struct cx18_stream *s = &cx->streams[i];
+
+		if (s->v4l2dev && s->handle)
+			return s->handle;
+	}
+	return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h
new file mode 100644
index 0000000..8c7ba7d
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-streams.h
@@ -0,0 +1,33 @@
+/*
+ *  cx18 init/start/stop/exit stream functions
+ *
+ *  Derived from ivtv-streams.h
+ *
+ *  Copyright (C) 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
+ */
+
+u32 cx18_find_handle(struct cx18 *cx);
+int cx18_streams_setup(struct cx18 *cx);
+int cx18_streams_register(struct cx18 *cx);
+void cx18_streams_cleanup(struct cx18 *cx);
+
+/* Capture related */
+int cx18_start_v4l2_encode_stream(struct cx18_stream *s);
+int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end);
+
+void cx18_stop_all_captures(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c
new file mode 100644
index 0000000..22e76ee
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-vbi.c
@@ -0,0 +1,208 @@
+/*
+ *  cx18 Vertical Blank Interval support functions
+ *
+ *  Derived from ivtv-vbi.c
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-vbi.h"
+#include "cx18-ioctl.h"
+#include "cx18-queue.h"
+#include "cx18-av-core.h"
+
+static void copy_vbi_data(struct cx18 *cx, 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 = cx->vbi.frame % CX18_VBI_FRAMES;
+	u8 *dst = &cx->vbi.sliced_mpeg_data[idx][0];
+
+	for (i = 0; i < lines; i++) {
+		struct v4l2_sliced_vbi_data *sdata = cx->vbi.sliced_data + i;
+		int f, l;
+
+		if (sdata->id == 0)
+			continue;
+
+		l = sdata->line - 6;
+		f = sdata->field;
+		if (f)
+			l += 18;
+		if (l < 32)
+			linemask[0] |= (1 << l);
+		else
+			linemask[1] |= (1 << (l - 32));
+		dst[sd + 12 + line * 43] = cx18_service2vbi(sdata->id);
+		memcpy(dst + sd + 12 + line * 43 + 1, sdata->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, "cx0", 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);
+	cx->vbi.sliced_mpeg_size[idx] = sd + size;
+}
+
+/* Compress raw VBI format, removes leading SAV codes and surplus space
+   after the field.
+   Returns new compressed size. */
+static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size)
+{
+	u32 line_size = cx->vbi.raw_decoder_line_size;
+	u32 lines = cx->vbi.count;
+	u8 sav1 = cx->vbi.raw_decoder_sav_odd_field;
+	u8 sav2 = cx->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 cx18 *cx, u32 line, u8 *buf,
+			       u32 size, u8 sav)
+{
+	u32 line_size = cx->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;
+		cx18_av_cmd(cx, VIDIOC_INT_DECODE_VBI_LINE, &vbi);
+		if (vbi.type) {
+			cx->vbi.sliced_data[line].id = vbi.type;
+			cx->vbi.sliced_data[line].field = vbi.is_second_field;
+			cx->vbi.sliced_data[line].line = vbi.line;
+			memcpy(cx->vbi.sliced_data[line].data, vbi.p, 42);
+			line++;
+		}
+	}
+	return line;
+}
+
+void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
+			   u64 pts_stamp, int streamtype)
+{
+	u8 *p = (u8 *) buf->buf;
+	u32 size = buf->bytesused;
+	int lines;
+
+	if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
+		return;
+
+	/* Raw VBI data */
+	if (cx->vbi.sliced_in->service_set == 0) {
+		u8 type;
+
+		cx18_buf_swap(buf);
+
+		type = p[3];
+
+		size = buf->bytesused = compress_raw_buf(cx, p, size);
+
+		/* second field of the frame? */
+		if (type == cx->vbi.raw_decoder_sav_even_field) {
+			/* Dirty hack needed for backwards
+			   compatibility of old VBI software. */
+			p += size - 4;
+			memcpy(p, &cx->vbi.frame, 4);
+			cx->vbi.frame++;
+		}
+		return;
+	}
+
+	/* Sliced VBI data with data insertion */
+	cx18_buf_swap(buf);
+
+	/* first field */
+	lines = compress_sliced_buf(cx, 0, p, size / 2,
+			cx->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(cx, lines, p + size / 2 - 32,
+			size / 2 + 32, cx->vbi.sliced_decoder_sav_even_field);
+	/* always return at least one empty line */
+	if (lines == 0) {
+		cx->vbi.sliced_data[0].id = 0;
+		cx->vbi.sliced_data[0].line = 0;
+		cx->vbi.sliced_data[0].field = 0;
+		lines = 1;
+	}
+	buf->bytesused = size = lines * sizeof(cx->vbi.sliced_data[0]);
+	memcpy(p, &cx->vbi.sliced_data[0], size);
+
+	if (cx->vbi.insert_mpeg)
+		copy_vbi_data(cx, lines, pts_stamp);
+	cx->vbi.frame++;
+}
diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/video/cx18/cx18-vbi.h
new file mode 100644
index 0000000..c56ff7d
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-vbi.h
@@ -0,0 +1,26 @@
+/*
+ *  cx18 Vertical Blank Interval support functions
+ *
+ *  Derived from ivtv-vbi.h
+ *
+ *  Copyright (C) 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 cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
+			   u64 pts_stamp, int streamtype);
+int cx18_used_line(struct cx18 *cx, int line, int field);
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
new file mode 100644
index 0000000..d5c7a6f
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-version.h
@@ -0,0 +1,34 @@
+/*
+ *  cx18 driver version information
+ *
+ *  Copyright (C) 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 CX18_VERSION_H
+#define CX18_VERSION_H
+
+#define CX18_DRIVER_NAME "cx18"
+#define CX18_DRIVER_VERSION_MAJOR 1
+#define CX18_DRIVER_VERSION_MINOR 0
+#define CX18_DRIVER_VERSION_PATCHLEVEL 0
+
+#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
+#define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \
+	CX18_DRIVER_VERSION_MINOR, CX18_DRIVER_VERSION_PATCHLEVEL)
+
+#endif
diff --git a/drivers/media/video/cx18/cx18-video.c b/drivers/media/video/cx18/cx18-video.c
new file mode 100644
index 0000000..2e5c419
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-video.c
@@ -0,0 +1,45 @@
+/*
+ *  cx18 video interface functions
+ *
+ *  Copyright (C) 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 "cx18-driver.h"
+#include "cx18-video.h"
+#include "cx18-av-core.h"
+#include "cx18-cards.h"
+
+void cx18_video_set_io(struct cx18 *cx)
+{
+	struct v4l2_routing route;
+	int inp = cx->active_input;
+	u32 type;
+
+	route.input = cx->card->video_inputs[inp].video_input;
+	route.output = 0;
+	cx18_av_cmd(cx, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+
+	type = cx->card->video_inputs[inp].video_type;
+
+	if (type == CX18_CARD_INPUT_VID_TUNER)
+		route.input = 0;  /* Tuner */
+	else if (type < CX18_CARD_INPUT_COMPOSITE1)
+		route.input = 2;  /* S-Video */
+	else
+		route.input = 1;  /* Composite */
+}
diff --git a/drivers/media/video/cx18/cx18-video.h b/drivers/media/video/cx18/cx18-video.h
new file mode 100644
index 0000000..529006a
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-video.h
@@ -0,0 +1,22 @@
+/*
+ *  cx18 video interface functions
+ *
+ *  Copyright (C) 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 cx18_video_set_io(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
new file mode 100644
index 0000000..33f78da
--- /dev/null
+++ b/drivers/media/video/cx18/cx23418.h
@@ -0,0 +1,458 @@
+/*
+ *  cx18 header containing common defines.
+ *
+ *  Copyright (C) 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 CX23418_H
+#define CX23418_H
+
+#include <media/cx2341x.h>
+
+#define MGR_CMD_MASK            		0x40000000
+/* The MSB of the command code indicates that this is the completion of a
+   command */
+#define MGR_CMD_MASK_ACK        		(MGR_CMD_MASK | 0x80000000)
+
+/* Description: This command creates a new instance of a certain task
+   IN[0]  - Task ID. This is one of the XPU_CMD_MASK_YYY where XPU is
+	    the processor on which the task YYY will be created
+   OUT[0] - Task handle. This handle is passed along with commands to
+	    dispatch to the right instance of the task
+   ReturnCode - One of the ERR_SYS_... */
+#define CX18_CREATE_TASK      			(MGR_CMD_MASK | 0x0001)
+
+/* Description: This command destroys an instance of a task
+   IN[0] - Task handle. Hanlde of the task to destroy
+   ReturnCode - One of the ERR_SYS_... */
+#define CX18_DESTROY_TASK     			(MGR_CMD_MASK | 0x0002)
+
+/* All commands for CPU have the following mask set */
+#define CPU_CMD_MASK                        	0x20000000
+#define CPU_CMD_MASK_ACK                    	(CPU_CMD_MASK | 0x80000000)
+#define CPU_CMD_MASK_CAPTURE                	(CPU_CMD_MASK | 0x00020000)
+#define CPU_CMD_MASK_TS                     	(CPU_CMD_MASK | 0x00040000)
+
+#define EPU_CMD_MASK                        	0x02000000
+#define EPU_CMD_MASK_DEBUG       		(EPU_CMD_MASK | 0x000000)
+#define EPU_CMD_MASK_DE                     	(EPU_CMD_MASK | 0x040000)
+
+/* Description: This command indicates that a Memory Descriptor List has been
+   filled with the requested channel type
+   IN[0] - Task handle. Handle of the task
+   IN[1] - Offset of the MDL_ACK from the beginning of the local DDR.
+   IN[2] - Number of CNXT_MDL_ACK structures in the array pointed to by IN[1]
+   ReturnCode - One of the ERR_DE_... */
+#define CX18_EPU_DMA_DONE              		(EPU_CMD_MASK_DE | 0x0001)
+
+/* Something interesting happened
+   IN[0] - A value to log
+   IN[1] - An offset of a string in the MiniMe memory;
+	   0/zero/NULL means "I have nothing to say" */
+#define CX18_EPU_DEBUG 				(EPU_CMD_MASK_DEBUG | 0x0003)
+
+/* Description: This command starts streaming with the set channel type
+   IN[0] - Task handle. Handle of the task to start
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_START               	(CPU_CMD_MASK_CAPTURE | 0x0002)
+
+/* Description: This command stops streaming with the set channel type
+   IN[0] - Task handle. Handle of the task to stop
+   IN[1] - 0 = stop at end of GOP, 1 = stop at end of frame (MPEG only)
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_STOP                	(CPU_CMD_MASK_CAPTURE | 0x0003)
+
+/* Description: This command pauses streaming with the set channel type
+   IN[0] - Task handle. Handle of the task to pause
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_PAUSE               	(CPU_CMD_MASK_CAPTURE | 0x0007)
+
+/* Description: This command resumes streaming with the set channel type
+   IN[0] - Task handle. Handle of the task to resume
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_RESUME              	(CPU_CMD_MASK_CAPTURE | 0x0008)
+
+#define CAPTURE_CHANNEL_TYPE_NONE  		0
+#define CAPTURE_CHANNEL_TYPE_MPEG  		1
+#define CAPTURE_CHANNEL_TYPE_INDEX 		2
+#define CAPTURE_CHANNEL_TYPE_YUV   		3
+#define CAPTURE_CHANNEL_TYPE_PCM   		4
+#define CAPTURE_CHANNEL_TYPE_VBI   		5
+#define CAPTURE_CHANNEL_TYPE_SLICED_VBI		6
+#define CAPTURE_CHANNEL_TYPE_TS			7
+#define CAPTURE_CHANNEL_TYPE_MAX   		15
+
+/* Description: This command sets the channel type. This can only be done
+   when stopped.
+   IN[0] - Task handle. Handle of the task to start
+   IN[1] - Channel Type. See Below.
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_CHANNEL_TYPE      		(CPU_CMD_MASK_CAPTURE + 1)
+
+/* Description: Set stream output type
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - type
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_STREAM_OUTPUT_TYPE		(CPU_CMD_MASK_CAPTURE | 0x0012)
+
+/* Description: Set video input resolution and frame rate
+   IN[0] - task handle
+   IN[1] - reserved
+   IN[2] - reserved
+   IN[3] - reserved
+   IN[4] - reserved
+   IN[5] - frame rate, 0 - 29.97f/s, 1 - 25f/s
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_IN                	(CPU_CMD_MASK_CAPTURE | 0x0004)
+
+/* Description: Set video frame rate
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - video bit rate mode
+   IN[2] - video average rate
+   IN[3] - video peak rate
+   IN[4] - system mux rate
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_RATE              	(CPU_CMD_MASK_CAPTURE | 0x0005)
+
+/* Description: Set video output resolution
+   IN[0] - task handle
+   IN[1] - horizontal size
+   IN[2] - vertical size
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_RESOLUTION		(CPU_CMD_MASK_CAPTURE | 0x0006)
+
+/* Description: This command set filter parameters
+   IN[0] - Task handle. Handle of the task
+   IN[1] - type, 0 - temporal, 1 - spatial, 2 - median
+   IN[2] - mode,  temporal/spatial: 0 - disable, 1 - static, 2 - dynamic
+			median:	0 = disable, 1 = horizontal, 2 = vertical,
+				3 = horizontal/vertical, 4 = diagonal
+   IN[3] - strength, temporal 0 - 31, spatial 0 - 15
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_FILTER_PARAM            	(CPU_CMD_MASK_CAPTURE | 0x0009)
+
+/* Description: This command set spatial filter type
+   IN[0] - Task handle.
+   IN[1] - luma type: 0 = disable, 1 = 1D horizontal only, 2 = 1D vertical only,
+		      3 = 2D H/V separable, 4 = 2D symmetric non-separable
+   IN[2] - chroma type: 0 - diable, 1 = 1D horizontal
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_SPATIAL_FILTER_TYPE     	(CPU_CMD_MASK_CAPTURE | 0x000C)
+
+/* Description: This command set coring levels for median filter
+   IN[0] - Task handle.
+   IN[1] - luma_high
+   IN[2] - luma_low
+   IN[3] - chroma_high
+   IN[4] - chroma_low
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_MEDIAN_CORING           	(CPU_CMD_MASK_CAPTURE | 0x000E)
+
+/* Description: This command set the picture type mask for index file
+   IN[0] - 	0 = disable index file output
+			1 = output I picture
+			2 = P picture
+			4 = B picture
+			other = illegal */
+#define CX18_CPU_SET_INDEXTABLE         	(CPU_CMD_MASK_CAPTURE | 0x0010)
+
+/* Description: Set audio parameters
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - audio parameter
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_AUDIO_PARAMETERS		(CPU_CMD_MASK_CAPTURE | 0x0011)
+
+/* Description: Set video mute
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - bit31-24: muteYvalue
+	   bit23-16: muteUvalue
+	   bit15-8:  muteVvalue
+	   bit0:     1:mute, 0: unmute
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_MUTE			(CPU_CMD_MASK_CAPTURE | 0x0013)
+
+/* Description: Set audio mute
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - mute/unmute
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_AUDIO_MUTE			(CPU_CMD_MASK_CAPTURE | 0x0014)
+
+/* Description: Set stream output type
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - subType
+	    SET_INITIAL_SCR      		1
+	    SET_QUALITY_MODE            2
+	    SET_VIM_PROTECT_MODE        3
+	    SET_PTS_CORRECTION          4
+	    SET_USB_FLUSH_MODE          5
+	    SET_MERAQPAR_ENABLE         6
+	    SET_NAV_PACK_INSERTION      7
+	    SET_SCENE_CHANGE_ENABLE     8
+   IN[2] - parameter 1
+   IN[3] - parameter 2
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_MISC_PARAMETERS		(CPU_CMD_MASK_CAPTURE | 0x0015)
+
+/* Description: Set raw VBI parameters
+   IN[0] - Task handle
+   IN[1] - No. of input lines per field:
+				bit[15:0]: field 1,
+				bit[31:16]: field 2
+   IN[2] - No. of input bytes per line
+   IN[3] - No. of output frames per transfer
+   IN[4] - start code
+   IN[5] - stop code
+   ReturnCode */
+#define CX18_CPU_SET_RAW_VBI_PARAM		(CPU_CMD_MASK_CAPTURE | 0x0016)
+
+/* Description: Set capture line No.
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - height1
+   IN[2] - height2
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_CAPTURE_LINE_NO		(CPU_CMD_MASK_CAPTURE | 0x0017)
+
+/* Description: Set copyright
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - copyright
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_COPYRIGHT			(CPU_CMD_MASK_CAPTURE | 0x0018)
+
+/* Description: Set audio PID
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - PID
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_AUDIO_PID			(CPU_CMD_MASK_CAPTURE | 0x0019)
+
+/* Description: Set video PID
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - PID
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_PID			(CPU_CMD_MASK_CAPTURE | 0x001A)
+
+/* Description: Set Vertical Crop Line
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - Line
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VER_CROP_LINE		(CPU_CMD_MASK_CAPTURE | 0x001B)
+
+/* Description: Set COP structure
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - M
+   IN[2] - N
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_GOP_STRUCTURE		(CPU_CMD_MASK_CAPTURE | 0x001C)
+
+/* Description: Set Scene Change Detection
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - scene change
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_SCENE_CHANGE_DETECTION	(CPU_CMD_MASK_CAPTURE | 0x001D)
+
+/* Description: Set Aspect Ratio
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - AspectRatio
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_ASPECT_RATIO		(CPU_CMD_MASK_CAPTURE | 0x001E)
+
+/* Description: Set Skip Input Frame
+   IN[0] - task handle. Handle of the task to start
+   IN[1] - skip input frames
+   ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_SKIP_INPUT_FRAME		(CPU_CMD_MASK_CAPTURE | 0x001F)
+
+/* Description: Set sliced VBI parameters -
+   Note This API will only apply to MPEG and Sliced VBI Channels
+   IN[0] - Task handle
+   IN[1] - output type, 0 - CC, 1 - Moji, 2 - Teletext
+   IN[2] - start / stop line
+			bit[15:0] start line number
+			bit[31:16] stop line number
+   IN[3] - number of output frames per interrupt
+   IN[4] - VBI insertion mode
+			bit 0:	output user data, 1 - enable
+			bit 1:	output private stream, 1 - enable
+			bit 2:	mux option, 0 - in GOP, 1 - in picture
+			bit[7:0] 	private stream ID
+   IN[5] - insertion period while mux option is in picture
+   ReturnCode - VBI data offset */
+#define CX18_CPU_SET_SLICED_VBI_PARAM		(CPU_CMD_MASK_CAPTURE | 0x0020)
+
+/* Description: Set the user data place holder
+   IN[0] - type of data (0 for user)
+   IN[1] - Stuffing period
+   IN[2] - ID data size in word (less than 10)
+   IN[3] - Pointer to ID buffer */
+#define CX18_CPU_SET_USERDATA_PLACE_HOLDER	(CPU_CMD_MASK_CAPTURE | 0x0021)
+
+
+/* Description:
+   In[0] Task Handle
+   return parameter:
+   Out[0]  Reserved
+   Out[1]  Video PTS bit[32:2] of last output video frame.
+   Out[2]  Video PTS bit[ 1:0] of last output video frame.
+   Out[3]  Hardware Video PTS counter bit[31:0],
+	     these bits get incremented on every 90kHz clock tick.
+   Out[4]  Hardware Video PTS counter bit32,
+	     these bits get incremented on every 90kHz clock tick.
+   ReturnCode */
+#define CX18_CPU_GET_ENC_PTS			(CPU_CMD_MASK_CAPTURE | 0x0022)
+
+/* Below is the list of commands related to the data exchange */
+#define CPU_CMD_MASK_DE 			(CPU_CMD_MASK | 0x040000)
+
+/* Description: This command provides the physical base address of the local
+   DDR as viewed by EPU
+   IN[0] - Physical offset where EPU has the local DDR mapped
+   ReturnCode - One of the ERR_DE_... */
+#define CPU_CMD_DE_SetBase 			(CPU_CMD_MASK_DE | 0x0001)
+
+/* Description: This command provides the offsets in the device memory where
+   the 2 cx18_mdl_ack blocks reside
+   IN[0] - Task handle. Handle of the task to start
+   IN[1] - Offset of the first cx18_mdl_ack from the beginning of the
+	   local DDR.
+   IN[2] - Offset of the second cx18_mdl_ack from the beginning of the
+	   local DDR.
+   ReturnCode - One of the ERR_DE_... */
+#define CX18_CPU_DE_SET_MDL_ACK                	(CPU_CMD_MASK_DE | 0x0002)
+
+/* Description: This command provides the offset to a Memory Descriptor List
+   IN[0] - Task handle. Handle of the task to start
+   IN[1] - Offset of the MDL from the beginning of the local DDR.
+   IN[2] - Number of cx18_mdl structures in the array pointed to by IN[1]
+   IN[3] - Buffer ID
+   IN[4] - Total buffer length
+   ReturnCode - One of the ERR_DE_... */
+#define CX18_CPU_DE_SET_MDL                   	(CPU_CMD_MASK_DE | 0x0005)
+
+/* Description: This command requests return of all current Memory
+   Descriptor Lists to the driver
+   IN[0] - Task handle. Handle of the task to start
+   ReturnCode - One of the ERR_DE_... */
+/* #define CX18_CPU_DE_ReleaseMDL               (CPU_CMD_MASK_DE | 0x0006) */
+
+/* Description: This command signals the cpu that the dat buffer has been
+   consumed and ready for re-use.
+   IN[0] - Task handle. Handle of the task
+   IN[1] - Offset of the data block from the beginning of the local DDR.
+   IN[2] - Number of bytes in the data block
+   ReturnCode - One of the ERR_DE_... */
+/* #define CX18_CPU_DE_RELEASE_BUFFER           (CPU_CMD_MASK_DE | 0x0007) */
+
+/* No Error / Success */
+#define CNXT_OK                 0x000000
+
+/* Received unknown command */
+#define CXERR_UNK_CMD           0x000001
+
+/* First parameter in the command is invalid */
+#define CXERR_INVALID_PARAM1    0x000002
+
+/* Second parameter in the command is invalid */
+#define CXERR_INVALID_PARAM2    0x000003
+
+/* Device interface is not open/found */
+#define CXERR_DEV_NOT_FOUND     0x000004
+
+/* Requested function is not implemented/available */
+#define CXERR_NOTSUPPORTED      0x000005
+
+/* Invalid pointer is provided */
+#define CXERR_BADPTR            0x000006
+
+/* Unable to allocate memory */
+#define CXERR_NOMEM             0x000007
+
+/* Object/Link not found */
+#define CXERR_LINK              0x000008
+
+/* Device busy, command cannot be executed */
+#define CXERR_BUSY              0x000009
+
+/* File/device/handle is not open. */
+#define CXERR_NOT_OPEN          0x00000A
+
+/* Value is out of range */
+#define CXERR_OUTOFRANGE        0x00000B
+
+/* Buffer overflow */
+#define CXERR_OVERFLOW          0x00000C
+
+/* Version mismatch */
+#define CXERR_BADVER            0x00000D
+
+/* Operation timed out */
+#define CXERR_TIMEOUT           0x00000E
+
+/* Operation aborted */
+#define CXERR_ABORT             0x00000F
+
+/* Specified I2C device not found for read/write */
+#define CXERR_I2CDEV_NOTFOUND   0x000010
+
+/* Error in I2C data xfer (but I2C device is present) */
+#define CXERR_I2CDEV_XFERERR    0x000011
+
+/* Chanel changing component not ready */
+#define CXERR_CHANNELNOTREADY   0x000012
+
+/* PPU (Presensation/Decoder) mail box is corrupted */
+#define CXERR_PPU_MB_CORRUPT    0x000013
+
+/* CPU (Capture/Encoder) mail box is corrupted */
+#define CXERR_CPU_MB_CORRUPT    0x000014
+
+/* APU (Audio) mail box is corrupted */
+#define CXERR_APU_MB_CORRUPT    0x000015
+
+/* Unable to open file for reading */
+#define CXERR_FILE_OPEN_READ    0x000016
+
+/* Unable to open file for writing */
+#define CXERR_FILE_OPEN_WRITE   0x000017
+
+/* Unable to find the I2C section specified */
+#define CXERR_I2C_BADSECTION    0x000018
+
+/* Error in I2C data xfer (but I2C device is present) */
+#define CXERR_I2CDEV_DATALOW    0x000019
+
+/* Error in I2C data xfer (but I2C device is present) */
+#define CXERR_I2CDEV_CLOCKLOW   0x00001A
+
+/* No Interrupt received from HW (for I2C access) */
+#define CXERR_NO_HW_I2C_INTR    0x00001B
+
+/* RPU is not ready to accept commands! */
+#define CXERR_RPU_NOT_READY     0x00001C
+
+/* RPU is not ready to accept commands! */
+#define CXERR_RPU_NO_ACK        0x00001D
+
+/* The are no buffers ready. Try again soon! */
+#define CXERR_NODATA_AGAIN      0x00001E
+
+/* The stream is stopping. Function not alllowed now! */
+#define CXERR_STOPPING_STATUS   0x00001F
+
+/* Trying to access hardware when the power is turned OFF */
+#define CXERR_DEVPOWER_OFF      0x000020
+
+#endif /* CX23418_H */
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index ca5fbce..cadf936 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -4,19 +4,19 @@
 	select I2C_ALGOBIT
 	select FW_LOADER
 	select VIDEO_BTCX
-	select VIDEO_TUNER
+	select MEDIA_TUNER
 	select VIDEO_TVEEPROM
 	select VIDEO_IR
 	select VIDEOBUF_DVB
 	select VIDEO_CX25840
-	select DVB_TUNER_MT2131 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MT2131 if !DVB_FE_CUSTOMISE
 	select DVB_S5H1409 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
 	select DVB_PLL if !DVB_FE_CUSTOMISE
-	select TUNER_XC2028 if !DVB_FE_CUSTOMIZE
-	select TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
-	select DVB_TDA18271 if !DVB_FE_CUSTOMIZE
-	select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
+	select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE
+	select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
+	select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE
+	select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
 	select DVB_TDA10048 if !DVB_FE_CUSTOMIZE
 	---help---
 	  This is a video4linux driver for Conexant 23885 based
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
index d7b0721..29c23b4 100644
--- a/drivers/media/video/cx23885/Makefile
+++ b/drivers/media/video/cx23885/Makefile
@@ -3,6 +3,7 @@
 obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
 
 EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
 
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index 27635cd..b0d7d6a 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -5,7 +5,7 @@
 	select FW_LOADER
 	select VIDEO_BTCX
 	select VIDEOBUF_DMA_SG
-	select VIDEO_TUNER
+	select MEDIA_TUNER
 	select VIDEO_TVEEPROM
 	select VIDEO_IR
 	select VIDEO_WM8775 if VIDEO_HELPER_CHIPS_AUTO
@@ -57,7 +57,7 @@
 	select DVB_NXT200X if !DVB_FE_CUSTOMISE
 	select DVB_CX24123 if !DVB_FE_CUSTOMISE
 	select DVB_ISL6421 if !DVB_FE_CUSTOMISE
-	select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
 	select DVB_S5H1411 if !DVB_FE_CUSTOMISE
 	---help---
 	  This adds support for DVB/ATSC cards based on the
diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile
index 532cee3..6ec30f2 100644
--- a/drivers/media/video/cx88/Makefile
+++ b/drivers/media/video/cx88/Makefile
@@ -10,5 +10,6 @@
 obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o
 
 EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 2b6b283..aeba26d 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -57,6 +57,9 @@
 /* ------------------------------------------------------------------ */
 /* board config info                                                  */
 
+/* If radio_type !=UNSET, radio_addr should be specified
+ */
+
 static const struct cx88_board cx88_boards[] = {
 	[CX88_BOARD_UNKNOWN] = {
 		.name		= "UNKNOWN/GENERIC",
@@ -2446,25 +2449,31 @@
 static void cx88_card_setup(struct cx88_core *core)
 {
 	static u8 eeprom[256];
+	struct tuner_setup tun_setup;
+	unsigned int mode_mask = T_RADIO     |
+				 T_ANALOG_TV |
+				 T_DIGITAL_TV;
+
+	memset(&tun_setup, 0, sizeof(tun_setup));
 
 	if (0 == core->i2c_rc) {
 		core->i2c_client.addr = 0xa0 >> 1;
-		tveeprom_read(&core->i2c_client,eeprom,sizeof(eeprom));
+		tveeprom_read(&core->i2c_client, eeprom, sizeof(eeprom));
 	}
 
 	switch (core->boardnr) {
 	case CX88_BOARD_HAUPPAUGE:
 	case CX88_BOARD_HAUPPAUGE_ROSLYN:
 		if (0 == core->i2c_rc)
-			hauppauge_eeprom(core,eeprom+8);
+			hauppauge_eeprom(core, eeprom+8);
 		break;
 	case CX88_BOARD_GDI:
 		if (0 == core->i2c_rc)
-			gdi_eeprom(core,eeprom);
+			gdi_eeprom(core, eeprom);
 		break;
 	case CX88_BOARD_WINFAST2000XP_EXPERT:
 		if (0 == core->i2c_rc)
-			leadtek_eeprom(core,eeprom);
+			leadtek_eeprom(core, eeprom);
 		break;
 	case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
 	case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
@@ -2474,7 +2483,7 @@
 	case CX88_BOARD_HAUPPAUGE_HVR3000:
 	case CX88_BOARD_HAUPPAUGE_HVR1300:
 		if (0 == core->i2c_rc)
-			hauppauge_eeprom(core,eeprom);
+			hauppauge_eeprom(core, eeprom);
 		break;
 	case CX88_BOARD_KWORLD_DVBS_100:
 		cx_write(MO_GP0_IO, 0x000007f8);
@@ -2555,6 +2564,35 @@
 
 		cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tea5767_cfg);
 	}
+	} /*end switch() */
+
+
+	/* Setup tuners */
+	if ((core->board.radio_type != UNSET)) {
+		tun_setup.mode_mask      = T_RADIO;
+		tun_setup.type           = core->board.radio_type;
+		tun_setup.addr           = core->board.radio_addr;
+		tun_setup.tuner_callback = cx88_tuner_callback;
+		cx88_call_i2c_clients(core, TUNER_SET_TYPE_ADDR, &tun_setup);
+		mode_mask &= ~T_RADIO;
+	}
+
+	if (core->board.tuner_type != TUNER_ABSENT) {
+		tun_setup.mode_mask      = mode_mask;
+		tun_setup.type           = core->board.tuner_type;
+		tun_setup.addr           = core->board.tuner_addr;
+		tun_setup.tuner_callback = cx88_tuner_callback;
+
+		cx88_call_i2c_clients(core, TUNER_SET_TYPE_ADDR, &tun_setup);
+	}
+
+	if (core->board.tda9887_conf) {
+		struct v4l2_priv_tun_config tda9887_cfg;
+
+		tda9887_cfg.tuner = TUNER_TDA9887;
+		tda9887_cfg.priv  = &core->board.tda9887_conf;
+
+		cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tda9887_cfg);
 	}
 
 	if (core->board.tuner_type == TUNER_XC2028) {
@@ -2572,6 +2610,7 @@
 			    ctl.fname);
 		cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &xc2028_cfg);
 	}
+	cx88_call_i2c_clients (core, TUNER_SET_STANDBY, NULL);
 }
 
 /* ------------------------------------------------------------------ */
@@ -2710,7 +2749,6 @@
 	if (TUNER_ABSENT != core->board.tuner_type)
 		request_module("tuner");
 
-	cx88_call_i2c_clients (core, TUNER_SET_STANDBY, NULL);
 	cx88_card_setup(core);
 	cx88_ir_init(core, pci);
 
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
index c6b4473..00aa7a3 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -104,37 +104,7 @@
 
 	dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
 		client->driver->driver.name, client->addr, client->name);
-	if (!client->driver->command)
-		return 0;
 
-	if (core->board.radio_type != UNSET) {
-		if ((core->board.radio_addr==ADDR_UNSET)||(core->board.radio_addr==client->addr)) {
-			tun_setup.mode_mask	 = T_RADIO;
-			tun_setup.type		 = core->board.radio_type;
-			tun_setup.addr		 = core->board.radio_addr;
-			tun_setup.tuner_callback = cx88_tuner_callback;
-			client->driver->command (client, TUNER_SET_TYPE_ADDR, &tun_setup);
-		}
-	}
-	if (core->board.tuner_type != UNSET) {
-		if ((core->board.tuner_addr==ADDR_UNSET)||(core->board.tuner_addr==client->addr)) {
-
-			tun_setup.mode_mask	 = T_ANALOG_TV;
-			tun_setup.type		 = core->board.tuner_type;
-			tun_setup.addr		 = core->board.tuner_addr;
-			tun_setup.tuner_callback = cx88_tuner_callback;
-			client->driver->command (client,TUNER_SET_TYPE_ADDR, &tun_setup);
-		}
-	}
-
-	if (core->board.tda9887_conf) {
-		struct v4l2_priv_tun_config tda9887_cfg;
-
-		tda9887_cfg.tuner = TUNER_TDA9887;
-		tda9887_cfg.priv  = &core->board.tda9887_conf;
-
-		client->driver->command(client, TUNER_SET_CONFIG, &tda9887_cfg);
-	}
 	return 0;
 }
 
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
index 9caffed..c7c2896 100644
--- a/drivers/media/video/em28xx/Kconfig
+++ b/drivers/media/video/em28xx/Kconfig
@@ -1,7 +1,7 @@
 config VIDEO_EM28XX
 	tristate "Empia EM28xx USB video capture support"
 	depends on VIDEO_DEV && I2C && INPUT
-	select VIDEO_TUNER
+	select MEDIA_TUNER
 	select VIDEO_TVEEPROM
 	select VIDEO_IR
 	select VIDEOBUF_VMALLOC
diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
index 3d1c3cc..8137a8c 100644
--- a/drivers/media/video/em28xx/Makefile
+++ b/drivers/media/video/em28xx/Makefile
@@ -8,6 +8,7 @@
 obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o
 
 EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
 
diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig
index b617170..eec115b 100644
--- a/drivers/media/video/ivtv/Kconfig
+++ b/drivers/media/video/ivtv/Kconfig
@@ -4,7 +4,7 @@
 	select I2C_ALGOBIT
 	select FW_LOADER
 	select VIDEO_IR
-	select VIDEO_TUNER
+	select MEDIA_TUNER
 	select VIDEO_TVEEPROM
 	select VIDEO_CX2341X
 	select VIDEO_CX25840
diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile
index a038901..26ce0d6 100644
--- a/drivers/media/video/ivtv/Makefile
+++ b/drivers/media/video/ivtv/Makefile
@@ -8,6 +8,7 @@
 obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o
 
 EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
 
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c
index e908649..4fb8fae 100644
--- a/drivers/media/video/ivtv/ivtv-cards.c
+++ b/drivers/media/video/ivtv/ivtv-cards.c
@@ -40,6 +40,8 @@
 #define MSP_MONO   MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \
 				MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
 
+#define V4L2_STD_NOT_MN (V4L2_STD_PAL|V4L2_STD_SECAM)
+
 /* usual i2c tuner addresses to probe */
 static struct ivtv_card_tuner_i2c ivtv_i2c_std = {
 	.radio = { I2C_CLIENT_END },
@@ -298,7 +300,7 @@
 	.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_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME },
 		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
 	},
 	.pci_list = ivtv_pci_mpg600,
@@ -339,7 +341,7 @@
 			      .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_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME },
 		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
 	},
 	.pci_list = ivtv_pci_mpg160,
@@ -375,7 +377,7 @@
 		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL },
 	},
 	.tuners = {
-		{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+		{ .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME },
 		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
 	},
 	.pci_list = ivtv_pci_pg600,
@@ -416,7 +418,7 @@
 	   on the country/region setting of the user to decide which tuner
 	   is available. */
 	.tuners = {
-		{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+		{ .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
 		{ .std = V4L2_STD_ALL - V4L2_STD_NTSC_M_JP,
 			.tuner = TUNER_PHILIPS_FM1236_MK3 },
 		{ .std = V4L2_STD_NTSC_M_JP, .tuner = TUNER_PHILIPS_FQ1286 },
@@ -490,7 +492,7 @@
 	.gpio_video_input  = { .mask = 0x0030, .tuner  = 0x0000,
 			  .composite = 0x0010, .svideo = 0x0020 },
 	.tuners = {
-		{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+		{ .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
 	},
 	.pci_list = ivtv_pci_tg5000tv,
 	.i2c = &ivtv_i2c_std,
@@ -521,7 +523,7 @@
 		{ IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
 	},
 	.tuners = {
-		{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+		{ .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
 	},
 	.pci_list = ivtv_pci_va2000,
 	.i2c = &ivtv_i2c_std,
@@ -565,7 +567,7 @@
 	.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_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
 		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
 	},
 	.pci_list = ivtv_pci_cx23416gyc,
@@ -597,7 +599,7 @@
 	.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_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
 		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
 	},
 	.i2c = &ivtv_i2c_std,
@@ -627,7 +629,7 @@
 	.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_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
 		{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
 	},
 	.i2c = &ivtv_i2c_std,
@@ -667,7 +669,7 @@
 	.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 },
+		{ .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 },
 	},
 	.pci_list = ivtv_pci_gv_mvprx,
 	.i2c = &ivtv_i2c_std,
@@ -704,7 +706,7 @@
 	.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 },
+		{ .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 },
 	},
 	.pci_list = ivtv_pci_gv_mvprx2e,
 	.i2c = &ivtv_i2c_std,
@@ -739,7 +741,7 @@
 	.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 },
+		{ .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
 	},
 	.pci_list = ivtv_pci_gotview_pci_dvd,
 	.i2c = &ivtv_i2c_std,
@@ -778,7 +780,7 @@
 	.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 },
+		{ .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
 	},
 	.pci_list = ivtv_pci_gotview_pci_dvd2,
 	.i2c = &ivtv_i2c_std,
@@ -856,7 +858,7 @@
 	.gpio_video_input  = { .mask = 0x0030, .tuner  = 0x0000,
 			       .composite = 0x0010, .svideo = 0x0020},
 	.tuners = {
-		{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+		{ .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
 	},
 	.pci_list = ivtv_pci_dctmvtvp1,
 	.i2c = &ivtv_i2c_std,
@@ -875,6 +877,7 @@
 static const struct ivtv_card ivtv_card_pg600v2 = {
 	.type = IVTV_CARD_PG600V2,
 	.name = "Yuan PG600-2, GotView PCI DVD Lite",
+	.comment = "only Composite and S-Video inputs are supported, not the tuner\n",
 	.v4l2_capabilities = IVTV_CAP_ENCODER,
 	.hw_video = IVTV_HW_CX25840,
 	.hw_audio = IVTV_HW_CX25840,
@@ -921,6 +924,7 @@
 	},
 	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
 	.gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */
+	.xceive_pin = 12,
 	.tuners = {
 		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
 	},
@@ -944,15 +948,22 @@
 	.hw_video = IVTV_HW_CX25840,
 	.hw_audio = IVTV_HW_CX25840,
 	.hw_audio_ctrl = IVTV_HW_CX25840,
-	.hw_all = IVTV_HW_CX25840 | IVTV_HW_WM8739,
+	.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739,
 	.video_inputs = {
-		{ IVTV_CARD_INPUT_SVIDEO1,    0, CX25840_SVIDEO3    },
-		{ IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 },
+		{ IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
+		{ IVTV_CARD_INPUT_SVIDEO1,    1, CX25840_SVIDEO3    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
 	},
 	.audio_inputs = {
+		{ IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
 		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL, 1 },
 	},
-	.gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, /* enable line-in */
+	/* enable line-in */
+	.gpio_init = { .direction = 0xe400, .initial_value = 0x4400 },
+	.xceive_pin = 10,
+	.tuners = {
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
 	.pci_list = ivtv_pci_avertv_mce116,
 	.i2c = &ivtv_i2c_std,
 };
@@ -990,7 +1001,7 @@
 	.gpio_audio_input  = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 },
 	.tuners = {
 		/* This card has a Partsnic PTI-5NF05 tuner */
-		{ .std = V4L2_STD_525_60, .tuner = TUNER_TCL_2002N },
+		{ .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_TCL_2002N },
 	},
 	.pci_list = ivtv_pci_aver_pvr150,
 	.i2c = &ivtv_i2c_radio,
@@ -1058,12 +1069,48 @@
 	},
 	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, M52790_IN_TUNER },
 	.tuners = {
-		{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+		{ .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 },
 	},
 	.pci_list = ivtv_pci_asus_falcon2,
 	.i2c = &ivtv_i2c_std,
 };
 
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia M104 miniPCI card */
+
+static const struct ivtv_card_pci_info ivtv_pci_aver_m104[] = {
+	{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc136 },
+	{ 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_aver_m104 = {
+	.type = IVTV_CARD_AVER_M104,
+	.name = "AVerMedia M104",
+	.comment = "Not yet supported!\n",
+	.v4l2_capabilities = 0, /*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 | IVTV_HW_WM8739,
+	.video_inputs = {
+		{ IVTV_CARD_INPUT_SVIDEO1,    0, CX25840_SVIDEO3    },
+		{ IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 },
+	},
+	.audio_inputs = {
+		{ IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL, 1 },
+	},
+	.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+	/* enable line-in + reset tuner */
+	.gpio_init = { .direction = 0xe400, .initial_value = 0x4000 },
+	.xceive_pin = 10,
+	.tuners = {
+		{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+	},
+	.pci_list = ivtv_pci_aver_m104,
+	.i2c = &ivtv_i2c_std,
+};
+
 static const struct ivtv_card *ivtv_card_list[] = {
 	&ivtv_card_pvr250,
 	&ivtv_card_pvr350,
@@ -1089,6 +1136,7 @@
 	&ivtv_card_asus_falcon2,
 	&ivtv_card_aver_pvr150,
 	&ivtv_card_aver_ezmaker,
+	&ivtv_card_aver_m104,
 
 	/* Variations of standard cards but with the same PCI IDs.
 	   These cards must come last in this list. */
@@ -1120,7 +1168,8 @@
 	if (index >= itv->nof_inputs)
 		return -EINVAL;
 	input->index = index;
-	strcpy(input->name, input_strs[card_input->video_type - 1]);
+	strlcpy(input->name, input_strs[card_input->video_type - 1],
+			sizeof(input->name));
 	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;
@@ -1137,7 +1186,7 @@
 	if (index >= itv->card->nof_outputs)
 		return -EINVAL;
 	output->index = index;
-	strcpy(output->name, card_output->name);
+	strlcpy(output->name, card_output->name, sizeof(output->name));
 	output->type = V4L2_OUTPUT_TYPE_ANALOG;
 	output->audioset = 1;
 	output->std = V4L2_STD_ALL;
@@ -1156,7 +1205,8 @@
 	memset(audio, 0, sizeof(*audio));
 	if (index >= itv->nof_audio_inputs)
 		return -EINVAL;
-	strcpy(audio->name, input_strs[aud_input->audio_type - 1]);
+	strlcpy(audio->name, input_strs[aud_input->audio_type - 1],
+			sizeof(audio->name));
 	audio->index = index;
 	audio->capability = V4L2_AUDCAP_STEREO;
 	return 0;
@@ -1167,6 +1217,6 @@
 	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");
+	strlcpy(aud_output->name, "A/V Audio Out", sizeof(aud_output->name));
 	return 0;
 }
diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h
index 9186fa2..748485d 100644
--- a/drivers/media/video/ivtv/ivtv-cards.h
+++ b/drivers/media/video/ivtv/ivtv-cards.h
@@ -48,7 +48,8 @@
 #define IVTV_CARD_ASUS_FALCON2	     21 /* ASUS Falcon2 */
 #define IVTV_CARD_AVER_PVR150PLUS    22 /* AVerMedia PVR-150 Plus */
 #define IVTV_CARD_AVER_EZMAKER       23 /* AVerMedia EZMaker PCI Deluxe */
-#define IVTV_CARD_LAST 		     23
+#define IVTV_CARD_AVER_M104          24 /* AverMedia M104 miniPCI card */
+#define IVTV_CARD_LAST 		     24
 
 /* Variants of existing cards but with the same PCI IDs. The driver
    detects these based on other device information.
@@ -244,6 +245,7 @@
 struct ivtv_card {
 	int type;
 	char *name;
+	char *comment;
 	u32 v4l2_capabilities;
 	u32 hw_video;		/* hardware used to process video */
 	u32 hw_audio;		/* hardware used to process audio */
@@ -256,6 +258,7 @@
 	int nof_outputs;
 	const struct ivtv_card_output *video_outputs;
 	u8 gr_config; 		/* config byte for the ghost reduction device */
+	u8 xceive_pin; 		/* XCeive tuner GPIO reset pin */
 
 	/* GPIO card-specific settings */
 	struct ivtv_gpio_init 		gpio_init;
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 065df53..47b5649 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -190,6 +190,7 @@
 		 "\t\t\t22 = ASUS Falcon2\n"
 		 "\t\t\t23 = AverMedia PVR-150 Plus\n"
 		 "\t\t\t24 = AverMedia EZMaker PCI Deluxe\n"
+		 "\t\t\t25 = AverMedia M104 (not yet working)\n"
 		 "\t\t\t 0 = Autodetect (default)\n"
 		 "\t\t\t-1 = Ignore this card\n\t\t");
 MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60");
@@ -871,7 +872,7 @@
 	unsigned i;
 
 	/* load modules */
-#ifndef CONFIG_VIDEO_TUNER
+#ifndef CONFIG_MEDIA_TUNER
 	hw = ivtv_request_module(itv, hw, "tuner", IVTV_HW_TUNER);
 #endif
 #ifndef CONFIG_VIDEO_CX25840
@@ -1097,6 +1098,13 @@
 		   The PCI IDs are not always reliable. */
 		ivtv_process_eeprom(itv);
 	}
+	if (itv->card->comment)
+		IVTV_INFO("%s", itv->card->comment);
+	if (itv->card->v4l2_capabilities == 0) {
+		/* card was detected but is not supported */
+		retval = -ENODEV;
+		goto free_i2c;
+	}
 
 	if (itv->std == 0) {
 		itv->std = V4L2_STD_NTSC_M;
@@ -1195,13 +1203,6 @@
 		ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std);
 	}
 
-	retval = ivtv_streams_setup(itv);
-	if (retval) {
-		IVTV_ERR("Error %d setting up streams\n", retval);
-		goto free_i2c;
-	}
-
-	IVTV_DEBUG_IRQ("Masking interrupts\n");
 	/* clear interrupt mask, effectively disabling interrupts */
 	ivtv_set_irq_mask(itv, 0xffffffff);
 
@@ -1210,32 +1211,38 @@
 			     IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv);
 	if (retval) {
 		IVTV_ERR("Failed to register irq %d\n", retval);
-		goto free_streams;
+		goto free_i2c;
+	}
+
+	retval = ivtv_streams_setup(itv);
+	if (retval) {
+		IVTV_ERR("Error %d setting up streams\n", retval);
+		goto free_irq;
 	}
 	retval = ivtv_streams_register(itv);
 	if (retval) {
 		IVTV_ERR("Error %d registering devices\n", retval);
-		goto free_irq;
+		goto free_streams;
 	}
 	IVTV_INFO("Initialized card #%d: %s\n", itv->num, itv->card_name);
 	return 0;
 
-      free_irq:
-	free_irq(itv->dev->irq, (void *)itv);
-      free_streams:
+free_streams:
 	ivtv_streams_cleanup(itv);
-      free_i2c:
+free_irq:
+	free_irq(itv->dev->irq, (void *)itv);
+free_i2c:
 	exit_ivtv_i2c(itv);
-      free_io:
+free_io:
 	ivtv_iounmap(itv);
-      free_mem:
+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:
+free_workqueue:
 	destroy_workqueue(itv->irq_work_queues);
-      err:
+err:
 	if (retval == 0)
 		retval = -ENODEV;
 	IVTV_ERR("Error %d on initialization\n", retval);
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
index 688cd38..d8ac09f 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -128,20 +128,17 @@
 {
 	struct i2c_algo_bit_data *algo = dev;
 	struct ivtv *itv = algo->data;
-	int curdir, curout;
+	u32 curout;
 
 	if (cmd != XC2028_TUNER_RESET)
 		return 0;
 	IVTV_DEBUG_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);
+	curout &= ~(1 << itv->card->xceive_pin);
 	write_reg(curout, IVTV_REG_GPIO_OUT);
 	schedule_timeout_interruptible(msecs_to_jiffies(1));
 
-	curout |= (1 << 12);
+	curout |= 1 << itv->card->xceive_pin;
 	write_reg(curout, IVTV_REG_GPIO_OUT);
 	schedule_timeout_interruptible(msecs_to_jiffies(1));
 	return 0;
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
index 9824eaf..771adf4 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.c
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -167,7 +167,8 @@
 		return -1;
 	id = hw_driverids[idx];
 	memset(&info, 0, sizeof(info));
-	strcpy(info.driver_name, hw_drivernames[idx]);
+	strlcpy(info.driver_name, hw_drivernames[idx],
+			sizeof(info.driver_name));
 	info.addr = hw_addrs[idx];
 	for (i = 0; itv->i2c_clients[i] && i < I2C_CLIENTS_MAX; i++) {}
 
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 15cac18..d508b5d 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -243,20 +243,31 @@
 	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 == 0)
+		cur_speed = 1000;
+	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;
+		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 >= 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;
+	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;
@@ -741,10 +752,9 @@
 		struct v4l2_capability *vcap = arg;
 
 		memset(vcap, 0, sizeof(*vcap));
-		strcpy(vcap->driver, IVTV_DRIVER_NAME);     /* driver name */
-		strncpy(vcap->card, itv->card_name,
-				sizeof(vcap->card)-1); 	    /* card type */
-		strcpy(vcap->bus_info, pci_name(itv->dev)); /* bus info... */
+		strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
+		strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
+		strlcpy(vcap->bus_info, pci_name(itv->dev), sizeof(vcap->bus_info));
 		vcap->version = IVTV_DRIVER_VERSION; 	    /* version */
 		vcap->capabilities = itv->v4l2_cap; 	    /* capabilities */
 
@@ -1018,7 +1028,7 @@
 				ivtv_std_60hz : ivtv_std_50hz;
 		vs->index = idx;
 		vs->id = enum_stds[idx].std;
-		strcpy(vs->name, enum_stds[idx].name);
+		strlcpy(vs->name, enum_stds[idx].name, sizeof(vs->name));
 		break;
 	}
 
@@ -1102,10 +1112,10 @@
 		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");
+			strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name));
 			vt->type = V4L2_TUNER_RADIO;
 		} else {
-			strcpy(vt->name, "ivtv TV Tuner");
+			strlcpy(vt->name, "ivtv TV Tuner", sizeof(vt->name));
 			vt->type = V4L2_TUNER_ANALOG_TV;
 		}
 		break;
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
index a329c46..d8ba3a4 100644
--- a/drivers/media/video/ivtv/ivtv-irq.c
+++ b/drivers/media/video/ivtv/ivtv-irq.c
@@ -384,7 +384,7 @@
 	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);
-	itv->dma_timer.expires = jiffies + msecs_to_jiffies(100);
+	itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
 	add_timer(&itv->dma_timer);
 }
 
@@ -400,7 +400,7 @@
 	ivtv_stream_sync_for_device(s);
 	write_reg(s->sg_handle, IVTV_REG_DECDMAADDR);
 	write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
-	itv->dma_timer.expires = jiffies + msecs_to_jiffies(100);
+	itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
 	add_timer(&itv->dma_timer);
 }
 
diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h
index 0f1d4cc..02c5ab0 100644
--- a/drivers/media/video/ivtv/ivtv-version.h
+++ b/drivers/media/video/ivtv/ivtv-version.h
@@ -23,7 +23,7 @@
 #define IVTV_DRIVER_NAME "ivtv"
 #define IVTV_DRIVER_VERSION_MAJOR 1
 #define IVTV_DRIVER_VERSION_MINOR 2
-#define IVTV_DRIVER_VERSION_PATCHLEVEL 0
+#define IVTV_DRIVER_VERSION_PATCHLEVEL 1
 
 #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/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c
index 3b23fc0..df789f6 100644
--- a/drivers/media/video/ivtv/ivtvfb.c
+++ b/drivers/media/video/ivtv/ivtvfb.c
@@ -532,7 +532,7 @@
 
 	IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n");
 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
-	strcpy(fix->id, "cx23415 TV out");
+	strlcpy(fix->id, "cx23415 TV out", sizeof(fix->id));
 	fix->smem_start = oi->video_pbase;
 	fix->smem_len = oi->video_buffer_size;
 	fix->type = FB_TYPE_PACKED_PIXELS;
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index ba09826..179e470 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -372,7 +372,7 @@
 }
 #endif
 
-const struct v4l2_queryctrl mt9m001_controls[] = {
+static const struct v4l2_queryctrl mt9m001_controls[] = {
 	{
 		.id		= V4L2_CID_VFLIP,
 		.type		= V4L2_CTRL_TYPE_BOOLEAN,
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index 7b22369..d1391ac 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -452,7 +452,7 @@
 }
 #endif
 
-const struct v4l2_queryctrl mt9v022_controls[] = {
+static const struct v4l2_queryctrl mt9v022_controls[] = {
 	{
 		.id		= V4L2_CID_VFLIP,
 		.type		= V4L2_CTRL_TYPE_BOOLEAN,
diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig
index 158b3d0..9620c67 100644
--- a/drivers/media/video/pvrusb2/Kconfig
+++ b/drivers/media/video/pvrusb2/Kconfig
@@ -1,14 +1,15 @@
 config VIDEO_PVRUSB2
 	tristate "Hauppauge WinTV-PVR USB2 support"
-	depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+	depends on VIDEO_V4L2 && I2C
 	select FW_LOADER
-	select VIDEO_TUNER
+	select MEDIA_TUNER
 	select VIDEO_TVEEPROM
 	select VIDEO_CX2341X
 	select VIDEO_SAA711X
 	select VIDEO_CX25840
 	select VIDEO_MSP3400
 	select VIDEO_WM8775
+	select VIDEO_CS53L32A
 	---help---
 	  This is a video4linux driver for Conexant 23416 based
 	  usb2 personal video recorder devices.
@@ -16,32 +17,6 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called pvrusb2
 
-config VIDEO_PVRUSB2_ONAIR_CREATOR
-	bool "pvrusb2 driver support for OnAir Creator model"
-	depends on VIDEO_PVRUSB2 && EXPERIMENTAL
-	select VIDEO_SAA711X
-	select VIDEO_CS53L32A
-	---help---
-
-	  This option enables support for the OnAir Creator USB tuner
-	  device.  This is a hybrid device, however currently only
-	  analog mode is supported.
-
-	  If you are in doubt, say Y.
-
-config VIDEO_PVRUSB2_ONAIR_USB2
-	bool "pvrusb2 driver support for OnAir USB2 model"
-	depends on VIDEO_PVRUSB2 && EXPERIMENTAL
-	select VIDEO_SAA711X
-	select VIDEO_CS53L32A
-	---help---
-
-	  This option enables support for the OnAir USB2 tuner device
-	  (also known as the Sasem tuner).  This is a hybrid device,
-	  however currently only analog mode is supported.
-
-	  If you are in doubt, say Y.
-
 config VIDEO_PVRUSB2_SYSFS
 	bool "pvrusb2 sysfs support (EXPERIMENTAL)"
 	default y
@@ -59,29 +34,23 @@
 	  Note: This feature is experimental and subject to change.
 
 config VIDEO_PVRUSB2_DVB
-	bool "pvrusb2 DVB support (EXPERIMENTAL)"
-	default n
+	bool "pvrusb2 ATSC/DVB support (EXPERIMENTAL)"
+	default y
 	depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
 	select DVB_S5H1409 if !DVB_FE_CUSTOMISE
 	select DVB_S5H1411 if !DVB_FE_CUSTOMISE
 	select DVB_TDA10048 if !DVB_FE_CUSTOMIZE
-	select DVB_TDA18271 if !DVB_FE_CUSTOMIZE
-	select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
-	select TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
+	select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE
+	select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
 	---help---
 
-	  This option enables compilation of a DVB interface for the
-	  pvrusb2 driver.  Currently this is very very experimental.
-	  It is also limiting - the DVB interface can only access the
-	  digital side of hybrid devices, and there are going to be
-	  issues if you attempt to mess with the V4L side at the same
-	  time.  Don't turn this on unless you know what you are
-	  doing.
+	  This option enables a DVB interface for the pvrusb2 driver.
+	  If your device does not support digital television, this
+	  feature will have no affect on the driver's operation.
 
-	  If you are in doubt, say N.
-
-	  Note: This feature is very experimental and might break
+	  If you are in doubt, say Y.
 
 config VIDEO_PVRUSB2_DEBUGIFC
 	bool "pvrusb2 debug interface"
diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile
index 5b3083c..4fda2de 100644
--- a/drivers/media/video/pvrusb2/Makefile
+++ b/drivers/media/video/pvrusb2/Makefile
@@ -16,5 +16,6 @@
 obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
 
 EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h
index 11537dd..707d2d9 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debug.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-debug.h
@@ -54,6 +54,7 @@
 #define PVR2_TRACE_DATA_FLOW  (1 << 25) /* Track data flow */
 #define PVR2_TRACE_DEBUGIFC   (1 << 26) /* Debug interface actions */
 #define PVR2_TRACE_GPIO       (1 << 27) /* GPIO state bit changes */
+#define PVR2_TRACE_DVB_FEED   (1 << 28) /* DVB transport feed debug */
 
 
 #endif /* __PVRUSB2_HDW_INTERNAL_H */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
index 3a141d9..5bf6d8f 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
@@ -153,7 +153,6 @@
 
 
 
-#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR
 /*------------------------------------------------------------------------*/
 /* OnAir Creator */
 
@@ -212,11 +211,9 @@
 		.dvb_props = &pvr2_onair_creator_fe_props,
 #endif
 };
-#endif
 
 
 
-#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_USB2
 /*------------------------------------------------------------------------*/
 /* OnAir USB 2.0 */
 
@@ -274,7 +271,6 @@
 		.dvb_props = &pvr2_onair_usb2_fe_props,
 #endif
 };
-#endif
 
 
 
@@ -497,14 +493,10 @@
 	  .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2},
 	{ USB_DEVICE(0x1164, 0x0602),
 	  .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d},
-#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR
 	{ USB_DEVICE(0x11ba, 0x1003),
 	  .driver_info = (kernel_ulong_t)&pvr2_device_onair_creator},
-#endif
-#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_USB2
 	{ USB_DEVICE(0x11ba, 0x1001),
 	  .driver_info = (kernel_ulong_t)&pvr2_device_onair_usb2},
-#endif
 	{ USB_DEVICE(0x2040, 0x7300),
 	  .driver_info = (kernel_ulong_t)&pvr2_device_73xxx},
 	{ USB_DEVICE(0x2040, 0x7500),
diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
index 6504c97..6ec4bf8 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-dvb.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
@@ -21,6 +21,7 @@
 #include <linux/kthread.h>
 #include <linux/freezer.h>
 #include "dvbdev.h"
+#include "pvrusb2-debug.h"
 #include "pvrusb2-hdw-internal.h"
 #include "pvrusb2-hdw.h"
 #include "pvrusb2-io.h"
@@ -35,7 +36,7 @@
 	struct pvr2_buffer *bp;
 	struct pvr2_stream *stream;
 
-	printk(KERN_DEBUG "dvb thread started\n");
+	pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread started");
 	set_freezable();
 
 	stream = adap->channel.stream->stream;
@@ -82,7 +83,7 @@
 	/* If we get here and ret is < 0, then an error has occurred.
 	   Probably would be a good idea to communicate that to DVB core... */
 
-	printk(KERN_DEBUG "dvb thread stopped\n");
+	pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread stopped");
 
 	return 0;
 }
@@ -210,7 +211,8 @@
 	do {
 		if (onoff) {
 			if (!adap->feedcount) {
-				printk(KERN_DEBUG "start feeding\n");
+				pvr2_trace(PVR2_TRACE_DVB_FEED,
+					   "start feeding demux");
 				ret = pvr2_dvb_stream_start(adap);
 				if (ret < 0) break;
 			}
@@ -218,7 +220,8 @@
 		} else if (adap->feedcount > 0) {
 			(adap->feedcount)--;
 			if (!adap->feedcount) {
-				printk(KERN_DEBUG "stop feeding\n");
+				pvr2_trace(PVR2_TRACE_DVB_FEED,
+					   "stop feeding demux");
 				pvr2_dvb_stream_end(adap);
 			}
 		}
@@ -230,15 +233,13 @@
 
 static int pvr2_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
 {
-	printk(KERN_DEBUG "start pid: 0x%04x, feedtype: %d\n",
-	       dvbdmxfeed->pid, dvbdmxfeed->type);
+	pvr2_trace(PVR2_TRACE_DVB_FEED, "start pid: 0x%04x", dvbdmxfeed->pid);
 	return pvr2_dvb_ctrl_feed(dvbdmxfeed, 1);
 }
 
 static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
 {
-	printk(KERN_DEBUG "stop pid: 0x%04x, feedtype: %d\n",
-	       dvbdmxfeed->pid, dvbdmxfeed->type);
+	pvr2_trace(PVR2_TRACE_DVB_FEED, "stop pid: 0x%04x", dvbdmxfeed->pid);
 	return pvr2_dvb_ctrl_feed(dvbdmxfeed, 0);
 }
 
@@ -259,7 +260,8 @@
 				   &adap->channel.hdw->usb_dev->dev,
 				   adapter_nr);
 	if (ret < 0) {
-		err("dvb_register_adapter failed: error %d", ret);
+		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+			   "dvb_register_adapter failed: error %d", ret);
 		goto err;
 	}
 	adap->dvb_adap.priv = adap;
@@ -276,7 +278,8 @@
 
 	ret = dvb_dmx_init(&adap->demux);
 	if (ret < 0) {
-		err("dvb_dmx_init failed: error %d", ret);
+		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+			   "dvb_dmx_init failed: error %d", ret);
 		goto err_dmx;
 	}
 
@@ -286,7 +289,8 @@
 
 	ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap);
 	if (ret < 0) {
-		err("dvb_dmxdev_init failed: error %d", ret);
+		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+			   "dvb_dmxdev_init failed: error %d", ret);
 		goto err_dmx_dev;
 	}
 
@@ -304,7 +308,7 @@
 
 static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap)
 {
-	printk(KERN_DEBUG "unregistering DVB devices\n");
+	pvr2_trace(PVR2_TRACE_INFO, "unregistering DVB devices");
 	dvb_net_release(&adap->dvb_net);
 	adap->demux.dmx.close(&adap->demux.dmx);
 	dvb_dmxdev_release(&adap->dmxdev);
@@ -320,7 +324,7 @@
 	int ret = 0;
 
 	if (dvb_props == NULL) {
-		err("fe_props not defined!");
+		pvr2_trace(PVR2_TRACE_ERROR_LEGS, "fe_props not defined!");
 		return -EINVAL;
 	}
 
@@ -328,13 +332,15 @@
 	    &adap->channel,
 	    (1 << PVR2_CVAL_INPUT_DTV));
 	if (ret) {
-		err("failed to grab control of dtv input (code=%d)",
+		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+			   "failed to grab control of dtv input (code=%d)",
 		    ret);
 		return ret;
 	}
 
 	if (dvb_props->frontend_attach == NULL) {
-		err("frontend_attach not defined!");
+		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+			   "frontend_attach not defined!");
 		ret = -EINVAL;
 		goto done;
 	}
@@ -342,7 +348,8 @@
 	if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) {
 
 		if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) {
-			err("frontend registration failed!");
+			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+				   "frontend registration failed!");
 			dvb_frontend_detach(adap->fe);
 			adap->fe = NULL;
 			ret = -ENODEV;
@@ -359,7 +366,8 @@
 		adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl;
 
 	} else {
-		err("no frontend was attached!");
+		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+			   "no frontend was attached!");
 		ret = -ENODEV;
 		return ret;
 	}
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
index e086f14..40e4c3b 100644
--- a/drivers/media/video/saa7134/Kconfig
+++ b/drivers/media/video/saa7134/Kconfig
@@ -3,7 +3,7 @@
 	depends on VIDEO_DEV && PCI && I2C && INPUT
 	select VIDEOBUF_DMA_SG
 	select VIDEO_IR
-	select VIDEO_TUNER
+	select MEDIA_TUNER
 	select VIDEO_TVEEPROM
 	select CRC32
 	---help---
@@ -35,9 +35,9 @@
 	select DVB_NXT200X if !DVB_FE_CUSTOMISE
 	select DVB_TDA10086 if !DVB_FE_CUSTOMISE
 	select DVB_TDA826X if !DVB_FE_CUSTOMISE
-	select DVB_TDA827X if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE
 	select DVB_ISL6421 if !DVB_FE_CUSTOMISE
-	select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
 	---help---
 	  This adds support for DVB cards based on the
 	  Philips saa7134 chip.
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
index 9aff937..3dbaa19 100644
--- a/drivers/media/video/saa7134/Makefile
+++ b/drivers/media/video/saa7134/Makefile
@@ -11,5 +11,6 @@
 obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
 
 EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 9837595..b111903 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -47,6 +47,9 @@
 /* ------------------------------------------------------------------ */
 /* board config info                                                  */
 
+/* If radio_type !=UNSET, radio_addr should be specified
+ */
+
 struct saa7134_board saa7134_boards[] = {
 	[SAA7134_BOARD_UNKNOWN] = {
 		.name		= "UNKNOWN/GENERIC",
@@ -3087,7 +3090,7 @@
 		.tuner_type     = TUNER_PHILIPS_TD1316, /* untested */
 		.radio_type     = TUNER_TEA5767, /* untested */
 		.tuner_addr     = ADDR_UNSET,
-		.radio_addr     = ADDR_UNSET,
+		.radio_addr     = 0x60,
 		.tda9887_conf   = TDA9887_PRESENT,
 		.mpeg           = SAA7134_MPEG_DVB,
 		.inputs         = {{
@@ -4247,6 +4250,36 @@
 			.amux = LINE1,
 		} },
 	},
+	[SAA7134_BOARD_BEHOLD_H6] = {
+		/* Igor Kuznetsov <igk@igk.ru> */
+		.name           = "Beholder BeholdTV H6",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 3,
+			.amux = TV,
+			.tv   = 1,
+		}, {
+			.name = name_comp1,
+			.vmux = 1,
+			.amux = LINE1,
+		}, {
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE1,
+		} },
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+		},
+		/* no DVB support for now */
+		/* .mpeg           = SAA7134_MPEG_DVB, */
+	},
 };
 
 const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -5197,6 +5230,12 @@
 		.subvendor    = 0x5ace,
 		.subdevice    = 0x6193,
 		.driver_data  = SAA7134_BOARD_BEHOLD_M6,
+	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x6191,
+		.driver_data  = SAA7134_BOARD_BEHOLD_M6,
 	},{
 		.vendor       = PCI_VENDOR_ID_PHILIPS,
 		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
@@ -5246,6 +5285,12 @@
 		.subdevice    = 0xc900,
 		.driver_data  = SAA7134_BOARD_VIDEOMATE_T750,
 	}, {
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x5ace,
+		.subdevice    = 0x6290,
+		.driver_data  = SAA7134_BOARD_BEHOLD_H6,
+	}, {
 		/* --- boards without eeprom + subsystem ID --- */
 		.vendor       = PCI_VENDOR_ID_PHILIPS,
 		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -5577,20 +5622,87 @@
 	return 0;
 }
 
+static void saa7134_tuner_setup(struct saa7134_dev *dev)
+{
+	struct tuner_setup tun_setup;
+	unsigned int mode_mask = T_RADIO     |
+				 T_ANALOG_TV |
+				 T_DIGITAL_TV;
+
+	memset(&tun_setup, 0, sizeof(tun_setup));
+	tun_setup.tuner_callback = saa7134_tuner_callback;
+
+	if (saa7134_boards[dev->board].radio_type != UNSET) {
+		tun_setup.type = saa7134_boards[dev->board].radio_type;
+		tun_setup.addr = saa7134_boards[dev->board].radio_addr;
+
+		tun_setup.mode_mask = T_RADIO;
+
+		saa7134_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
+		mode_mask &= ~T_RADIO;
+	}
+
+	if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type != UNSET)) {
+		tun_setup.type = dev->tuner_type;
+		tun_setup.addr = dev->tuner_addr;
+		tun_setup.config = saa7134_boards[dev->board].tuner_config;
+		tun_setup.tuner_callback = saa7134_tuner_callback;
+
+		tun_setup.mode_mask = mode_mask;
+
+		saa7134_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
+	}
+
+	if (dev->tda9887_conf) {
+		struct v4l2_priv_tun_config tda9887_cfg;
+
+		tda9887_cfg.tuner = TUNER_TDA9887;
+		tda9887_cfg.priv = &dev->tda9887_conf;
+
+		saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG,
+					 &tda9887_cfg);
+	}
+
+	if (dev->tuner_type == TUNER_XC2028) {
+		struct v4l2_priv_tun_config  xc2028_cfg;
+		struct xc2028_ctrl           ctl;
+
+		memset(&xc2028_cfg, 0, sizeof(ctl));
+		memset(&ctl, 0, sizeof(ctl));
+
+		ctl.fname   = XC2028_DEFAULT_FIRMWARE;
+		ctl.max_len = 64;
+
+		switch (dev->board) {
+		case SAA7134_BOARD_AVERMEDIA_A16D:
+			ctl.demod = XC3028_FE_ZARLINK456;
+			break;
+		default:
+			ctl.demod = XC3028_FE_OREN538;
+			ctl.mts = 1;
+		}
+
+		xc2028_cfg.tuner = TUNER_XC2028;
+		xc2028_cfg.priv  = &ctl;
+
+		saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg);
+	}
+}
+
 /* stuff which needs working i2c */
 int saa7134_board_init2(struct saa7134_dev *dev)
 {
 	unsigned char buf;
 	int board;
-	struct tuner_setup tun_setup;
-	tun_setup.config = 0;
-	tun_setup.tuner_callback = saa7134_tuner_callback;
+
+	dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+	dev->tuner_addr = saa7134_boards[dev->board].tuner_addr;
 
 	switch (dev->board) {
 	case SAA7134_BOARD_BMK_MPEX_NOTUNER:
 	case SAA7134_BOARD_BMK_MPEX_TUNER:
 		dev->i2c_client.addr = 0x60;
-		board = (i2c_master_recv(&dev->i2c_client,&buf,0) < 0)
+		board = (i2c_master_recv(&dev->i2c_client, &buf, 0) < 0)
 			? SAA7134_BOARD_BMK_MPEX_NOTUNER
 			: SAA7134_BOARD_BMK_MPEX_TUNER;
 		if (board == dev->board)
@@ -5600,21 +5712,9 @@
 		saa7134_boards[dev->board].name);
 		dev->tuner_type = saa7134_boards[dev->board].tuner_type;
 
-		if (TUNER_ABSENT != dev->tuner_type) {
-			tun_setup.mode_mask = T_RADIO     |
-					      T_ANALOG_TV |
-					      T_DIGITAL_TV;
-			tun_setup.type = dev->tuner_type;
-			tun_setup.addr = ADDR_UNSET;
-			tun_setup.tuner_callback = saa7134_tuner_callback;
-
-			saa7134_i2c_call_clients(dev,
-						 TUNER_SET_TYPE_ADDR,
-						 &tun_setup);
-		}
 		break;
 	case SAA7134_BOARD_MD7134:
-		{
+	{
 		u8 subaddr;
 		u8 data[3];
 		int ret, tuner_t;
@@ -5667,30 +5767,8 @@
 		}
 
 		printk(KERN_INFO "%s Tuner type is %d\n", dev->name, dev->tuner_type);
-		if (dev->tuner_type == TUNER_PHILIPS_FMD1216ME_MK3) {
-			struct v4l2_priv_tun_config tda9887_cfg;
-
-			tda9887_cfg.tuner = TUNER_TDA9887;
-			tda9887_cfg.priv  = &dev->tda9887_conf;
-
-			dev->tda9887_conf = TDA9887_PRESENT      |
-					    TDA9887_PORT1_ACTIVE |
-					    TDA9887_PORT2_ACTIVE;
-
-			saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG,
-						 &tda9887_cfg);
-		}
-
-		tun_setup.mode_mask = T_RADIO     |
-				      T_ANALOG_TV |
-				      T_DIGITAL_TV;
-		tun_setup.type = dev->tuner_type;
-		tun_setup.addr = ADDR_UNSET;
-
-		saa7134_i2c_call_clients(dev,
-					 TUNER_SET_TYPE_ADDR, &tun_setup);
-		}
 		break;
+	}
 	case SAA7134_BOARD_PHILIPS_EUROPA:
 		if (dev->autodetected && (dev->eedata[0x41] == 0x1c)) {
 			/* Reconfigure board as Snake reference design */
@@ -5702,43 +5780,43 @@
 		}
 	case SAA7134_BOARD_VIDEOMATE_DVBT_300:
 	case SAA7134_BOARD_ASUS_EUROPA2_HYBRID:
+	{
+
 		/* The Philips EUROPA based hybrid boards have the tuner connected through
 		 * the channel decoder. We have to make it transparent to find it
 		 */
-		{
 		u8 data[] = { 0x07, 0x02};
 		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
 
-		tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
-		tun_setup.type = dev->tuner_type;
-		tun_setup.addr = dev->tuner_addr;
-
-		saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup);
-		}
 		break;
+	}
 	case SAA7134_BOARD_PHILIPS_TIGER:
 	case SAA7134_BOARD_PHILIPS_TIGER_S:
-		{
+	{
 		u8 data[] = { 0x3c, 0x33, 0x60};
 		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
-		if(dev->autodetected && (dev->eedata[0x49] == 0x50)) {
+		if (dev->autodetected && (dev->eedata[0x49] == 0x50)) {
 			dev->board = SAA7134_BOARD_PHILIPS_TIGER_S;
 			printk(KERN_INFO "%s: Reconfigured board as %s\n",
 				dev->name, saa7134_boards[dev->board].name);
 		}
-		if(dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) {
-			tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
-			tun_setup.type = TUNER_PHILIPS_TDA8290;
-			tun_setup.addr = 0x4b;
-			tun_setup.config = 2;
+		if (dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) {
+			dev->tuner_type = TUNER_PHILIPS_TDA8290;
 
-			saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup);
+			saa7134_tuner_setup(dev);
+
 			data[2] = 0x68;
+			i2c_transfer(&dev->i2c_adap, &msg, 1);
+
+			/* Tuner setup is handled before I2C transfer.
+			   Due to that, there's no need to do it later
+			 */
+			return 0;
 		}
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
-		}
 		break;
+	}
 	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
 		hauppauge_eeprom(dev, dev->eedata+0x80);
 		/* break intentionally omitted */
@@ -5751,52 +5829,55 @@
 	case SAA7134_BOARD_AVERMEDIA_SUPER_007:
 	case SAA7134_BOARD_TWINHAN_DTV_DVB_3056:
 	case SAA7134_BOARD_CREATIX_CTX953:
+	{
 		/* this is a hybrid board, initialize to analog mode
 		 * and configure firmware eeprom address
 		 */
-		{
 		u8 data[] = { 0x3c, 0x33, 0x60};
 		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
-		}
 		break;
+	}
 	case SAA7134_BOARD_FLYDVB_TRIO:
-		{
+	{
 		u8 data[] = { 0x3c, 0x33, 0x62};
 		struct i2c_msg msg = {.addr=0x09, .flags=0, .buf=data, .len = sizeof(data)};
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
-		}
 		break;
+	}
 	case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
 	case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
+	{
 		/* initialize analog mode  */
-		{
 		u8 data[] = { 0x3c, 0x33, 0x6a};
 		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
-		}
 		break;
+	}
 	case SAA7134_BOARD_CINERGY_HT_PCMCIA:
 	case SAA7134_BOARD_CINERGY_HT_PCI:
+	{
 		/* initialize analog mode */
-		{
 		u8 data[] = { 0x3c, 0x33, 0x68};
 		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
-		}
 		break;
+	}
 	case SAA7134_BOARD_KWORLD_ATSC110:
-		{
-			/* enable tuner */
-			int i;
-			static const u8 buffer [] = { 0x10,0x12,0x13,0x04,0x16,0x00,0x14,0x04,0x017,0x00 };
-			dev->i2c_client.addr = 0x0a;
-			for (i = 0; i < 5; i++)
-				if (2 != i2c_master_send(&dev->i2c_client,&buffer[i*2],2))
-					printk(KERN_WARNING "%s: Unable to enable tuner(%i).\n",
-					       dev->name, i);
-		}
+	{
+		/* enable tuner */
+		int i;
+		static const u8 buffer [] = { 0x10, 0x12, 0x13, 0x04, 0x16,
+					      0x00, 0x14, 0x04, 0x17, 0x00 };
+		dev->i2c_client.addr = 0x0a;
+		for (i = 0; i < 5; i++)
+			if (2 != i2c_master_send(&dev->i2c_client,
+						 &buffer[i*2], 2))
+				printk(KERN_WARNING
+				       "%s: Unable to enable tuner(%i).\n",
+				       dev->name, i);
 		break;
+	}
 	case SAA7134_BOARD_VIDEOMATE_DVBT_200:
 	case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
 		/* The T200 and the T200A share the same pci id.  Consequently,
@@ -5821,7 +5902,7 @@
 		}
 		break;
 	case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM:
-		{
+	{
 		struct v4l2_priv_tun_config tea5767_cfg;
 		struct tea5767_ctrl ctl;
 
@@ -5832,34 +5913,11 @@
 		tea5767_cfg.tuner = TUNER_TEA5767;
 		tea5767_cfg.priv  = &ctl;
 		saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &tea5767_cfg);
-		}
 		break;
 	}
+	} /* switch() */
 
-	if (dev->tuner_type == TUNER_XC2028) {
-		struct v4l2_priv_tun_config  xc2028_cfg;
-		struct xc2028_ctrl           ctl;
-
-		memset(&xc2028_cfg, 0, sizeof(ctl));
-		memset(&ctl, 0, sizeof(ctl));
-
-		ctl.fname   = XC2028_DEFAULT_FIRMWARE;
-		ctl.max_len = 64;
-
-		switch (dev->board) {
-		case SAA7134_BOARD_AVERMEDIA_A16D:
-			ctl.demod = XC3028_FE_ZARLINK456;
-			break;
-		default:
-			ctl.demod = XC3028_FE_OREN538;
-			ctl.mts = 1;
-		}
-
-		xc2028_cfg.tuner = TUNER_XC2028;
-		xc2028_cfg.priv  = &ctl;
-
-		saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg);
-	}
+	saa7134_tuner_setup(dev);
 
 	return 0;
 }
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
index 2ccfaba..d8af386 100644
--- a/drivers/media/video/saa7134/saa7134-i2c.c
+++ b/drivers/media/video/saa7134/saa7134-i2c.c
@@ -324,8 +324,6 @@
 static int attach_inform(struct i2c_client *client)
 {
 	struct saa7134_dev *dev = client->adapter->algo_data;
-	int tuner = dev->tuner_type;
-	struct tuner_setup tun_setup;
 
 	d1printk( "%s i2c attach [addr=0x%x,client=%s]\n",
 		client->driver->driver.name, client->addr, client->name);
@@ -346,46 +344,6 @@
 		}
 	}
 
-	if (!client->driver->command)
-		return 0;
-
-	if (saa7134_boards[dev->board].radio_type != UNSET) {
-
-		tun_setup.type = saa7134_boards[dev->board].radio_type;
-		tun_setup.addr = saa7134_boards[dev->board].radio_addr;
-
-		if ((tun_setup.addr == ADDR_UNSET) || (tun_setup.addr == client->addr)) {
-			tun_setup.mode_mask = T_RADIO;
-
-			client->driver->command(client, TUNER_SET_TYPE_ADDR, &tun_setup);
-		}
-	}
-
-	if (tuner != UNSET) {
-		tun_setup.type = tuner;
-		tun_setup.addr = saa7134_boards[dev->board].tuner_addr;
-		tun_setup.config = saa7134_boards[dev->board].tuner_config;
-		tun_setup.tuner_callback = saa7134_tuner_callback;
-
-		if ((tun_setup.addr == ADDR_UNSET)||(tun_setup.addr == client->addr)) {
-
-			tun_setup.mode_mask = T_ANALOG_TV;
-
-			client->driver->command(client,TUNER_SET_TYPE_ADDR, &tun_setup);
-		}
-
-		if (tuner == TUNER_TDA9887) {
-			struct v4l2_priv_tun_config tda9887_cfg;
-
-			tda9887_cfg.tuner = TUNER_TDA9887;
-			tda9887_cfg.priv = &dev->tda9887_conf;
-
-			client->driver->command(client, TUNER_SET_CONFIG,
-						&tda9887_cfg);
-		}
-	}
-
-
 	return 0;
 }
 
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index 767ff30..919632b 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -531,6 +531,7 @@
 		break;
 	case SAA7134_BOARD_BEHOLD_607_9FM:
 	case SAA7134_BOARD_BEHOLD_M6:
+	case SAA7134_BOARD_BEHOLD_H6:
 		snprintf(ir->c.name, sizeof(ir->c.name), "BeholdTV");
 		ir->get_key   = get_key_beholdm6xx;
 		ir->ir_codes  = ir_codes_behold;
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 924ffd1..34ff0d4 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -263,6 +263,7 @@
 #define SAA7134_BOARD_VIDEOMATE_T750       139
 #define SAA7134_BOARD_AVERMEDIA_A700_PRO    140
 #define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141
+#define SAA7134_BOARD_BEHOLD_H6      142
 
 
 #define SAA7134_MAXBOARDS 8
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 2a27482..cc19c4a 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -33,6 +33,46 @@
 
 #define PREFIX t->i2c->driver->driver.name
 
+/** This macro allows us to probe dynamically, avoiding static links */
+#ifdef CONFIG_DVB_CORE_ATTACH
+#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
+	int __r = -EINVAL; \
+	typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
+	if (__a) { \
+		__r = (int) __a(ARGS); \
+	} else { \
+		printk(KERN_ERR "TUNER: Unable to find " \
+				"symbol "#FUNCTION"()\n"); \
+	} \
+	symbol_put(FUNCTION); \
+	__r; \
+})
+
+static void tuner_detach(struct dvb_frontend *fe)
+{
+	if (fe->ops.tuner_ops.release) {
+		fe->ops.tuner_ops.release(fe);
+		symbol_put_addr(fe->ops.tuner_ops.release);
+	}
+	if (fe->ops.analog_ops.release) {
+		fe->ops.analog_ops.release(fe);
+		symbol_put_addr(fe->ops.analog_ops.release);
+	}
+}
+#else
+#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
+	FUNCTION(ARGS); \
+})
+
+static void tuner_detach(struct dvb_frontend *fe)
+{
+	if (fe->ops.tuner_ops.release)
+		fe->ops.tuner_ops.release(fe);
+	if (fe->ops.analog_ops.release)
+		fe->ops.analog_ops.release(fe);
+}
+#endif
+
 struct tuner {
 	/* device */
 	struct dvb_frontend fe;
@@ -56,7 +96,7 @@
 
 /* standard i2c insmod options */
 static unsigned short normal_i2c[] = {
-#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE))
 	0x10,
 #endif
 	0x42, 0x43, 0x4a, 0x4b,			/* tda8290 */
@@ -139,22 +179,6 @@
 	fe_tuner_ops->set_analog_params(fe, params);
 }
 
-static void fe_release(struct dvb_frontend *fe)
-{
-	if (fe->ops.tuner_ops.release)
-		fe->ops.tuner_ops.release(fe);
-
-	/* DO NOT kfree(fe->analog_demod_priv)
-	 *
-	 * If we are in this function, analog_demod_priv contains a pointer
-	 * to struct tuner *t.  This will be kfree'd in tuner_detach().
-	 *
-	 * Otherwise, fe->ops.analog_demod_ops->release will
-	 * handle the cleanup for analog demodulator modules.
-	 */
-	fe->analog_demod_priv = NULL;
-}
-
 static void fe_standby(struct dvb_frontend *fe)
 {
 	struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
@@ -191,7 +215,6 @@
 static struct analog_demod_ops tuner_core_ops = {
 	.set_params     = fe_set_params,
 	.standby        = fe_standby,
-	.release        = fe_release,
 	.has_signal     = fe_has_signal,
 	.set_config     = fe_set_config,
 	.tuner_status   = tuner_status
@@ -323,7 +346,8 @@
 		.lna_cfg        = t->config,
 		.tuner_callback = t->tuner_callback,
 	};
-	tda829x_attach(&t->fe, t->i2c->adapter, t->i2c->addr, &cfg);
+	dvb_attach(tda829x_attach,
+		   &t->fe, t->i2c->adapter, t->i2c->addr, &cfg);
 }
 
 static struct xc5000_config xc5000_cfg;
@@ -356,12 +380,13 @@
 	}
 
 	/* discard private data, in case set_type() was previously called */
-	if (analog_ops->release)
-		analog_ops->release(&t->fe);
+	tuner_detach(&t->fe);
+	t->fe.analog_demod_priv = NULL;
 
 	switch (t->type) {
 	case TUNER_MT2032:
-		microtune_attach(&t->fe, t->i2c->adapter, t->i2c->addr);
+		dvb_attach(microtune_attach,
+			   &t->fe, t->i2c->adapter, t->i2c->addr);
 		break;
 	case TUNER_PHILIPS_TDA8290:
 	{
@@ -369,12 +394,14 @@
 		break;
 	}
 	case TUNER_TEA5767:
-		if (!tea5767_attach(&t->fe, t->i2c->adapter, t->i2c->addr))
+		if (!dvb_attach(tea5767_attach, &t->fe,
+				t->i2c->adapter, t->i2c->addr))
 			goto attach_failed;
 		t->mode_mask = T_RADIO;
 		break;
 	case TUNER_TEA5761:
-		if (!tea5761_attach(&t->fe, t->i2c->adapter, t->i2c->addr))
+		if (!dvb_attach(tea5761_attach, &t->fe,
+				t->i2c->adapter, t->i2c->addr))
 			goto attach_failed;
 		t->mode_mask = T_RADIO;
 		break;
@@ -388,8 +415,8 @@
 		buffer[2] = 0x86;
 		buffer[3] = 0x54;
 		i2c_master_send(c, buffer, 4);
-		if (!simple_tuner_attach(&t->fe, t->i2c->adapter, t->i2c->addr,
-					t->type))
+		if (!dvb_attach(simple_tuner_attach, &t->fe,
+				t->i2c->adapter, t->i2c->addr, t->type))
 			goto attach_failed;
 		break;
 	case TUNER_PHILIPS_TD1316:
@@ -397,9 +424,9 @@
 		buffer[1] = 0xdc;
 		buffer[2] = 0x86;
 		buffer[3] = 0xa4;
-		i2c_master_send(c,buffer,4);
-		if (!simple_tuner_attach(&t->fe, t->i2c->adapter,
-					t->i2c->addr, t->type))
+		i2c_master_send(c, buffer, 4);
+		if (!dvb_attach(simple_tuner_attach, &t->fe,
+				t->i2c->adapter, t->i2c->addr, t->type))
 			goto attach_failed;
 		break;
 	case TUNER_XC2028:
@@ -409,12 +436,13 @@
 			.i2c_addr  = t->i2c->addr,
 			.callback  = t->tuner_callback,
 		};
-		if (!xc2028_attach(&t->fe, &cfg))
+		if (!dvb_attach(xc2028_attach, &t->fe, &cfg))
 			goto attach_failed;
 		break;
 	}
 	case TUNER_TDA9887:
-		tda9887_attach(&t->fe, t->i2c->adapter, t->i2c->addr);
+		dvb_attach(tda9887_attach,
+			   &t->fe, t->i2c->adapter, t->i2c->addr);
 		break;
 	case TUNER_XC5000:
 	{
@@ -424,7 +452,8 @@
 		xc5000_cfg.if_khz	  = 5380;
 		xc5000_cfg.priv           = c->adapter->algo_data;
 		xc5000_cfg.tuner_callback = t->tuner_callback;
-		if (!xc5000_attach(&t->fe, t->i2c->adapter, &xc5000_cfg))
+		if (!dvb_attach(xc5000_attach,
+				&t->fe, t->i2c->adapter, &xc5000_cfg))
 			goto attach_failed;
 
 		xc_tuner_ops = &t->fe.ops.tuner_ops;
@@ -433,8 +462,8 @@
 		break;
 	}
 	default:
-		if (!simple_tuner_attach(&t->fe, t->i2c->adapter,
-					t->i2c->addr, t->type))
+		if (!dvb_attach(simple_tuner_attach, &t->fe,
+				t->i2c->adapter, t->i2c->addr, t->type))
 			goto attach_failed;
 
 		break;
@@ -442,12 +471,14 @@
 
 	if ((NULL == analog_ops->set_params) &&
 	    (fe_tuner_ops->set_analog_params)) {
+
 		strlcpy(t->i2c->name, fe_tuner_ops->info.name,
 			sizeof(t->i2c->name));
 
 		t->fe.analog_demod_priv = t;
 		memcpy(analog_ops, &tuner_core_ops,
 		       sizeof(struct analog_demod_ops));
+
 	} else {
 		strlcpy(t->i2c->name, analog_ops->info.name,
 			sizeof(t->i2c->name));
@@ -645,8 +676,8 @@
 {
 	struct tuner *t = fe->analog_demod_priv;
 	unsigned long freq, freq_fraction;
-	struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
-	struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+	struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+	struct analog_demod_ops *analog_ops = &fe->ops.analog_ops;
 	const char *p;
 
 	switch (t->mode) {
@@ -730,8 +761,10 @@
 	struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
 	struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
 
-	if (tuner_debug>1)
+	if (tuner_debug > 1) {
 		v4l_i2c_print_ioctl(client,cmd);
+		printk("\n");
+	}
 
 	switch (cmd) {
 	/* --- configuration --- */
@@ -1112,8 +1145,9 @@
 	if (!no_autodetect) {
 		switch (client->addr) {
 		case 0x10:
-			if (tea5761_autodetection(t->i2c->adapter,
-						  t->i2c->addr) >= 0) {
+			if (tuner_symbol_probe(tea5761_autodetection,
+					       t->i2c->adapter,
+					       t->i2c->addr) >= 0) {
 				t->type = TUNER_TEA5761;
 				t->mode_mask = T_RADIO;
 				t->mode = T_STANDBY;
@@ -1132,8 +1166,8 @@
 		case 0x4b:
 			/* If chip is not tda8290, don't register.
 			   since it can be tda9887*/
-			if (tda829x_probe(t->i2c->adapter,
-					  t->i2c->addr) == 0) {
+			if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter,
+					       t->i2c->addr) == 0) {
 				tuner_dbg("tda829x detected\n");
 			} else {
 				/* Default is being tda9887 */
@@ -1145,7 +1179,8 @@
 			}
 			break;
 		case 0x60:
-			if (tea5767_autodetection(t->i2c->adapter, t->i2c->addr)
+			if (tuner_symbol_probe(tea5767_autodetection,
+					       t->i2c->adapter, t->i2c->addr)
 					!= EINVAL) {
 				t->type = TUNER_TEA5767;
 				t->mode_mask = T_RADIO;
@@ -1234,10 +1269,9 @@
 static int tuner_remove(struct i2c_client *client)
 {
 	struct tuner *t = i2c_get_clientdata(client);
-	struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
 
-	if (analog_ops->release)
-		analog_ops->release(&t->fe);
+	tuner_detach(&t->fe);
+	t->fe.analog_demod_priv = NULL;
 
 	list_del(&t->list);
 	kfree(t);
diff --git a/drivers/media/video/usbvision/Kconfig b/drivers/media/video/usbvision/Kconfig
index fc24ef0..74e1d30 100644
--- a/drivers/media/video/usbvision/Kconfig
+++ b/drivers/media/video/usbvision/Kconfig
@@ -1,7 +1,7 @@
 config VIDEO_USBVISION
 	tristate "USB video devices based on Nogatech NT1003/1004/1005"
 	depends on I2C && VIDEO_V4L2
-	select VIDEO_TUNER
+	select MEDIA_TUNER
 	select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
 	---help---
 	  There are more than 50 different USB video devices based on
diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile
index 9ac92a8..3387187 100644
--- a/drivers/media/video/usbvision/Makefile
+++ b/drivers/media/video/usbvision/Makefile
@@ -3,3 +3,4 @@
 obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o
 
 EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index 0ea0bd8..2a52774 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -64,6 +64,7 @@
 	/* Conexant MPEG encoder/decoders: reserved range 410-420 */
 	V4L2_IDENT_CX23415 = 415,
 	V4L2_IDENT_CX23416 = 416,
+	V4L2_IDENT_CX23418 = 418,
 
 	/* module vp27smpx: just ident 2700 */
 	V4L2_IDENT_VP27SMPX = 2700,