diff --git a/etc/MAKEDEV b/etc/MAKEDEV
index 4234a43..82f650c 100644
--- a/etc/MAKEDEV
+++ b/etc/MAKEDEV
@@ -147,6 +147,7 @@
 #	ctx*	Cortex-I video acquisition card
 #	meteor*	Matrox Meteor video acquisition card (pci)
 #	bktr*	Bt848 based video acquisition card (pci)
+#	cxm*	Conexant iTVC MPEG Coder
 #	labpc*	National Instrument's Lab-PC and LAB-PC+
 #	perfmon	CPU performance-monitoring counters
 #	pci	PCI configuration-space access from user mode
@@ -1998,6 +1999,14 @@ bktr?)
 
 #-----------------------------------------------------------------------------
 
+cxm?)
+	unit=`expr $i : 'cxm\(.*\)'`
+
+	mknod cxm   $unit c 93 `unit2minor $unit` root:wheel 444
+	;;
+
+#-----------------------------------------------------------------------------
+
 tun*)
 	ntun=`expr $i : 'tun\(.*\)$'`
 	unit=0
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 6a9f3ff..ee9d46d 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -52,6 +52,7 @@ MAN=	aac.4 \
 	crypto.4 \
 	csa.4 \
 	cue.4 \
+	cxm.4 \
 	da.4 \
 	dc.4 \
 	dcons.4 \
diff --git a/share/man/man4/bktr.4 b/share/man/man4/bktr.4
index 75454ad..d13562c 100644
--- a/share/man/man4/bktr.4
+++ b/share/man/man4/bktr.4
@@ -251,6 +251,7 @@ or the
 port.
 .El
 .Sh SEE ALSO
+.Xr cxm 4 ,
 .Xr meteor 4
 .Sh HISTORY
 The
diff --git a/share/man/man4/cxm.4 b/share/man/man4/cxm.4
new file mode 100644
index 0000000..1880003
--- /dev/null
+++ b/share/man/man4/cxm.4
@@ -0,0 +1,168 @@
+.\"
+.\" Copyright (c) 2009
+.\"	The DragonFly Project.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\" 3. Neither the name of The DragonFly Project nor the names of its
+.\"    contributors may be used to endorse or promote products derived
+.\"    from this software without specific, prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: cxm.4,v 1.1 2004/10/16 00:12:35 mavetju Exp $
+.\"
+.Dd February 18, 2009
+.Dt CXM 4
+.Os
+.Sh NAME
+.Nm cxm
+.Nd Conexant iTVC MPEG Coder
+.Sh SYNOPSIS
+To compile this driver into the kernel, place the following lines in
+your kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device iicbus"
+.Cd "device iic"
+.Cd "device iicbb"
+.Cd "device cxm"
+.Ed
+.Pp
+Alternatively, to load the driver as a module at boot time, place the
+following line in
+.Pa /boot/loader.conf :
+.Bd -literal -offset indent
+cxm_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the PCI
+.Em video
+capture on the Hauppauge PVR-150/250/350.
+The driver uses the
+.Xr bktr 4
+and
+.Xr meteor 4
+ioctls.
+.Sh HARDWARE
+The following cards are known to work:
+.Pp
+.Bl -bullet -compact
+.It
+Hauppage PVR-150
+.It
+Hauppage PVR-250
+.It
+Hauppage PVR-350
+.El
+.Pp
+The following tuners are known to work:
+.Pp
+.Bl -bullet -compact
+.It
+Philips FI1216 MK2
+.It
+Philips FI1236 MK2
+.It
+Philips FI1246 MK2
+.It
+Philips FM1216
+.It
+Philips FM1216ME MK3
+.It
+Philips FM1236
+.It
+Philips FM1246
+.It
+Philips FQ1216ME
+.It
+Philips FQ1216ME MK3
+.It
+Temic 4006 FH5
+.It
+Temic 4009 FR5
+.It
+Temic 4036 FY5
+.It
+Temic 4039 FR5
+.It
+Temic 4066 FY5
+.It
+LG Innotek TPI8PSB11D
+.It
+LG Innotek TPI8PSB01N
+.It
+LG Innotek TAPC-H701F
+.It
+LG Innotek TAPC-H001F
+.It
+LG Innotek TAPE-H001F
+.It
+Microtune 4049 FM5
+.It
+TCL 2002N-6A
+.It
+TCL M2523-5N-E
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /usr/src/tools/multimedia/cxm/extract_fw" -compact
+.It Pa /boot/modules/cxm.ko
+Kernel module
+.It Pa /boot/modules/cxm_iic.ko
+Kernel module
+.It Pa /dev/cxm0
+Device
+.It Pa /usr/src/tools/multimedia/cxm/extract_fw
+Firmware extraction tool
+.It Pa /usr/src/tools/multimedia/cxm/setchannel
+Channel switching tool
+.\".It Pa /usr/local/bin/pvr250-setsize
+.\"Set size of capture windows
+.El
+.Sh EXAMPLES
+To access the video card, use
+.Pa /dev/cxm0 .
+For example:
+.Pp
+.Dl "cat /dev/cxm0 > filename.mpg"
+.Sh SEE ALSO
+.Xr bktr 4 ,
+.Xr meteor 4
+.Sh HISTORY
+The
+.Nm
+driver first appeared in the -multimedia mailing-list in January 2004 and
+in the FreeBSD Ports collection in October 2004.
+.Sh AUTHORS
+.An -nosplit
+This driver was made by
+.An John Wehle Aq john@feith.com
+and this man page was made by
+.An Edwin Groothuis Aq edwin@FreeBSD.org .
+.Pp
+The
+.Nm
+driver was ported to
+.Dx 2.3
+by
+.An Sascha Wildner .
diff --git a/share/man/man4/man4.i386/meteor.4 b/share/man/man4/man4.i386/meteor.4
index 01f7f09..0da128c 100644
--- a/share/man/man4/man4.i386/meteor.4
+++ b/share/man/man4/man4.i386/meteor.4
@@ -909,6 +909,9 @@ will be set to:
 invalid meteor_counts structure pointer.
 .El
 .El
+.Sh SEE ALSO
+.Xr bktr 4 ,
+.Xr cxm 4
 .Sh AUTHORS
 .An Jim Lowe Aq james@miller.cs.uwm.edu ,
 .An Mark Tinguely Aq tinguely@plains.nodak.edu
diff --git a/sys/bus/iicbus/iicbb.c b/sys/bus/iicbus/iicbb.c
index 6f003f4..3fbb1e1 100644
--- a/sys/bus/iicbus/iicbb.c
+++ b/sys/bus/iicbus/iicbb.c
@@ -375,5 +375,6 @@ iicbb_read(device_t dev, char * buf, int len, int *read, int last, int delay)
 }
 
 DRIVER_MODULE(iicbb, bti2c, iicbb_driver, iicbb_devclass, 0, 0);
+DRIVER_MODULE(iicbb, cxm_iic, iicbb_driver, iicbb_devclass, 0, 0);
 DRIVER_MODULE(iicbb, lpbb, iicbb_driver, iicbb_devclass, 0, 0);
 DRIVER_MODULE(iicbb, viapm, iicbb_driver, iicbb_devclass, 0, 0);
diff --git a/sys/bus/pci/pcidevs b/sys/bus/pci/pcidevs
index b0ce168..ca954bb 100644
--- a/sys/bus/pci/pcidevs
+++ b/sys/bus/pci/pcidevs
@@ -1588,6 +1588,7 @@ product IBM 133PCIX	0x01a7	133 PCI-X Bridge
 product IBM MPIC2	0xffff	MPIC-II
 
 /* Conexant (iCompression, GlobeSpan) products */
+product ICOMPRESSION ITVC16	0x0016	iTVC16 MPEG2 codec
 product ICOMPRESSION ITVC15	0x0803	iTVC15 MPEG2 codec
 
 /* IDT products */
diff --git a/sys/bus/pci/pcidevs.h b/sys/bus/pci/pcidevs.h
index 6513171..f77d35f 100644
--- a/sys/bus/pci/pcidevs.h
+++ b/sys/bus/pci/pcidevs.h
@@ -1590,6 +1590,7 @@
 #define	PCI_PRODUCT_IBM_MPIC2	0xffff		/* MPIC-II */
 
 /* Conexant (iCompression, GlobeSpan) products */
+#define	PCI_PRODUCT_ICOMPRESSION_ITVC16	0x0016		/* iTVC16 MPEG2 codec */
 #define	PCI_PRODUCT_ICOMPRESSION_ITVC15	0x0803		/* iTVC15 MPEG2 codec */
 
 /* IDT products */
diff --git a/sys/bus/pci/pcidevs_data.h b/sys/bus/pci/pcidevs_data.h
index b4e8e42..16b6f8d 100644
--- a/sys/bus/pci/pcidevs_data.h
+++ b/sys/bus/pci/pcidevs_data.h
@@ -4590,6 +4590,12 @@ const struct pci_knowndev pci_knowndevs[] = {
 	    "MPIC-II",
 	},
 	{
+	    PCI_VENDOR_ICOMPRESSION, PCI_PRODUCT_ICOMPRESSION_ITVC16,
+	    0,
+	    "Conexant (iCompression)",
+	    "iTVC16 MPEG2 codec",
+	},
+	{
 	    PCI_VENDOR_ICOMPRESSION, PCI_PRODUCT_ICOMPRESSION_ITVC15,
 	    0,
 	    "Conexant (iCompression)",
diff --git a/sys/conf/files b/sys/conf/files
index 50e891f..f412a51 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1206,6 +1206,23 @@ dev/video/bktr/bktr_i2c.c	optional bktr pci smbus
 dev/video/bktr/bktr_os.c	optional bktr pci
 dev/video/bktr/bktr_tuner.c	optional bktr pci
 dev/video/bktr/msp34xx.c	optional bktr pci
+dev/video/cxm/cxm.c		optional cxm pci
+dev/video/cxm/cxm_cx2584x_fw.c	optional cxm pci
+dev/video/cxm/cxm_dec_fw.c	optional cxm pci
+dev/video/cxm/cxm_eeprom.c	optional cxm pci
+dev/video/cxm/cxm_enc_fw.c	optional cxm pci
+dev/video/cxm/cxm_i2c.c		optional cxm pci smbus
+dev/video/cxm/cxm_ir.c		optional cxm pci
+dev/video/cxm/cxm_msp34xxx.c	optional cxm pci
+dev/video/cxm/cxm_saa7115.c	optional cxm pci
+dev/video/cxm/cxm_tuner.c	optional cxm pci
+dev/video/cxm/cxm_wm8775.c	optional cxm pci
+contrib/dev/cxm/cx25840-audio.c optional cxm pci
+contrib/dev/cxm/cx25840-core.c	optional cxm pci
+contrib/dev/cxm/cx25840-firmware.c optional cxm pci
+contrib/dev/cxm/cx25840-vbi.c	optional cxm pci
+contrib/dev/cxm/fbsd-compat.c	optional cxm pci
+contrib/dev/cxm/tda9887.c	optional cxm pci
 dev/serial/cy/cy_pci.c		optional cy pci
 dev/netif/dc/if_dc.c		optional dc
 dev/netif/de/if_de.c		optional de
diff --git a/sys/conf/options b/sys/conf/options
index 1ae4cbd..09dfb31 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -540,6 +540,9 @@ METEOR_SYSTEM_DEFAULT	opt_meteor.h
 METEOR_DEALLOC_PAGES	opt_meteor.h
 METEOR_DEALLOC_ABOVE	opt_meteor.h
 
+# cxm opt_cxm.h
+CXM_PVR150		opt_cxm.h
+
 # Various mi ISA bus flags
 COMPAT_OLDISA		opt_compat_oldisa.h
 COM_ESP			opt_sio.h
diff --git a/sys/config/LINT b/sys/config/LINT
index aea6466..3da9d9d 100644
--- a/sys/config/LINT
+++ b/sys/config/LINT
@@ -1997,6 +1997,10 @@ device		meteor
 device		bktr
 options 	BKTR_NEW_MSP34XX_DRIVER
 
+# WinTV PVR-250/350 experimental driver
+device		cxm
+options		CXM_PVR150	# compile for PVR-150/500 instead
+
 #
 # PCCARD/PCMCIA
 #
diff --git a/sys/contrib/dev/cxm/audiochip.h b/sys/contrib/dev/cxm/audiochip.h
new file mode 100644
index 0000000..0807ea2
--- /dev/null
+++ b/sys/contrib/dev/cxm/audiochip.h
@@ -0,0 +1,55 @@
+/*
+ * $Id: audiochip.h,v 1.7 2005/10/16 12:13:58 mchehab Exp $
+ */
+
+#ifndef AUDIOCHIP_H
+#define AUDIOCHIP_H
+
+enum audiochip {
+	AUDIO_CHIP_NONE,
+	AUDIO_CHIP_UNKNOWN,
+	/* Provided by video chip */
+	AUDIO_CHIP_INTERNAL,
+	/* Provided by tvaudio.c */
+	AUDIO_CHIP_TDA8425,
+	AUDIO_CHIP_TEA6300,
+	AUDIO_CHIP_TEA6420,
+	AUDIO_CHIP_TDA9840,
+	AUDIO_CHIP_TDA985X,
+	AUDIO_CHIP_TDA9874,
+	AUDIO_CHIP_PIC16C54,
+	/* Provided by msp3400.c */
+	AUDIO_CHIP_MSP34XX
+};
+
+/* ---------------------------------------------------------------------- */
+
+/* v4l device was opened in Radio mode */
+#define AUDC_SET_RADIO        _IO('m',2)
+/* select from TV,radio,extern,MUTE */
+#define AUDC_SET_INPUT        _IOW('m',17,int)
+
+/* audio inputs */
+#define AUDIO_TUNER        0x00
+#define AUDIO_RADIO        0x01
+#define AUDIO_EXTERN       0x02
+#define AUDIO_INTERN       0x03
+#define AUDIO_OFF          0x04
+#define AUDIO_ON           0x05
+#define AUDIO_EXTERN_1     AUDIO_EXTERN
+#define AUDIO_EXTERN_2     0x06
+#define AUDIO_MUTE         0x80
+#define AUDIO_UNMUTE       0x81
+
+/* all the stuff below is obsolete and just here for reference.  I'll
+ * remove it once the driver is tested and works fine.
+ *
+ * Instead creating alot of tiny API's for all kinds of different
+ * chips, we'll just pass throuth the v4l ioctl structs (v4l2 not
+ * yet...).  It is a bit less flexible, but most/all used i2c chips
+ * make sense in v4l context only.  So I think that's acceptable...
+ */
+
+/* misc stuff to pass around config info to i2c chips */
+#define AUDC_CONFIG_PINNACLE  _IOW('m',32,int)
+#endif /* AUDIOCHIP_H */
diff --git a/sys/contrib/dev/cxm/cx25840-audio.c b/sys/contrib/dev/cxm/cx25840-audio.c
new file mode 100644
index 0000000..f9c1dda
--- /dev/null
+++ b/sys/contrib/dev/cxm/cx25840-audio.c
@@ -0,0 +1,370 @@
+/* cx25840 audio functions
+ *
+ * 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 "fbsd-compat.h"
+#include "audiochip.h"
+#include "v4l2-common.h"
+#include "cx25840.h"
+
+extern struct cx25840_state local_info[4];
+
+inline static int set_audclk_freq(device_t client,
+				 enum v4l2_audio_clock_freq freq)
+{
+	struct cx25840_state *state = i2c_get_clientdata(client);
+
+	/* assert soft reset */
+	cx25840_and_or(client, 0x810, ~0x1, 0x01);
+
+	/* common for all inputs and rates */
+	/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
+	cx25840_write(client, 0x127, 0x50);
+
+	switch (state->audio_input) {
+	case AUDIO_TUNER:
+		switch (freq) {
+		case V4L2_AUDCLK_32_KHZ:
+			/* VID_PLL and AUX_PLL */
+			cx25840_write4(client, 0x108, 0x0f040610);
+
+			/* AUX_PLL_FRAC */
+			cx25840_write4(client, 0x110, 0xee39bb01);
+
+			/* src3/4/6_ctl = 0x0801f77f */
+			cx25840_write4(client, 0x900, 0x7ff70108);
+			cx25840_write4(client, 0x904, 0x7ff70108);
+			cx25840_write4(client, 0x90c, 0x7ff70108);
+			break;
+
+		case V4L2_AUDCLK_441_KHZ:
+			/* VID_PLL and AUX_PLL */
+			cx25840_write4(client, 0x108, 0x0f040910);
+
+			/* AUX_PLL_FRAC */
+			cx25840_write4(client, 0x110, 0xd66bec00);
+
+			/* src3/4/6_ctl = 0x08016d59 */
+			cx25840_write4(client, 0x900, 0x596d0108);
+			cx25840_write4(client, 0x904, 0x596d0108);
+			cx25840_write4(client, 0x90c, 0x596d0108);
+			break;
+
+		case V4L2_AUDCLK_48_KHZ:
+			/* VID_PLL and AUX_PLL */
+			cx25840_write4(client, 0x108, 0x0f040a10);
+
+			/* AUX_PLL_FRAC */
+			cx25840_write4(client, 0x110, 0xe5d69800);
+
+			/* src3/4/6_ctl = 0x08014faa */
+			cx25840_write4(client, 0x900, 0xaa4f0108);
+			cx25840_write4(client, 0x904, 0xaa4f0108);
+			cx25840_write4(client, 0x90c, 0xaa4f0108);
+			break;
+		}
+		break;
+
+	case AUDIO_EXTERN_1:
+	case AUDIO_EXTERN_2:
+	case AUDIO_INTERN:
+	case AUDIO_RADIO:
+		switch (freq) {
+		case V4L2_AUDCLK_32_KHZ:
+			/* VID_PLL and AUX_PLL */
+			cx25840_write4(client, 0x108, 0x0f04081e);
+
+			/* AUX_PLL_FRAC */
+			cx25840_write4(client, 0x110, 0x69082a01);
+
+			/* src1_ctl = 0x08010000 */
+			cx25840_write4(client, 0x8f8, 0x00000108);
+
+			/* src3/4/6_ctl = 0x08020000 */
+			cx25840_write4(client, 0x900, 0x00000208);
+			cx25840_write4(client, 0x904, 0x00000208);
+			cx25840_write4(client, 0x90c, 0x00000208);
+
+			/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
+			cx25840_write(client, 0x127, 0x54);
+			break;
+
+		case V4L2_AUDCLK_441_KHZ:
+			/* VID_PLL and AUX_PLL */
+			cx25840_write4(client, 0x108, 0x0f040918);
+
+			/* AUX_PLL_FRAC */
+			cx25840_write4(client, 0x110, 0xd66bec00);
+
+			/* src1_ctl = 0x08010000 */
+			cx25840_write4(client, 0x8f8, 0xcd600108);
+
+			/* src3/4/6_ctl = 0x08020000 */
+			cx25840_write4(client, 0x900, 0x85730108);
+			cx25840_write4(client, 0x904, 0x85730108);
+			cx25840_write4(client, 0x90c, 0x85730108);
+			break;
+
+		case V4L2_AUDCLK_48_KHZ:
+			/* VID_PLL and AUX_PLL */
+			cx25840_write4(client, 0x108, 0x0f040a18);
+
+			/* AUX_PLL_FRAC */
+			cx25840_write4(client, 0x110, 0xe5d69800);
+
+			/* src1_ctl = 0x08010000 */
+			cx25840_write4(client, 0x8f8, 0x00800108);
+
+			/* src3/4/6_ctl = 0x08020000 */
+			cx25840_write4(client, 0x900, 0x55550108);
+			cx25840_write4(client, 0x904, 0x55550108);
+			cx25840_write4(client, 0x90c, 0x55550108);
+			break;
+		}
+		break;
+	}
+
+	/* deassert soft reset */
+	cx25840_and_or(client, 0x810, ~0x1, 0x00);
+
+	state->audclk_freq = freq;
+
+	return 0;
+}
+
+static int set_input(device_t client, int audio_input)
+{
+	struct cx25840_state *state = i2c_get_clientdata(client);
+
+	cx25840_dbg("set audio input (%d)\n", audio_input);
+
+	/* stop microcontroller */
+	cx25840_and_or(client, 0x803, ~0x10, 0);
+
+	/* Mute everything to prevent the PFFT! */
+	cx25840_write(client, 0x8d3, 0x1f);
+
+	switch (audio_input) {
+	case AUDIO_TUNER:
+		/* Set Path1 to Analog Demod Main Channel */
+		cx25840_write4(client, 0x8d0, 0x7038061f);
+
+
+		/* When the microcontroller detects the
+		 * audio format, it will unmute the lines */
+		cx25840_and_or(client, 0x803, ~0x10, 0x10);
+		break;
+
+	case AUDIO_EXTERN_1:
+	case AUDIO_EXTERN_2:
+	case AUDIO_INTERN:
+	case AUDIO_RADIO:
+		/* Set Path1 to Serial Audio Input */
+		cx25840_write4(client, 0x8d0, 0x12100101);
+
+		/* The microcontroller should not be started for the
+		 * non-tuner inputs: autodetection is specific for
+		 * TV audio. */
+		break;
+
+	default:
+		cx25840_dbg("Invalid audio input selection %d\n", audio_input);
+		return -EINVAL;
+	}
+
+	state->audio_input = audio_input;
+
+	return set_audclk_freq(client, state->audclk_freq);
+}
+
+inline static int get_volume(device_t client)
+{
+	/* Volume runs +18dB to -96dB in 1/2dB steps
+	 * change to fit the msp3400 -114dB to +12dB range */
+
+	/* check PATH1_VOLUME */
+	int vol = 228 - cx25840_read(client, 0x8d4);
+	vol = (vol / 2) + 23;
+	return vol << 9;
+}
+
+inline static void set_volume(device_t client, int volume)
+{
+	/* First convert the volume to msp3400 values (0-127) */
+	int vol = volume >> 9;
+	/* now scale it up to cx25840 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 */
+	cx25840_write(client, 0x8d4, 228 - (vol * 2));
+}
+
+inline static int get_bass(device_t client)
+{
+	/* bass is 49 steps +12dB to -12dB */
+
+	/* check PATH1_EQ_BASS_VOL */
+	int bass = cx25840_read(client, 0x8d9) & 0x3f;
+	bass = (((48 - bass) * 0xffff) + 47) / 48;
+	return bass;
+}
+
+inline static void set_bass(device_t client, int bass)
+{
+	/* PATH1_EQ_BASS_VOL */
+	cx25840_and_or(client, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
+}
+
+inline static int get_treble(device_t client)
+{
+	/* treble is 49 steps +12dB to -12dB */
+
+	/* check PATH1_EQ_TREBLE_VOL */
+	int treble = cx25840_read(client, 0x8db) & 0x3f;
+	treble = (((48 - treble) * 0xffff) + 47) / 48;
+	return treble;
+}
+
+inline static void set_treble(device_t client, int treble)
+{
+	/* PATH1_EQ_TREBLE_VOL */
+	cx25840_and_or(client, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
+}
+
+inline static int get_balance(device_t client)
+{
+	/* balance is 7 bit, 0 to -96dB */
+
+	/* check PATH1_BAL_LEVEL */
+	int balance = cx25840_read(client, 0x8d5) & 0x7f;
+	/* check PATH1_BAL_LEFT */
+	if ((cx25840_read(client, 0x8d5) & 0x80) == 0)
+		balance = 0x80 - balance;
+	else
+		balance = 0x80 + balance;
+	return balance << 8;
+}
+
+inline static void set_balance(device_t client, int balance)
+{
+	int bal = balance >> 8;
+	if (bal > 0x80) {
+		/* PATH1_BAL_LEFT */
+		cx25840_and_or(client, 0x8d5, 0x7f, 0x80);
+		/* PATH1_BAL_LEVEL */
+		cx25840_and_or(client, 0x8d5, ~0x7f, bal & 0x7f);
+	} else {
+		/* PATH1_BAL_LEFT */
+		cx25840_and_or(client, 0x8d5, 0x7f, 0x00);
+		/* PATH1_BAL_LEVEL */
+		cx25840_and_or(client, 0x8d5, ~0x7f, 0x80 - bal);
+	}
+}
+
+inline static int get_mute(device_t client)
+{
+	/* check SRC1_MUTE_EN */
+	return cx25840_read(client, 0x8d3) & 0x2 ? 1 : 0;
+}
+
+void set_mute(device_t client, int mute)
+{
+	struct cx25840_state *state = i2c_get_clientdata(client);
+
+       i2c_cl[device_get_unit(client)] = 0x88;
+
+
+	if (1 || state->audio_input == AUDIO_TUNER) {
+		/* 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 */
+			cx25840_and_or(client, 0x803, ~0x10, 0x00);
+			cx25840_write(client, 0x8d3, 0x1f);
+		} else {
+			/* enable microcontroller */
+			cx25840_and_or(client, 0x803, ~0x10, 0x10);
+		}
+	} else {
+		/* SRC1_MUTE_EN */
+		cx25840_and_or(client, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
+	}
+}
+
+int cx25840_audio(device_t client, unsigned int cmd, void *arg)
+{
+	struct v4l2_control *ctrl = arg;
+
+	switch (cmd) {
+	case AUDC_SET_INPUT:
+		return set_input(client, *(int *)arg);
+	case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+		return set_audclk_freq(client, *(enum v4l2_audio_clock_freq *)arg);
+	case VIDIOC_G_CTRL:
+		switch (ctrl->id) {
+		case V4L2_CID_AUDIO_VOLUME:
+			ctrl->value = get_volume(client);
+			break;
+		case V4L2_CID_AUDIO_BASS:
+			ctrl->value = get_bass(client);
+			break;
+		case V4L2_CID_AUDIO_TREBLE:
+			ctrl->value = get_treble(client);
+			break;
+		case V4L2_CID_AUDIO_BALANCE:
+			ctrl->value = get_balance(client);
+			break;
+		case V4L2_CID_AUDIO_MUTE:
+			ctrl->value = get_mute(client);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case VIDIOC_S_CTRL:
+		switch (ctrl->id) {
+		case V4L2_CID_AUDIO_VOLUME:
+			set_volume(client, ctrl->value);
+			break;
+		case V4L2_CID_AUDIO_BASS:
+			set_bass(client, ctrl->value);
+			break;
+		case V4L2_CID_AUDIO_TREBLE:
+			set_treble(client, ctrl->value);
+			break;
+		case V4L2_CID_AUDIO_BALANCE:
+			set_balance(client, ctrl->value);
+			break;
+		case V4L2_CID_AUDIO_MUTE:
+			set_mute(client, ctrl->value);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
diff --git a/sys/contrib/dev/cxm/cx25840-core.c b/sys/contrib/dev/cxm/cx25840-core.c
new file mode 100644
index 0000000..8822b3b
--- /dev/null
+++ b/sys/contrib/dev/cxm/cx25840-core.c
@@ -0,0 +1,920 @@
+/* cx25840 - Conexant CX25840 audio/video decoder driver
+ *
+ * Copyright (C) 2004 Ulf Eklund
+ *
+ * Based on the saa7115 driver and on the first verison of Chris Kennedy's
+ * cx25840 driver.
+ *
+ * Changes by Tyler Trafford <tatrafford@comcast.net>
+ *    - cleanup/rewrite for V4L2 API (2005)
+ *
+ * VBI support by 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 "fbsd-compat.h"
+#include "audiochip.h"
+#include "v4l2-common.h"
+#include "cx25840.h"
+
+/* static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; */
+
+int cx25840_debug = 1;
+
+
+int cx25840_write(device_t client, u16 addr, u8 value)
+{
+	u8 buffer[3];
+	buffer[0] = addr >> 8;
+	buffer[1] = addr & 0xff;
+	buffer[2] = value;
+	return i2c_master_send(client, buffer, 3);
+}
+
+int cx25840_write4(device_t client, u16 addr, u32 value)
+{
+	u8 buffer[6];
+	buffer[0] = addr >> 8;
+	buffer[1] = addr & 0xff;
+	buffer[2] = value >> 24;
+	buffer[3] = (value >> 16) & 0xff;
+	buffer[4] = (value >> 8) & 0xff;
+	buffer[5] = value & 0xff;
+	return i2c_master_send(client, buffer, 6);
+}
+
+u8 cx25840_read(device_t  client, u16 addr)
+{
+	u8 buffer[2];
+	buffer[0] = addr >> 8;
+	buffer[1] = addr & 0xff;
+
+	if (i2c_master_send(client, buffer, 2) < 2)
+        {
+        kprintf("_read send failed\n");
+		return 0;
+        }
+
+	if (i2c_master_recv(client, buffer, 1) < 1)
+        {
+        kprintf("_read recv failed\n");
+		return 0;
+        }
+
+	return buffer[0];
+}
+
+u32 cx25840_read4(device_t  client, u16 addr)
+{
+	u8 buffer[4];
+	buffer[0] = addr >> 8;
+	buffer[1] = addr & 0xff;
+
+	if (i2c_master_send(client, buffer, 2) < 2)
+		return 0;
+
+	if (i2c_master_recv(client, buffer, 4) < 4)
+		return 0;
+
+	return (buffer[0] << 24) | (buffer[1] << 16) |
+	    (buffer[2] << 8) | buffer[3];
+}
+
+int cx25840_and_or(device_t client, u16 addr, u8 and_mask, u8 or_value)
+{
+	return cx25840_write(client, addr,
+			     (cx25840_read(client, addr) & and_mask) | or_value);
+}
+
+
+static int set_input(device_t , enum cx25840_input);
+static void input_change(device_t );
+static void log_status(device_t client);
+
+
+static inline void init_dll1(device_t client)
+{
+	/* This is the Hauppauge sequence used to
+	 * initialize the Delay Lock Loop 1 (ADC DLL). */
+	cx25840_write(client, 0x159, 0x23);
+	cx25840_write(client, 0x15a, 0x87);
+	cx25840_write(client, 0x15b, 0x06);
+	cx25840_write(client, 0x159, 0xe1);
+	cx25840_write(client, 0x15a, 0x86);
+	cx25840_write(client, 0x159, 0xe0);
+	cx25840_write(client, 0x159, 0xe1);
+	cx25840_write(client, 0x15b, 0x10);
+}
+
+static inline void init_dll2(device_t client)
+{
+	/* This is the Hauppauge sequence used to
+	 * initialize the Delay Lock Loop 2 (ADC DLL). */
+	cx25840_write(client, 0x15d, 0xe3);
+	cx25840_write(client, 0x15e, 0x86);
+	cx25840_write(client, 0x15f, 0x06);
+	cx25840_write(client, 0x15d, 0xe1);
+	cx25840_write(client, 0x15d, 0xe0);
+	cx25840_write(client, 0x15d, 0xe1);
+}
+
+struct cx25840_state local_info[4];
+
+
+void cx25840_initialize(device_t client, int loadfw)
+{
+	struct cx25840_state *state = i2c_get_clientdata(client);
+
+	i2c_cl[device_get_unit(client)] = 0x88;
+	state->input = CX25840_TUNER;
+    state->audclk_freq = V4L2_AUDCLK_48_KHZ;
+    state->audio_input = AUDIO_TUNER;
+    state->cardtype = CARDTYPE_PVR150;
+
+	/* datasheet startup in numbered steps, refer to page 3-77 */
+	/* 2. */
+	cx25840_and_or(client, 0x803, ~0x10, 0x00);
+	/* The default of this register should be 4, but I get 0 instead.
+	 * Set this register to 4 manually. */
+	cx25840_write(client, 0x000, 0x04);
+	/* 3. */
+	init_dll1(client);
+	init_dll2(client);
+	cx25840_write(client, 0x136, 0x0a);
+	/* 4. */
+	cx25840_write(client, 0x13c, 0x01);
+	cx25840_write(client, 0x13c, 0x00);
+	/* 5. */
+	if (loadfw)
+		cx25840_loadfw_hp(client);
+	/* 6. */
+	cx25840_write(client, 0x115, 0x8c);
+	cx25840_write(client, 0x116, 0x07);
+	cx25840_write(client, 0x118, 0x02);
+	/* 7. */
+	cx25840_write(client, 0x4a5, 0x80);
+	cx25840_write(client, 0x4a5, 0x00);
+	cx25840_write(client, 0x402, 0x00);
+	/* 8. */
+	cx25840_write(client, 0x401, 0x18);
+	cx25840_write(client, 0x4a2, 0x10);
+	cx25840_write(client, 0x402, 0x04);
+	/* 10. */
+	cx25840_write(client, 0x8d3, 0x1f);
+	cx25840_write(client, 0x8e3, 0x03);
+
+	cx25840_vbi_setup(client);
+
+	/* trial and error says these are needed to get audio */
+	cx25840_write(client, 0x914, 0xa0);
+	cx25840_write(client, 0x918, 0xa0);
+	cx25840_write(client, 0x919, 0x01);
+
+	/* stereo prefered */
+	cx25840_write(client, 0x809, 0x04);
+
+	/* AC97 shift */
+	cx25840_write(client, 0x8cf, 0x0f);
+
+
+	/* set_v4lstd(client,V4L2_STD_PAL); */
+
+	/* (re)set video input */
+	set_input(client, state->input);
+	/* (re)set audio input  */
+	cx25840_audio(client, AUDC_SET_INPUT, &state->audio_input);
+
+	/* start microcontroller */
+	cx25840_and_or(client, 0x803, ~0x10, 0x10);
+}
+
+static void input_change(device_t client)
+{
+	struct cx25840_state *state = i2c_get_clientdata(client);
+	v4l2_std_id std = cx25840_get_v4lstd(client);
+
+	/* Note: perhaps V4L2_STD_PAL_M should be handled as V4L2_STD_NTSC
+	   instead of V4L2_STD_PAL. Someone needs to test this. */
+	if (std & V4L2_STD_PAL ) {
+		/* Follow tuner change procedure for PAL */
+		cx25840_write(client, 0x808, 0x4f); /* 0xff */
+		/* cx25840_write(client, 0x80b, 0x10);  */
+	} else if (std & V4L2_STD_SECAM) {
+		/* Select autodetect for SECAM */
+		cx25840_write(client, 0x808, 0xff);
+		cx25840_write(client, 0x80b, 0x10);
+	} else if (std & V4L2_STD_NTSC) {
+		/* NTSC */
+		if (state->cardtype == CARDTYPE_PVR150_WORKAROUND) {
+			/* Certain Hauppauge PVR150 models have a hardware bug
+			   that causes audio to drop out. For these models the
+			   audio standard must be set explicitly.
+			   To be precise: it affects cards with tuner models
+			   85, 99 and 112 (model numbers from tveeprom). */
+			if (std == V4L2_STD_NTSC_M_JP) {
+				/* Japan uses EIAJ audio standard */
+				cx25840_write(client, 0x808, 0x2f);
+			} else {
+				/* Others use the BTSC audio standard */
+				cx25840_write(client, 0x808, 0x1f); /* 0x1f */
+			}
+			/* South Korea uses the A2-M (aka Zweiton M) audio
+			   standard, and should set 0x808 to 0x3f, but I don't
+			   know how to detect this. */
+		} else if (std == V4L2_STD_NTSC_M_JP) {
+			/* Japan uses EIAJ audio standard */
+			cx25840_write(client, 0x808, 0xf7);
+		} else {
+			/* Others use the BTSC audio standard */
+			cx25840_write(client, 0x808, 0xf6);
+		}
+		/* South Korea uses the A2-M (aka Zweiton M) audio standard,
+		   and should set 0x808 to 0xf8, but I don't know how to
+		   detect this. */
+		cx25840_write(client, 0x80b, 0x00); /* 0x00 */
+	}
+
+	if (cx25840_read(client, 0x803) & 0x10) {
+		/* restart audio decoder microcontroller */
+		cx25840_and_or(client, 0x803, ~0x10, 0x00);
+		cx25840_and_or(client, 0x803, ~0x10, 0x10);
+	}
+}
+
+static int set_input(device_t client, enum cx25840_input input)
+{
+	struct cx25840_state *state = i2c_get_clientdata(client);
+	u8 reg;
+
+	cx25840_dbg("decoder set input (%d)\n", input);
+
+	switch (input) {
+	case CX25840_TUNER:
+		cx25840_dbg("now setting Tuner input\n");
+
+		reg = 0xf6;
+
+		reg &= ~0xc0; reg |= 0x40;
+
+		cx25840_write(client, 0x103, reg);
+
+		/* INPUT_MODE=0 */
+		cx25840_and_or(client, 0x401, ~0x6, 0x00);
+
+		cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
+
+		if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
+			cx25840_and_or(client, 0x102, ~0x4, 4);
+		else
+			cx25840_and_or(client, 0x102, ~0x4, 0);
+		break;
+
+	case CX25840_COMPOSITE0:
+	case CX25840_COMPOSITE1:
+		cx25840_dbg("now setting Composite input\n");
+
+		/* Video Input Control */
+		if (state->cardtype == CARDTYPE_PG600) {
+			cx25840_write(client, 0x103, 0x00);
+		} else {
+			cx25840_write(client, 0x103, 0x02);
+		}
+
+		/* INPUT_MODE=0 */
+		cx25840_and_or(client, 0x401, ~0x6, 0x00);
+		break;
+
+	case CX25840_SVIDEO0:
+	case CX25840_SVIDEO1:
+		cx25840_dbg("now setting S-Video input\n");
+
+		/* CH_SEL_ADC2=0 */
+		cx25840_and_or(client, 0x102, ~0x2, 0x00);
+
+		/* Video Input Control */
+		if (state->cardtype == CARDTYPE_PG600) {
+			cx25840_write(client, 0x103, 0x02);
+		} else {
+			cx25840_write(client, 0x103, 0x10);
+		}
+
+		/* INPUT_MODE=1 */
+		cx25840_and_or(client, 0x401, ~0x6, 0x02);
+		break;
+
+	default:
+		cx25840_err("%d is not a valid input!\n", input);
+		return -EINVAL;
+	}
+
+	state->input = input;
+	cx25840_audio(client, AUDC_SET_INPUT, &state->audio_input);
+
+	input_change(client);
+	return 0;
+}
+
+static int set_v4lstd(device_t client, v4l2_std_id std)
+{
+	u8 fmt;
+
+	switch (std) {
+	/* zero is autodetect */
+	case 0: fmt = 0x0; break;
+	/* default ntsc to ntsc-m */
+	case V4L2_STD_NTSC:
+	case V4L2_STD_NTSC_M: fmt = 0x1; break;
+	case V4L2_STD_NTSC_M_JP: fmt = 0x2; break;
+	case V4L2_STD_NTSC_443: fmt = 0x3; break;
+	case V4L2_STD_PAL: fmt = 0x4; break;
+	case V4L2_STD_PAL_M: fmt = 0x5; break;
+	case V4L2_STD_PAL_N: fmt = 0x6; break;
+	case V4L2_STD_PAL_Nc: fmt = 0x7; break;
+	case V4L2_STD_PAL_60: fmt = 0x8; break;
+	case V4L2_STD_SECAM: fmt = 0xc; break;
+	default:
+		return -ERANGE;
+	}
+
+	cx25840_and_or(client, 0x400, ~0xf, fmt);
+	cx25840_vbi_setup(client);
+	return 0;
+}
+
+v4l2_std_id cx25840_get_v4lstd(device_t  client)
+{
+	/* check VID_FMT_SEL first */
+	u8 fmt = cx25840_read(client, 0x400) & 0xf;
+
+	if (!fmt) {
+		/* check AFD_FMT_STAT if set to autodetect */
+		fmt = cx25840_read(client, 0x40d) & 0xf;
+	}
+
+	switch (fmt) {
+	case 0x1: return V4L2_STD_NTSC_M;
+	case 0x2: return V4L2_STD_NTSC_M_JP;
+	case 0x3: return V4L2_STD_NTSC_443;
+	case 0x4: return V4L2_STD_PAL;
+	case 0x5: return V4L2_STD_PAL_M;
+	case 0x6: return V4L2_STD_PAL_N;
+	case 0x7: return V4L2_STD_PAL_Nc;
+	case 0x8: return V4L2_STD_PAL_60;
+	case 0xc: return V4L2_STD_SECAM;
+	default: return V4L2_STD_UNKNOWN;
+	}
+}
+
+static int set_v4lctrl(device_t client, struct v4l2_control *ctrl)
+{
+	struct cx25840_state *state = i2c_get_clientdata(client);
+
+	switch (ctrl->id) {
+	case CX25840_CID_CARDTYPE:
+		switch (ctrl->value) {
+		case CARDTYPE_PVR150:
+		case CARDTYPE_PVR150_WORKAROUND:
+		case CARDTYPE_PG600:
+			state->cardtype = ctrl->value;
+			break;
+		default:
+			return -ERANGE;
+		}
+
+		set_input(client, state->input);
+		break;
+
+	case V4L2_CID_BRIGHTNESS:
+		if (ctrl->value < 0 || ctrl->value > 255) {
+			cx25840_err("invalid brightness setting %d\n",
+				    ctrl->value);
+			return -ERANGE;
+		}
+
+		cx25840_write(client, 0x414, ctrl->value - 128);
+		break;
+
+	case V4L2_CID_CONTRAST:
+		if (ctrl->value < 0 || ctrl->value > 127) {
+			cx25840_err("invalid contrast setting %d\n",
+				    ctrl->value);
+			return -ERANGE;
+		}
+
+		cx25840_write(client, 0x415, ctrl->value << 1);
+		break;
+
+	case V4L2_CID_SATURATION:
+		if (ctrl->value < 0 || ctrl->value > 127) {
+			cx25840_err("invalid saturation setting %d\n",
+				    ctrl->value);
+			return -ERANGE;
+		}
+
+		cx25840_write(client, 0x420, ctrl->value << 1);
+		cx25840_write(client, 0x421, ctrl->value << 1);
+		break;
+
+	case V4L2_CID_HUE:
+		if (ctrl->value < -127 || ctrl->value > 127) {
+			cx25840_err("invalid hue setting %d\n", ctrl->value);
+			return -ERANGE;
+		}
+
+		cx25840_write(client, 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 cx25840_audio(client, VIDIOC_S_CTRL, ctrl);
+	}
+
+	return 0;
+}
+
+static int get_v4lctrl(device_t client, struct v4l2_control *ctrl)
+{
+	struct cx25840_state *state = i2c_get_clientdata(client);
+
+	switch (ctrl->id) {
+	case CX25840_CID_CARDTYPE:
+		ctrl->value = state->cardtype;
+		break;
+	case V4L2_CID_BRIGHTNESS:
+		ctrl->value = (s8)cx25840_read(client, 0x414) + 128;
+		break;
+	case V4L2_CID_CONTRAST:
+		ctrl->value = cx25840_read(client, 0x415) >> 1;
+		break;
+	case V4L2_CID_SATURATION:
+		ctrl->value = cx25840_read(client, 0x420) >> 1;
+		break;
+	case V4L2_CID_HUE:
+		ctrl->value = (s8)cx25840_read(client, 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 cx25840_audio(client, VIDIOC_G_CTRL, ctrl);
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int get_v4lfmt(device_t client, struct v4l2_format *fmt)
+{
+	switch (fmt->type) {
+	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+		return cx25840_vbi(client, VIDIOC_G_FMT, fmt);
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int set_v4lfmt(device_t client, struct v4l2_format *fmt)
+{
+	struct v4l2_pix_format *pix;
+	int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
+	int is_pal = !(cx25840_get_v4lstd(client) & V4L2_STD_NTSC);
+
+	switch (fmt->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		pix = &(fmt->fmt.pix);
+
+		Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4;
+		Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4;
+
+		Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4;
+		Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4;
+
+		Vlines = pix->height + (is_pal ? 4 : 7);
+
+		if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) ||
+		    (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
+			cx25840_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;
+
+		cx25840_dbg("decoder set size %dx%d -> scale  %ux%u\n",
+			    pix->width, pix->height, HSC, VSC);
+
+		/* HSCALE=HSC */
+		cx25840_write(client, 0x418, HSC & 0xff);
+		cx25840_write(client, 0x419, (HSC >> 8) & 0xff);
+		cx25840_write(client, 0x41a, HSC >> 16);
+		/* VSCALE=VSC */
+		cx25840_write(client, 0x41c, VSC & 0xff);
+		cx25840_write(client, 0x41d, VSC >> 8);
+		/* VS_INTRLACE=1 VFILT=filter */
+		cx25840_write(client, 0x41e, 0x8 | filter);
+		break;
+
+	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+		return cx25840_vbi(client, VIDIOC_S_FMT, fmt);
+
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		return cx25840_vbi(client, VIDIOC_S_FMT, fmt);
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+int cx25840_command(device_t client, unsigned int cmd,
+			   void *arg)
+{
+	struct cx25840_state *state = i2c_get_clientdata(client);
+	struct v4l2_tuner *vt = arg;
+	int result = 0;
+
+       /* i2c_cl = 0x88; */
+       i2c_cl[device_get_unit(client)] = 0x88;
+
+
+	switch (cmd) {
+	case 0:
+		break;
+
+	/* ioctls to allow direct access to the
+	 * cx25840 registers for testing */
+	case VIDIOC_INT_G_REGISTER:
+	{
+		struct v4l2_register *reg = arg;
+
+        kprintf("in read reg!\n");
+		reg->val = cx25840_read(client, reg->reg & 0x0fff);
+		break;
+	}
+
+	case VIDIOC_INT_S_REGISTER:
+	{
+		struct v4l2_register *reg = arg;
+
+		if (reg->i2c_id != I2C_DRIVERID_CX25840)
+			return -EINVAL;
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
+		break;
+	}
+
+	case VIDIOC_INT_DECODE_VBI_LINE:
+		return cx25840_vbi(client, cmd, arg);
+
+	case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+	case AUDC_SET_INPUT:
+		result = cx25840_audio(client, cmd, arg);
+		break;
+
+	case VIDIOC_STREAMON:
+		cx25840_write(client, 0x115, 0x8c);
+		cx25840_write(client, 0x116, 0x07);
+		break;
+
+	case VIDIOC_STREAMOFF:
+		cx25840_write(client, 0x115, 0x00);
+		cx25840_write(client, 0x116, 0x00);
+		break;
+
+	case VIDIOC_LOG_STATUS:
+		log_status(client);
+		break;
+
+	case VIDIOC_G_CTRL:
+		result = get_v4lctrl(client, (struct v4l2_control *)arg);
+		break;
+
+	case VIDIOC_S_CTRL:
+		result = set_v4lctrl(client, (struct v4l2_control *)arg);
+		break;
+
+	case VIDIOC_G_STD:
+		*(v4l2_std_id *)arg = cx25840_get_v4lstd(client);
+		break;
+
+	case VIDIOC_S_STD:
+		result = set_v4lstd(client, *(v4l2_std_id *)arg);
+		input_change(client);
+		break;
+
+	case VIDIOC_G_INPUT:
+		*(int *)arg = state->input;
+		break;
+
+	case VIDIOC_S_INPUT:
+		result = set_input(client, *(int *)arg);
+		break;
+
+	case VIDIOC_S_FREQUENCY:
+		input_change(client);
+		break;
+
+	case VIDIOC_G_TUNER:
+	{
+		u8 mode = cx25840_read(client, 0x804);
+		u8 pref = cx25840_read(client, 0x809) & 0xf;
+		u8 vpres = cx25840_read(client, 0x80a) & 0x10;
+		int val = 0;
+
+		vt->capability |=
+		    V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+		    V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+
+		vt->signal = vpres ? 0xffff : 0x0;
+
+		/* 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;
+
+		switch (pref) {
+		case 0:
+			vt->audmode = V4L2_TUNER_MODE_MONO;
+			break;
+		case 1:
+		case 2:
+			vt->audmode = V4L2_TUNER_MODE_LANG2;
+			break;
+		case 4:
+		default:
+			vt->audmode = V4L2_TUNER_MODE_STEREO;
+		}
+		break;
+	}
+
+	case VIDIOC_S_TUNER:
+		switch (vt->audmode) {
+		case V4L2_TUNER_MODE_MONO:
+		case V4L2_TUNER_MODE_LANG1:
+			/* Force PREF_MODE to MONO */
+			cx25840_and_or(client, 0x809, ~0xf, 0x00);
+			break;
+		case V4L2_TUNER_MODE_STEREO:
+			/* Force PREF_MODE to STEREO */
+			cx25840_and_or(client, 0x809, ~0xf, 0x04);
+			break;
+		case V4L2_TUNER_MODE_LANG2:
+			/* Force PREF_MODE to LANG2 */
+			cx25840_and_or(client, 0x809, ~0xf, 0x01);
+			break;
+		}
+		break;
+
+	case VIDIOC_G_FMT:
+		result = get_v4lfmt(client, (struct v4l2_format *)arg);
+		break;
+
+	case VIDIOC_S_FMT:
+		result = set_v4lfmt(client, (struct v4l2_format *)arg);
+		break;
+
+	case VIDIOC_INT_RESET:
+		cx25840_initialize(client, 0);
+		break;
+
+	case VIDIOC_INT_G_CHIP_IDENT:
+		*(enum v4l2_chip_ident *)arg =
+			V4L2_IDENT_CX25840 + ((cx25840_read(client, 0x100) >> 4) & 0xf);
+		break;
+
+	default:
+		cx25840_err("invalid ioctl %x\n", cmd);
+		return -EINVAL;
+	}
+
+	return result;
+}
+
+static void log_status(device_t client)
+{
+	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 cx25840_state *state = i2c_get_clientdata(client);
+	u8 microctrl_vidfmt = cx25840_read(client, 0x80a);
+	u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf;
+	u8 gen_stat1 = cx25840_read(client, 0x40d);
+	u8 download_ctl = cx25840_read(client, 0x803);
+	u8 mod_det_stat0 = cx25840_read(client, 0x804);
+	u8 mod_det_stat1 = cx25840_read(client, 0x805);
+	u8 audio_config = cx25840_read(client, 0x808);
+	u8 pref_mode = cx25840_read(client, 0x809);
+	u8 afc0 = cx25840_read(client, 0x80b);
+	u8 mute_ctl = cx25840_read(client, 0x8d3);
+	char *p;
+
+	cx25840_info("Video signal:              %spresent\n",
+		    (microctrl_vidfmt & 0x10) ? "" : "not ");
+	cx25840_info("Detected format:           %s\n",
+		    fmt_strs[gen_stat1 & 0xf]);
+
+	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";
+	}
+	cx25840_info("Detected audio mode:       %s\n", p);
+
+	switch (mod_det_stat1) {
+	case 0x00: p = "not defined"; 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 0x0e: p = "IF FM Radio"; break;
+	case 0x0f: p = "BTSC"; break;
+	case 0x10: p = "high-deviation FM"; break;
+	case 0x11: p = "very high-deviation FM"; break;
+	case 0xfd: p = "unknown audio standard"; break;
+	case 0xfe: p = "forced audio standard"; break;
+	case 0xff: p = "no detected audio standard"; break;
+	default: p = "not defined";
+	}
+	cx25840_info("Detected audio standard:   %s\n", p);
+	cx25840_info("Audio muted:               %s\n",
+		    (mute_ctl & 0x2) ? "yes" : "no");
+	cx25840_info("Audio microcontroller:     %s\n",
+		    (download_ctl & 0x10) ? "running" : "stopped");
+
+	switch (audio_config >> 4) {
+	case 0x00: p = "undefined"; break;
+	case 0x01: p = "BTSC"; break;
+	case 0x02: p = "EIAJ"; break;
+	case 0x03: p = "A2-M"; break;
+	case 0x04: p = "A2-BG"; break;
+	case 0x05: p = "A2-DK1"; break;
+	case 0x06: p = "A2-DK2"; break;
+	case 0x07: p = "A2-DK3"; break;
+	case 0x08: p = "A1 (6.0 MHz FM Mono)"; break;
+	case 0x09: p = "AM-L"; break;
+	case 0x0a: p = "NICAM-BG"; break;
+	case 0x0b: p = "NICAM-DK"; break;
+	case 0x0c: p = "NICAM-I"; break;
+	case 0x0d: p = "NICAM-L"; break;
+	case 0x0e: p = "FM radio"; break;
+	case 0x0f: p = "automatic detection"; break;
+	default: p = "undefined";
+	}
+	cx25840_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";
+		}
+		cx25840_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";
+		}
+		cx25840_info("Configured audio system:   %s\n", p);
+	}
+
+	cx25840_info("Specified standard:        %s\n",
+		    vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection");
+
+	switch (state->input) {
+	case CX25840_COMPOSITE0: p = "Composite 0"; break;
+	case CX25840_COMPOSITE1: p = "Composite 1"; break;
+	case CX25840_SVIDEO0: p = "S-Video 0"; break;
+	case CX25840_SVIDEO1: p = "S-Video 1"; break;
+	case CX25840_TUNER: p = "Tuner"; break;
+	}
+	cx25840_info("Specified input:           %s\n", p);
+	cx25840_info("Specified audio input:     %s\n",
+		    state->audio_input == 0 ? "Tuner" : "External");
+
+	switch (state->audclk_freq) {
+	case V4L2_AUDCLK_441_KHZ: p = "44.1 kHz"; break;
+	case V4L2_AUDCLK_48_KHZ: p = "48 kHz"; break;
+	case V4L2_AUDCLK_32_KHZ: p = "32 kHz"; break;
+	default: p = "undefined";
+	}
+	cx25840_info("Specified audioclock freq: %s\n", p);
+
+	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";
+	}
+	cx25840_info("Preferred audio mode:      %s\n", p);
+
+	if ((audio_config & 0xf) == 0xf) {
+		switch ((afc0 >> 3) & 0x3) {
+		case 0: p = "system DK"; break;
+		case 1: p = "system L"; break;
+		case 2: p = "autodetect"; break;
+		default: p = "undefined";
+		}
+		cx25840_info("Selected 65 MHz format:    %s\n", p);
+
+		switch (afc0 & 0x7) {
+		case 0: p = "chroma"; break;
+		case 1: p = "BTSC"; break;
+		case 2: p = "EIAJ"; break;
+		case 3: p = "A2-M"; break;
+		case 4: p = "autodetect"; break;
+		default: p = "undefined";
+		}
+		cx25840_info("Selected 45 MHz format:    %s\n", p);
+	}
+}
diff --git a/sys/contrib/dev/cxm/cx25840-firmware.c b/sys/contrib/dev/cxm/cx25840-firmware.c
new file mode 100644
index 0000000..63414dc
--- /dev/null
+++ b/sys/contrib/dev/cxm/cx25840-firmware.c
@@ -0,0 +1,253 @@
+/* cx25840 firmware functions
+ *
+ * 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 "fbsd-compat.h"
+#include "v4l2-common.h"
+#include "cx25840.h"
+
+#define FWSEND 1024
+
+#define FWDEV(x) ""
+
+static int fastfw = 1;
+static char *firmware = FWFILE;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+module_param(fastfw, bool, 0444);
+module_param(firmware, charp, 0444);
+#else
+MODULE_PARM(fastfw, "i");
+MODULE_PARM(firmware, "s");
+#endif
+
+MODULE_PARM_DESC(fastfw, "Load firmware fast [0=100MHz 1=333MHz (default)]");
+MODULE_PARM_DESC(firmware, "Firmware image [default: " FWFILE "]");
+
+static inline void set_i2c_delay(device_t client, int delay)
+{
+#ifdef _nuniet_
+	struct i2c_algo_bit_data *algod = client->adapter->algo_data;
+
+	/* We aren't guaranteed to be using algo_bit,
+	 * so avoid the null pointer dereference
+	 * and disable the 'fast firmware load' */
+	if (algod) {
+		algod->udelay = delay;
+	} else {
+		fastfw = 0;
+	}
+#endif
+}
+
+static inline void start_fw_load(device_t client)
+{
+	/* DL_ADDR_LB=0 DL_ADDR_HB=0 */
+	cx25840_write(client, 0x800, 0x00);
+	cx25840_write(client, 0x801, 0x00);
+	/* DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1 */
+	cx25840_write(client, 0x803, 0x0b);
+	/* AUTO_INC_DIS=1 */
+	cx25840_write(client, 0x000, 0x20);
+
+	if (fastfw)
+		set_i2c_delay(client, 3);
+}
+
+static inline void end_fw_load(device_t client)
+{
+	if (fastfw)
+		set_i2c_delay(client, 10);
+
+	/* AUTO_INC_DIS=0 */
+	cx25840_write(client, 0x000, 0x00);
+	/* DL_ENABLE=0 */
+	cx25840_write(client, 0x803, 0x03);
+}
+
+static inline int check_fw_load(device_t client, int size)
+{
+	/* DL_ADDR_HB DL_ADDR_LB */
+	int s = cx25840_read(client, 0x801) << 8;
+	s |= cx25840_read(client, 0x800);
+
+	if (size != s) {
+		cx25840_err("firmware %s load failed\n", firmware);
+		return -EINVAL;
+	}
+
+	cx25840_info("loaded %s firmware (%d bytes)\n", firmware, size);
+	return 0;
+}
+
+static inline int fw_write(device_t client, u8 * data, int size)
+{
+	int sent;
+
+	if ((sent = i2c_master_send(client, data, size)) < size) {
+
+		if (fastfw) {
+			cx25840_err("333MHz i2c firmware load failed\n");
+			fastfw = 0;
+			set_i2c_delay(client, 10);
+
+			if (sent > 2) {
+				u16 dl_addr = cx25840_read(client, 0x801) << 8;
+				dl_addr |= cx25840_read(client, 0x800);
+				dl_addr -= sent - 2;
+				cx25840_write(client, 0x801, dl_addr >> 8);
+				cx25840_write(client, 0x800, dl_addr & 0xff);
+			}
+
+			if (i2c_master_send(client, data, size) < size) {
+				cx25840_err
+				    ("100MHz i2c firmware load failed\n");
+				return -ENOSYS;
+			}
+
+		} else {
+			cx25840_err("firmware load i2c failure\n");
+			return -ENOSYS;
+		}
+
+	}
+
+	return 0;
+}
+
+int cx25840_loadfw_hp(device_t client)
+{
+	struct firmware *fw = NULL;
+	u8 buffer[4], *ptr;
+	int size, send, retval;
+
+	if (request_firmware((void**)&fw, firmware, (int)FWDEV(client)) != 0) {
+		cx25840_err("unable to open firmware %s\n", firmware);
+		return -EINVAL;
+	}
+
+	start_fw_load(client);
+
+	buffer[0] = 0x08;
+	buffer[1] = 0x02;
+	buffer[2] = fw->data[0];
+	buffer[3] = fw->data[1];
+	retval = fw_write(client, buffer, 4);
+
+	if (retval < 0) {
+		release_firmware(fw);
+		return retval;
+	}
+
+	size = fw->size - 2;
+	ptr = fw->data;
+	while (size > 0) {
+		u8 bkp1 = ptr[0], bkp2 = ptr[1];
+
+		ptr[0] = 0x08;
+		ptr[1] = 0x02;
+		send = size > (FWSEND - 2) ? FWSEND : size + 2;
+		retval = fw_write(client, ptr, send);
+
+		ptr[0] = bkp1;
+		ptr[1] = bkp2;
+
+		if (retval < 0) {
+			release_firmware(fw);
+			return retval;
+		}
+
+		size -= FWSEND - 2;
+		ptr += FWSEND - 2;
+	}
+
+	end_fw_load(client);
+
+	size = fw->size;
+	release_firmware(fw);
+
+	return check_fw_load(client, size);
+}
+
+int cx25840_loadfw_nohp(device_t client)
+{
+	return 0;
+#ifdef _nuniet_
+	loff_t file_offset = 0;
+	mm_segment_t fs;
+	kernel_filep filep;
+	int size, tsize, retval;
+	u8 *buffer;
+
+	buffer = kmalloc(FWSEND, GFP_KERNEL);
+	if (buffer == 0) {
+		cx25840_err("firmware load failed (out of memory)\n");
+		return -ENOMEM;
+	}
+
+	buffer[0] = 0x08;
+	buffer[1] = 0x02;
+
+	fs = get_fs();
+	set_fs(get_ds());
+
+	filep = kernel_file_open(firmware, 0, 0);
+	if (kernel_file_is_err(filep)) {
+		cx25840_err("unable to open firmware %s\n", firmware);
+		kfree(buffer);
+		set_fs(fs);
+		return -EINVAL;
+	}
+
+	start_fw_load(client);
+
+	tsize = 0;
+	do {
+		size =
+		    kernel_file_read(filep, &buffer[2], FWSEND - 2,
+				     &file_offset);
+		if (size < 0) {
+			cx25840_err("firmware %s read failed\n", firmware);
+			kfree(buffer);
+			kernel_file_close(filep);
+			set_fs(fs);
+			return -EINVAL;
+		}
+
+		tsize += size;
+
+		if (size) {
+			retval = fw_write(client, buffer, size + 2);
+
+			if (retval < 0) {
+				kfree(buffer);
+				kernel_file_close(filep);
+				set_fs(fs);
+				return retval;
+			}
+		}
+
+	} while (size == FWSEND - 2);
+
+	end_fw_load(client);
+
+	kfree(buffer);
+	kernel_file_close(filep);
+	set_fs(fs);
+
+	return check_fw_load(client, tsize);
+#endif
+}
diff --git a/sys/contrib/dev/cxm/cx25840-vbi.c b/sys/contrib/dev/cxm/cx25840-vbi.c
new file mode 100644
index 0000000..fb961a9
--- /dev/null
+++ b/sys/contrib/dev/cxm/cx25840-vbi.c
@@ -0,0 +1,312 @@
+/* cx25840 VBI functions
+ *
+ * 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 "fbsd-compat.h"
+#include "v4l2-common.h"
+#include "cx25840.h"
+
+static inline int odd_parity(u8 c)
+{
+	c ^= (c >> 4);
+	c ^= (c >> 2);
+	c ^= (c >> 1);
+
+	return c & 1;
+}
+
+static inline 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 cx25840_vbi_setup(device_t client)
+{
+	v4l2_std_id std = cx25840_get_v4lstd(client);
+
+	if (std & ~V4L2_STD_NTSC) {
+		/* datasheet startup, step 8d */
+		cx25840_write(client, 0x49f, 0x11);
+
+		cx25840_write(client, 0x470, 0x84);
+		cx25840_write(client, 0x471, 0x00);
+		cx25840_write(client, 0x472, 0x2d);
+		cx25840_write(client, 0x473, 0x5d);
+
+		cx25840_write(client, 0x474, 0x24);
+		cx25840_write(client, 0x475, 0x40);
+		cx25840_write(client, 0x476, 0x24);
+		cx25840_write(client, 0x477, 0x28);
+
+		cx25840_write(client, 0x478, 0x1f);
+		cx25840_write(client, 0x479, 0x02);
+
+		if (std & V4L2_STD_SECAM) {
+			cx25840_write(client, 0x47a, 0x80);
+			cx25840_write(client, 0x47b, 0x00);
+			cx25840_write(client, 0x47c, 0x5f);
+			cx25840_write(client, 0x47d, 0x42);
+		} else {
+			cx25840_write(client, 0x47a, 0x90);
+			cx25840_write(client, 0x47b, 0x20);
+			cx25840_write(client, 0x47c, 0x63);
+			cx25840_write(client, 0x47d, 0x82);
+		}
+
+		cx25840_write(client, 0x47e, 0x0a);
+		cx25840_write(client, 0x47f, 0x01);
+	} else {
+		/* datasheet startup, step 8d */
+		cx25840_write(client, 0x49f, 0x14);
+
+		cx25840_write(client, 0x470, 0x7a);
+		cx25840_write(client, 0x471, 0x00);
+		cx25840_write(client, 0x472, 0x2d);
+		cx25840_write(client, 0x473, 0x5b);
+
+		cx25840_write(client, 0x474, 0x1a);
+		cx25840_write(client, 0x475, 0x70);
+		cx25840_write(client, 0x476, 0x1e);
+		cx25840_write(client, 0x477, 0x1e);
+
+		cx25840_write(client, 0x478, 0x1f);
+		cx25840_write(client, 0x479, 0x02);
+		cx25840_write(client, 0x47a, 0x50);
+		cx25840_write(client, 0x47b, 0x66);
+
+		cx25840_write(client, 0x47c, 0x1f);
+		cx25840_write(client, 0x47d, 0x7c);
+		cx25840_write(client, 0x47e, 0x08);
+		cx25840_write(client, 0x47f, 0x00);
+	}
+}
+
+int cx25840_vbi(device_t client, unsigned int cmd, void *arg)
+{
+	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 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 ((cx25840_read(client, 0x404) & 0x10) == 0)
+			break;
+
+		for (i = 7; i <= 23; i++) {
+			u8 v = cx25840_read(client, 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];
+		}
+		break;
+	}
+
+	case VIDIOC_S_FMT:
+	{
+		int is_pal = !(cx25840_get_v4lstd(client) & V4L2_STD_NTSC);
+		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 */
+			cx25840_vbi_setup(client);
+
+			/* VBI Offset */
+			cx25840_write(client, 0x47f, vbi_offset);
+			cx25840_write(client, 0x404, 0x2e);
+			break;
+		}
+
+		for (x = 0; x <= 23; x++)
+			lcr[x] = 0x00;
+
+		/* Setup VBI */
+		cx25840_vbi_setup(client);
+
+		/* Sliced VBI */
+		cx25840_write(client, 0x404, 0x36);	/* Ancillery data */
+		cx25840_write(client, 0x406, 0x13);
+		cx25840_write(client, 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;
+				}
+			}
+		}
+
+		for (x = 1, i = 0x424; i <= 0x434; i++, x++) {
+			cx25840_write(client, i, lcr[6 + x]);
+		}
+
+		cx25840_write(client, 0x43c, 0x16);
+
+		if (is_pal) {
+			cx25840_write(client, 0x474, 0x2a);
+		} else {
+			cx25840_write(client, 0x474, 0x1a + 6);
+		}
+		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 += 5;
+		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/sys/contrib/dev/cxm/cx25840.h b/sys/contrib/dev/cxm/cx25840.h
new file mode 100644
index 0000000..d0ce59d
--- /dev/null
+++ b/sys/contrib/dev/cxm/cx25840.h
@@ -0,0 +1,122 @@
+/* cx25840 API header
+ *
+ * Copyright (C) 2003-2004 Chris Kennedy
+ *
+ * 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 _CX25840_H_
+#define _CX25840_H_
+
+#include <contrib/dev/cxm/fbsd-compat.h>
+#include <contrib/dev/cxm/v4l2-common.h>
+
+extern int cx25840_debug;
+
+#define cx25840_dbg(fmt, arg...) do { if (cx25840_debug) \
+	printk(KERN_INFO "%s debug %d-%04x: " fmt, "cxm"/*client->driver->name*/, \
+	       0, 0 , ## arg); } while (0)
+
+#define cx25840_err(fmt, arg...) do { \
+	printk(KERN_ERR "%s %d-%04x: " fmt, "cxm"/* client->driver->name */, \
+	       0, 0 , ## arg); } while (0)
+
+#define cx25840_info(fmt, arg...) do { \
+	printk(KERN_INFO "%s %d-%04x: " fmt, "cxm" /* client->driver->name */, \
+	       0, 0 , ## arg); } while (0)
+
+#define CX25840_CID_CARDTYPE (V4L2_CID_PRIVATE_BASE+0)
+
+/* The CARDTYPE_PVR150_WORKAROUND cardtype activates a workaround for a
+   hardware bug that is present in PVR150 (and possible PVR500) cards that
+   have certain NTSC tuners (tveeprom model numbers 85, 99 and 112). The
+   audio autodetect fails on some channels for these models and the workaround
+   is to select the audio standard explicitly. Many thanks to Hauppauge for
+   providing this information. */
+enum cx25840_cardtype {
+	CARDTYPE_PVR150,
+	CARDTYPE_PG600,
+	CARDTYPE_PVR150_WORKAROUND,
+};
+
+enum cx25840_input {
+	CX25840_TUNER,
+	CX25840_COMPOSITE0,
+	CX25840_COMPOSITE1,
+	CX25840_SVIDEO0,
+	CX25840_SVIDEO1
+};
+
+struct cx25840_state {
+	enum cx25840_cardtype cardtype;
+	enum cx25840_input input;
+	int audio_input;
+	enum v4l2_audio_clock_freq audclk_freq;
+};
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-core.c 							   */
+int cx25840_write(device_t client, u16 addr, u8 value);
+int cx25840_write4(device_t client, u16 addr, u32 value);
+u8 cx25840_read(device_t client, u16 addr);
+u32 cx25840_read4(device_t client, u16 addr);
+int cx25840_and_or(device_t client, u16 addr, u8 mask, u8 value);
+v4l2_std_id cx25840_get_v4lstd(device_t client);
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-audio.c                                                         */
+int cx25840_audio(device_t client, unsigned int cmd, void *arg);
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-vbi.c                                                           */
+void cx25840_vbi_setup(device_t client);
+int cx25840_vbi(device_t client, unsigned int cmd, void *arg);
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-firmware.c                                                      */
+ int cx25840_loadfw_hp(device_t client);
+int cx25840_loadfw_nohp(device_t client);
+
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+#define FWFILE "v4l-cx25840.fw"
+static inline int cx25840_loadfw(device_t client)
+{
+	return cx25840_loadfw_hp(client);
+}
+#else
+#define FWFILE "/lib/modules/v4l-cx25840.fw"
+struct firmware {
+	size_t size;
+	u8 *data;
+};
+#ifdef _nuniet_
+#define request_firmware(...) 1
+#define release_firmware(...)
+#endif
+/* static inline int cx25840_loadfw(device_t client)
+{
+	return cx25840_loadfw_nohp(client);
+} */
+#endif
+
+
+void cx25840_initialize(device_t client, int loadfw);
+int cx25840_command(device_t client, unsigned int cmd,
+                           void *arg);
+void set_mute(device_t client, int mute);
+
+
+
+#endif
diff --git a/sys/contrib/dev/cxm/fbsd-compat.c b/sys/contrib/dev/cxm/fbsd-compat.c
new file mode 100644
index 0000000..03466cb
--- /dev/null
+++ b/sys/contrib/dev/cxm/fbsd-compat.c
@@ -0,0 +1,86 @@
+#include "fbsd-compat.h"
+
+#include "cx25840.h" /* waar is dat voor nodig ?? */
+
+#include <bus/iicbus/iiconf.h>
+
+int i2c_last_delay=1000;
+u8 i2c_cl[4];
+void *i2c_cd;
+
+void cx25840_i2c_set_delay(device_t client, int delay)
+{
+        i2c_last_delay = 1000;
+}
+/*
+void * i2c_get_clientdata(device_t client)
+{
+        return i2c_cd;
+}
+*/
+
+int i2c_master_send(device_t client, u8 *buffer, int len)
+{
+        int sent;
+	int res;
+	u_char addr = i2c_cl[device_get_unit(client)] & ~0x1;
+        int error;
+
+        if ((error = iicbus_start(client, addr, 10000))) {
+		int i;
+		kprintf("could not start iic bus \n");
+		for(i=2;i<256;i+=2) {
+			if(!iicbus_start(client,i,0)) {
+				kprintf("probed %d\n",i);
+				iicbus_stop(client);
+			}
+		}
+
+                return (error);
+	}
+
+        res = iicbus_write(client, buffer, len, &sent, 10000);
+
+        iicbus_stop(client);
+
+	if(res!=0) {
+		u8 n;
+		n = i2c_cl[device_get_unit(client)];
+		kprintf("send error @ %d\n",(int)n);
+	}
+
+	if(sent>0)
+		return sent;
+	else
+		return -1;
+}
+
+int i2c_master_recv(device_t client, u8 *buffer, int len)
+{
+        int received;
+	int res;
+	/*
+        iicbus_read(client,  buffer, len, &received,IIC_LAST_READ,0) ; */
+	res= iicbus_block_read(client, i2c_cl[device_get_unit(client)], buffer,len, &received );
+	if(res!=0)
+		kprintf("read error\n");
+        return received;
+}
+
+extern u_int8_t cxm_cx2584x_fw[];
+
+int request_firmware(void **fw, char *file, int dummy)
+{
+        static struct firmware thef;
+
+	*fw = &thef;
+        thef.data=cxm_cx2584x_fw;
+        thef.size = 14264;
+
+        return 0;
+}
+
+int release_firmware(void *fw )
+{
+        return 0;
+}
diff --git a/sys/contrib/dev/cxm/fbsd-compat.h b/sys/contrib/dev/cxm/fbsd-compat.h
new file mode 100644
index 0000000..f70b938
--- /dev/null
+++ b/sys/contrib/dev/cxm/fbsd-compat.h
@@ -0,0 +1,152 @@
+#ifndef _fbsd_compat_h
+#define _fbsd_compat_h
+
+/* needed for node reference in videodev2.h */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+#include <sys/mman.h>
+#include <sys/lock.h>
+#include <sys/sem.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/types.h>
+#include <sys/taskqueue.h>
+#include <sys/file.h>
+#include <sys/poll.h>
+#include <sys/malloc.h>
+#include <sys/signal.h>
+#include <sys/signalvar.h>
+#include <sys/ioccom.h>
+#include <sys/proc.h>
+#include <sys/kthread.h>
+
+
+#ifndef _FBSD6_
+#include <sys/mount.h>
+#include <vfs/ufs/dinode.h>
+#endif
+#include <vfs/ufs/fs.h>
+#include <vfs/ufs/quota.h>
+#include <vfs/ufs/inode.h>
+
+#undef i_flags /* this too! */
+
+
+
+
+
+/* these were defined in BKTR_IOCTL.h */
+#undef AUDIO_EXTERN
+#undef AUDIO_INTERN
+
+/* missing types ? */
+#if __FreeBSD_version < 500000
+	#include <machine/inttypes.h>
+#else
+	#include <inttypes.h>
+#endif
+
+#define u16 uint16_t
+#define u32 uint32_t
+#define u8  uint8_t
+#define __u32 uint32_t
+#define __u8 uint8_t
+#define __u16 uint16_t
+#define __s32 int32_t
+#define __u64	uint32_t
+#define s8	int8_t
+#define s16	int16_t
+#define s32	int32_t
+
+
+
+/* macros too compile linux-modules easily */
+
+#define MODULE_PARM_DESC(x,y)
+#define MODULE_PARM(x,y)
+#define MODULE_AUTHOR(x)
+#define MODULE_LICENSE(x)
+#define MODULE_DESCRIPTION(x)
+#define MODULE_SUPPORTED_DEVICE(x)
+#define KERN_ERR "<3>"
+#define KERN_INFO "<2>"
+#define printk(x, args...) kprintf(x, ##args)
+#define I2C_CLIENT_INSMOD
+#define devfs_handle_t int
+#define __user
+#define THIS_MODULE "tasteslike..."
+#define I2C_DF_NOTIFY 3
+#define I2C_DRIVERID_CX25840 0x88
+#define I2C_FUNC_I2C 3
+#define CAP_SYS_ADMIN 3
+#define module_param(x,y,z)
+#define capable(x)	3
+#define i2c_check_functionality(x,y) 3
+#define i2c_driver dummy
+#define I2C_CLIENT_END          0xfffe
+#define I2C_CLIENT_ALLOW_USE            0x01
+
+
+#include <machine/clock.h>
+#define msleep_interruptible(x) DELAY(x)
+
+
+/* additional dummys for videodev2.h */
+
+struct dummy { int dummy_int; } ;
+
+#define class_device dummy
+#define class_device_attribute dummy
+#define inline
+
+/* missing V4L2 constants ? */
+#define IVTV_SLICED_TYPE_TELETEXT_B     (1)
+#define IVTV_SLICED_TYPE_CAPTION_525    (4)
+#define IVTV_SLICED_TYPE_WSS_625        (5)
+#define IVTV_SLICED_TYPE_VPS            (7)
+
+/* I2C compatibility layer ( see .c ) */
+
+
+
+#define i2c_get_clientdata(x)  &(local_info[device_get_unit(x)])
+#define i2c_set_clientdata(x,y)
+
+int i2c_master_send(device_t client, u8 *buffer, int len);
+int i2c_master_recv(device_t client, u8 *buffer, int len);
+void cx25840_i2c_set_delay(device_t client, int delay);
+
+extern u8 i2c_cl[4]; /* ugly */
+extern void *i2c_cd; /* ugly */
+
+/* firmware load is implemented here ( ugly ) */
+
+/* #include "cx25840.h" */
+
+int request_firmware(void **fw, char *file, int dummy);
+int release_firmware(void *fw);
+
+#define KERNEL_VERSION(x,y,z) 1000
+#define LINUX_VERSION_CODE 1001
+#define GFP_KERNEL 1
+
+#include "videodev2.h"
+#include "videodev.h"
+#include "video_decoder.h"
+
+#endif
diff --git a/sys/contrib/dev/cxm/tda9887.c b/sys/contrib/dev/cxm/tda9887.c
new file mode 100644
index 0000000..0008a6a
--- /dev/null
+++ b/sys/contrib/dev/cxm/tda9887.c
@@ -0,0 +1,743 @@
+#include "fbsd-compat.h"
+#include "tuner.h"
+#include "audiochip.h"
+#include "tda9887.h"
+
+#include <bus/iicbus/iiconf.h>
+
+/* Chips:
+   TDA9885 (PAL, NTSC)
+   TDA9886 (PAL, SECAM, NTSC)
+   TDA9887 (PAL, SECAM, NTSC, FM Radio)
+
+   found on:
+   - Pinnacle PCTV (Jul.2002 Version with MT2032, bttv)
+      TDA9887 (world), TDA9885 (USA)
+      Note: OP2 of tda988x must be set to 1, else MT2032 is disabled!
+   - KNC One TV-Station RDS (saa7134)
+   - Hauppauge PVR-150/500 (possibly more)
+*/
+
+/* Addresses to scan
+static unsigned short normal_i2c[] = {
+	0x84 >>1,
+	0x86 >>1,
+	0x96 >>1,
+	I2C_CLIENT_END,
+};
+*/
+
+static unsigned int debug = 1;
+
+/* ---------------------------------------------------------------------- */
+
+#define UNSET  (-1U)
+#define tda9887_info(fmt, arg...) do {\
+	printk(KERN_INFO "%s %d-%04x: " fmt, "tda", 0, 0 , ##arg); } while (0)
+
+#define tda9887_dbg(fmt, arg...) do {\
+	if (debug) \
+		printk(KERN_INFO "%s %d-%04x: " fmt, "tda", 0, 0 , ##arg); } while (0)
+
+struct tda9887 {
+	device_t  client;
+	v4l2_std_id        std;
+	enum tuner_mode    mode;
+	unsigned int       config;
+	unsigned int       pinnacle_id;
+	unsigned int       using_v4l2;
+	unsigned int 	   radio_mode;
+	unsigned char 	   data[4];
+};
+
+struct tvnorm {
+	v4l2_std_id       std;
+	char              *name;
+	unsigned char     b;
+	unsigned char     c;
+	unsigned char     e;
+};
+
+
+/* ---------------------------------------------------------------------- */
+
+/* */
+/* TDA defines */
+/* */
+
+/*// first reg (b) */
+#define cVideoTrapBypassOFF     0x00    /* bit b0 */
+#define cVideoTrapBypassON      0x01    /* bit b0 */
+
+#define cAutoMuteFmInactive     0x00    /* bit b1 */
+#define cAutoMuteFmActive       0x02    /* bit b1 */
+
+#define cIntercarrier           0x00    /* bit b2 */
+#define cQSS                    0x04    /* bit b2 */
+
+#define cPositiveAmTV           0x00    /* bit b3:4 */
+#define cFmRadio                0x08    /* bit b3:4 */
+#define cNegativeFmTV           0x10    /* bit b3:4 */
+
+
+#define cForcedMuteAudioON      0x20    /* bit b5 */
+#define cForcedMuteAudioOFF     0x00    /* bit b5 */
+
+#define cOutputPort1Active      0x00    /* bit b6 */
+#define cOutputPort1Inactive    0x40    /* bit b6 */
+
+#define cOutputPort2Active      0x00    /* bit b7 */
+#define cOutputPort2Inactive    0x80    /* bit b7 */
+
+
+/*// second reg (c) */
+#define cDeemphasisOFF          0x00    /* bit c5 */
+#define cDeemphasisON           0x20    /* bit c5 */
+
+#define cDeemphasis75           0x00    /* bit c6 */
+#define cDeemphasis50           0x40    /* bit c6 */
+
+#define cAudioGain0             0x00    /* bit c7 */
+#define cAudioGain6             0x80    /* bit c7 */
+
+
+/*// third reg (e) */
+#define cAudioIF_4_5             0x00    /* bit e0:1 */
+#define cAudioIF_5_5             0x01    /* bit e0:1 */
+#define cAudioIF_6_0             0x02    /* bit e0:1 */
+#define cAudioIF_6_5             0x03    /* bit e0:1 */
+
+
+#define cVideoIF_58_75           0x00    /* bit e2:4 */
+#define cVideoIF_45_75           0x04    /* bit e2:4 */
+#define cVideoIF_38_90           0x08    /* bit e2:4 */
+#define cVideoIF_38_00           0x0C    /* bit e2:4 */
+#define cVideoIF_33_90           0x10    /* bit e2:4 */
+#define cVideoIF_33_40           0x14    /* bit e2:4 */
+#define cRadioIF_45_75           0x18    /* bit e2:4 */
+#define cRadioIF_38_90           0x1C    /* bit e2:4 */
+
+
+#define cTunerGainNormal         0x00    /* bit e5 */
+#define cTunerGainLow            0x20    /* bit e5 */
+
+#define cGating_18               0x00    /* bit e6 */
+#define cGating_36               0x40    /* bit e6 */
+
+#define cAgcOutON                0x80    /* bit e7 */
+#define cAgcOutOFF               0x00    /* bit e7 */
+
+/* ---------------------------------------------------------------------- */
+
+static struct tvnorm tvnorms[] = {
+	{
+		.std   = V4L2_STD_PAL_BG,
+		.name  = "PAL-BG",
+		.b     = ( cNegativeFmTV  |
+			   cQSS           ),
+		.c     = ( cDeemphasisON  |
+			   cDeemphasis50  ),
+		.e     = ( cAudioIF_5_5   |
+			   cVideoIF_38_90 ),
+	},{
+		.std   = V4L2_STD_PAL_I,
+		.name  = "PAL-I",
+		.b     = ( cNegativeFmTV  |
+			   cQSS           ),
+		.c     = ( cDeemphasisON  |
+			   cDeemphasis50  ),
+		.e     = ( cAudioIF_6_0   |
+			   cVideoIF_38_90 ),
+	},{
+		.std   = V4L2_STD_PAL_DK,
+		.name  = "PAL-DK",
+		.b     = ( cNegativeFmTV  |
+			   cQSS           ),
+		.c     = ( cDeemphasisON  |
+			   cDeemphasis50  ),
+		.e     = ( cAudioIF_6_5   |
+			   cVideoIF_38_00 ),
+	},{
+		.std   = V4L2_STD_PAL_M | V4L2_STD_PAL_N,
+		.name  = "PAL-M/N",
+		.b     = ( cNegativeFmTV  |
+			   cQSS           ),
+		.c     = ( cDeemphasisON  |
+			   cDeemphasis75  ),
+		.e     = ( cAudioIF_4_5   |
+			   cVideoIF_45_75 ),
+	},{
+		.std   = V4L2_STD_SECAM_L,
+		.name  = "SECAM-L",
+		.b     = ( cPositiveAmTV  |
+			   cQSS           ),
+		.e     = ( cGating_36	  |
+			   cAudioIF_6_5   |
+			   cVideoIF_38_90 ),
+	},{
+		.std   = V4L2_STD_SECAM_DK,
+		.name  = "SECAM-DK",
+		.b     = ( cNegativeFmTV  |
+			   cQSS           ),
+		.c     = ( cDeemphasisON  |
+			   cDeemphasis50  ),
+		.e     = ( cAudioIF_6_5   |
+			   cVideoIF_38_00 ),
+	},{
+		.std   = V4L2_STD_NTSC_M,
+		.name  = "NTSC-M",
+		.b     = ( cNegativeFmTV  |
+			   cQSS           ),
+		.c     = ( cDeemphasisON  |
+			   cDeemphasis75 | 0x10  ),
+		.e     = ( cGating_36     |
+			   cAudioIF_4_5   |
+			   cVideoIF_45_75 ),
+	},{
+		.std   = V4L2_STD_NTSC_M_JP,
+		.name  = "NTSC-JP",
+		.b     = ( cNegativeFmTV  |
+			   cQSS           ),
+		.c     = ( cDeemphasisON  |
+			   cDeemphasis50  ),
+		.e     = ( cGating_36     |
+			   cAudioIF_4_5   |
+			   cVideoIF_58_75 ),
+	}
+};
+
+static struct tvnorm radio_stereo = {
+	.name = "Radio Stereo",
+	.b    = ( cFmRadio       |
+		  cQSS           ),
+	.c    = ( cDeemphasisOFF |
+		  cAudioGain6 ),
+	.e    = ( cAudioIF_5_5   |
+		  cRadioIF_38_90 ),
+};
+
+static struct tvnorm radio_mono = {
+	.name = "Radio Mono",
+	.b    = ( cFmRadio       |
+		  cQSS           ),
+	.c    = ( cDeemphasisON  |
+		  cDeemphasis50),
+	.e    = ( cAudioIF_5_5   |
+		  cRadioIF_38_90 ),
+};
+
+/* ---------------------------------------------------------------------- */
+
+static void dump_read_message(struct tda9887 *t, unsigned char *buf)
+{
+	static char *afc[16] = {
+		"- 12.5 kHz",
+		"- 37.5 kHz",
+		"- 62.5 kHz",
+		"- 87.5 kHz",
+		"-112.5 kHz",
+		"-137.5 kHz",
+		"-162.5 kHz",
+		"-187.5 kHz [min]",
+		"+187.5 kHz [max]",
+		"+162.5 kHz",
+		"+137.5 kHz",
+		"+112.5 kHz",
+		"+ 87.5 kHz",
+		"+ 62.5 kHz",
+		"+ 37.5 kHz",
+		"+ 12.5 kHz",
+	};
+	tda9887_info("read: 0x%2x\n", buf[0]);
+	tda9887_info("  after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
+	tda9887_info("  afc            : %s\n", afc[(buf[0] >> 1) & 0x0f]);
+	tda9887_info("  fmif level     : %s\n", (buf[0] & 0x20) ? "high" : "low");
+	tda9887_info("  afc window     : %s\n", (buf[0] & 0x40) ? "in" : "out");
+	tda9887_info("  vfi level      : %s\n", (buf[0] & 0x80) ? "high" : "low");
+}
+
+static void dump_write_message(struct tda9887 *t, unsigned char *buf)
+{
+	static char *sound[4] = {
+		"AM/TV",
+		"FM/radio",
+		"FM/TV",
+		"FM/radio"
+	};
+	static char *adjust[32] = {
+		"-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9",
+		"-8",  "-7",  "-6",  "-5",  "-4",  "-3",  "-2",  "-1",
+		"0",   "+1",  "+2",  "+3",  "+4",  "+5",  "+6",  "+7",
+		"+8",  "+9",  "+10", "+11", "+12", "+13", "+14", "+15"
+	};
+	static char *deemph[4] = {
+		"no", "no", "75", "50"
+	};
+	static char *carrier[4] = {
+		"4.5 MHz",
+		"5.5 MHz",
+		"6.0 MHz",
+		"6.5 MHz / AM"
+	};
+	static char *vif[8] = {
+		"58.75 MHz",
+		"45.75 MHz",
+		"38.9 MHz",
+		"38.0 MHz",
+		"33.9 MHz",
+		"33.4 MHz",
+		"45.75 MHz + pin13",
+		"38.9 MHz + pin13",
+	};
+	static char *rif[4] = {
+		"44 MHz",
+		"52 MHz",
+		"52 MHz",
+		"44 MHz",
+	};
+
+	tda9887_info("write: byte B 0x%02x\n",buf[1]);
+	tda9887_info("  B0   video mode      : %s\n",
+	       (buf[1] & 0x01) ? "video trap" : "sound trap");
+	tda9887_info("  B1   auto mute fm    : %s\n",
+	       (buf[1] & 0x02) ? "yes" : "no");
+	tda9887_info("  B2   carrier mode    : %s\n",
+	       (buf[1] & 0x04) ? "QSS" : "Intercarrier");
+	tda9887_info("  B3-4 tv sound/radio  : %s\n",
+	       sound[(buf[1] & 0x18) >> 3]);
+	tda9887_info("  B5   force mute audio: %s\n",
+	       (buf[1] & 0x20) ? "yes" : "no");
+	tda9887_info("  B6   output port 1   : %s\n",
+	       (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
+	tda9887_info("  B7   output port 2   : %s\n",
+	       (buf[1] & 0x80) ? "high (inactive)" : "low (active)");
+
+	tda9887_info("write: byte C 0x%02x\n",buf[2]);
+	tda9887_info("  C0-4 top adjustment  : %s dB\n", adjust[buf[2] & 0x1f]);
+	tda9887_info("  C5-6 de-emphasis     : %s\n", deemph[(buf[2] & 0x60) >> 5]);
+	tda9887_info("  C7   audio gain      : %s\n",
+	       (buf[2] & 0x80) ? "-6" : "0");
+
+	tda9887_info("write: byte E 0x%02x\n",buf[3]);
+	tda9887_info("  E0-1 sound carrier   : %s\n",
+	       carrier[(buf[3] & 0x03)]);
+	tda9887_info("  E6   l pll gating   : %s\n",
+	       (buf[3] & 0x40) ? "36" : "13");
+
+	if (buf[1] & 0x08) {
+		/* radio */
+		tda9887_info("  E2-4 video if        : %s\n",
+		       rif[(buf[3] & 0x0c) >> 2]);
+		tda9887_info("  E7   vif agc output  : %s\n",
+		       (buf[3] & 0x80)
+		       ? ((buf[3] & 0x10) ? "fm-agc radio" : "sif-agc radio")
+		       : "fm radio carrier afc");
+	} else {
+		/* video */
+		tda9887_info("  E2-4 video if        : %s\n",
+		       vif[(buf[3] & 0x1c) >> 2]);
+		tda9887_info("  E5   tuner gain      : %s\n",
+		       (buf[3] & 0x80)
+		       ? ((buf[3] & 0x20) ? "external" : "normal")
+		       : ((buf[3] & 0x20) ? "minimum"  : "normal"));
+		tda9887_info("  E7   vif agc output  : %s\n",
+		       (buf[3] & 0x80)
+		       ? ((buf[3] & 0x20)
+			  ? "pin3 port, pin22 vif agc out"
+			  : "pin22 port, pin3 vif acg ext in")
+		       : "pin3+pin22 port");
+	}
+	tda9887_info("--\n");
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int tda9887_set_tvnorm(struct tda9887 *t, char *buf)
+{
+	struct tvnorm *norm = NULL;
+	int i;
+
+	if (t->mode == T_RADIO) {
+		if (t->radio_mode == V4L2_TUNER_MODE_MONO)
+			norm = &radio_mono;
+		else
+			norm = &radio_stereo;
+	} else {
+		for (i = 0; i < 8 /*ARRAY_SIZE(tvnorms)*/; i++) {
+			if (tvnorms[i].std & t->std) {
+				norm = tvnorms+i;
+				break;
+			}
+		}
+		/* norm = tvnorms; */
+	}
+
+	if (NULL == norm) {
+		tda9887_dbg("Unsupported tvnorm entry - audio muted\n");
+		return -1;
+	}
+
+	tda9887_dbg("configure for: %s\n",norm->name);
+	buf[1] = norm->b;
+	buf[2] = norm->c;
+	buf[3] = norm->e;
+	return 0;
+}
+
+static unsigned int port1  = UNSET;
+static unsigned int port2  = UNSET;
+static unsigned int qss    = UNSET;
+static unsigned int adjust = 0x10;
+
+static int tda9887_set_insmod(struct tda9887 *t, char *buf)
+{
+	if (UNSET != port1) {
+		if (port1)
+			buf[1] |= cOutputPort1Inactive;
+		else
+			buf[1] &= ~cOutputPort1Inactive;
+	}
+	if (UNSET != port2) {
+		if (port2)
+			buf[1] |= cOutputPort2Inactive;
+		else
+			buf[1] &= ~cOutputPort2Inactive;
+	}
+
+	if (UNSET != qss) {
+		if (qss)
+			buf[1] |= cQSS;
+		else
+			buf[1] &= ~cQSS;
+	}
+
+	if (adjust >= 0x00 && adjust < 0x20)
+		buf[2] |= adjust;
+	return 0;
+}
+
+static int tda9887_set_config(struct tda9887 *t, char *buf)
+{
+	if (t->config & TDA9887_PORT1_ACTIVE)
+		buf[1] &= ~cOutputPort1Inactive;
+	if (t->config & TDA9887_PORT1_INACTIVE)
+		buf[1] |= cOutputPort1Inactive;
+	if (t->config & TDA9887_PORT2_ACTIVE)
+		buf[1] &= ~cOutputPort2Inactive;
+	if (t->config & TDA9887_PORT2_INACTIVE)
+		buf[1] |= cOutputPort2Inactive;
+
+	if (t->config & TDA9887_QSS)
+		buf[1] |= cQSS;
+	if (t->config & TDA9887_INTERCARRIER)
+		buf[1] &= ~cQSS;
+
+	if (t->config & TDA9887_AUTOMUTE)
+		buf[1] |= cAutoMuteFmActive;
+	if (t->config & TDA9887_DEEMPHASIS_MASK) {
+		buf[2] &= ~0x60;
+		switch (t->config & TDA9887_DEEMPHASIS_MASK) {
+		case TDA9887_DEEMPHASIS_NONE:
+			buf[2] |= cDeemphasisOFF;
+			break;
+		case TDA9887_DEEMPHASIS_50:
+			buf[2] |= cDeemphasisON | cDeemphasis50;
+			break;
+		case TDA9887_DEEMPHASIS_75:
+			buf[2] |= cDeemphasisON | cDeemphasis75;
+			break;
+		}
+	}
+	if ((t->config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC))
+		buf[1] &= ~cQSS;
+	return 0;
+}
+
+static int tda9887_set_pinnacle(struct tda9887 *t, char *buf)
+{
+	unsigned int bCarrierMode = UNSET;
+
+	if (t->std & V4L2_STD_625_50) {
+		if ((1 == t->pinnacle_id) || (7 == t->pinnacle_id)) {
+			bCarrierMode = cIntercarrier;
+		} else {
+			bCarrierMode = cQSS;
+		}
+	}
+	if (t->std & V4L2_STD_525_60) {
+		if ((5 == t->pinnacle_id) || (6 == t->pinnacle_id)) {
+			bCarrierMode = cIntercarrier;
+		} else {
+			bCarrierMode = cQSS;
+		}
+	}
+
+	if (bCarrierMode != UNSET) {
+		buf[1] &= ~0x04;
+		buf[1] |= bCarrierMode;
+	}
+	return 0;
+}
+
+static char pal[] = "-";
+static char secam[] = "-";
+
+static int tda9887_fixup_std(struct tda9887 *t)
+{
+	/* get more precise norm info from insmod option */
+	if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
+		switch (pal[0]) {
+		case 'b':
+		case 'B':
+		case 'g':
+		case 'G':
+			tda9887_dbg("insmod fixup: PAL => PAL-BG\n");
+			t->std = V4L2_STD_PAL_BG;
+			break;
+		case 'i':
+		case 'I':
+			tda9887_dbg("insmod fixup: PAL => PAL-I\n");
+			t->std = V4L2_STD_PAL_I;
+			break;
+		case 'd':
+		case 'D':
+		case 'k':
+		case 'K':
+			tda9887_dbg("insmod fixup: PAL => PAL-DK\n");
+			t->std = V4L2_STD_PAL_DK;
+			break;
+		case '-':
+			/* default parameter, do nothing */
+			break;
+		default:
+			tda9887_info("pal= argument not recognised\n");
+			break;
+		}
+	}
+	if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
+		switch (secam[0]) {
+		case 'd':
+		case 'D':
+		case 'k':
+		case 'K':
+			tda9887_dbg("insmod fixup: SECAM => SECAM-DK\n");
+			t->std = V4L2_STD_SECAM_DK;
+			break;
+		case 'l':
+		case 'L':
+			tda9887_dbg("insmod fixup: SECAM => SECAM-L\n");
+			t->std = V4L2_STD_SECAM_L;
+			break;
+		case '-':
+			/* default parameter, do nothing */
+			break;
+		default:
+			tda9887_info("secam= argument not recognised\n");
+			break;
+		}
+	}
+	return 0;
+}
+
+static int tda9887_status(struct tda9887 *t)
+{
+	unsigned char buf[1];
+	int rc;
+
+	memset(buf,0,sizeof(buf));
+	if (1 != (rc = i2c_master_recv(t->client,buf,1)))
+		tda9887_info("i2c i/o error: rc == %d (should be 1)\n",rc);
+	dump_read_message(t, buf);
+	return 0;
+}
+
+static int tda9887_configure(struct tda9887 *t)
+{
+	int rc;
+
+	memset(t->data,0,sizeof(t->data));
+	tda9887_set_tvnorm(t,t->data);
+
+	t->data[1] |= cOutputPort1Inactive;
+	t->data[1] |= cOutputPort2Inactive;
+
+	if (UNSET != t->pinnacle_id) {
+		tda9887_set_pinnacle(t,t->data);
+	}
+	tda9887_set_config(t,t->data);
+	tda9887_set_insmod(t,t->data);
+
+	if (t->mode == T_STANDBY) {
+		t->data[1] |= cForcedMuteAudioON;
+	}
+
+#if 0
+	/* This as-is breaks some cards, must be fixed in a
+	 * card-specific way, probably using TDA9887_SET_CONFIG to
+	 * turn on/off port2 */
+	if (t->std & V4L2_STD_SECAM_L) {
+		/* secam fixup (FIXME: move this to tvnorms array?) */
+		buf[1] &= ~cOutputPort2Inactive;
+	}
+#endif
+
+	tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
+		t->data[1],t->data[2],t->data[3]);
+	if (debug > 1)
+		dump_write_message(t, t->data);
+
+	if (4 != (rc = i2c_master_send(t->client,t->data,4)))
+		tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc);
+
+	if (debug > 2) {
+		msleep_interruptible(1000);
+		tda9887_status(t);
+	}
+
+	DELAY(1000);
+    tda9887_status(t);
+
+	return 0;
+}
+
+
+#define SWITCH_V4L2	if (!t->using_v4l2 && debug) \
+			  tda9887_info("switching to v4l2\n"); t->using_v4l2 = 1;
+
+static struct tda9887 local_info[4];
+
+int tda9887_initialize(device_t client)
+{
+ struct tda9887 *t = i2c_get_clientdata(client);
+
+	t->client      = client;
+    t->std         = 0;
+    t->pinnacle_id = UNSET;
+    t->radio_mode  = V4L2_TUNER_MODE_STEREO;
+	t->config = 0;/* TDA9887_PORT1_INACTIVE | TDA9887_PORT2_INACTIVE ;  */
+    t->mode = T_ANALOG_TV;
+
+	return 0;
+}
+
+int
+tda9887_command(device_t client, unsigned int cmd, void *arg)
+{
+	struct tda9887 *t = i2c_get_clientdata(client);
+
+       i2c_cl[device_get_unit(client)] = 0x86;
+
+	switch (cmd) {
+
+	/* --- configuration --- */
+	case AUDC_SET_RADIO:
+	{
+		t->mode = T_RADIO;
+		tda9887_configure(t);
+		break;
+	}
+	case TUNER_SET_STANDBY:
+	{
+		t->mode = T_STANDBY;
+		tda9887_configure(t);
+		break;
+	}
+	case AUDC_CONFIG_PINNACLE:
+	{
+		int *i = arg;
+
+		t->pinnacle_id = *i;
+		tda9887_configure(t);
+		break;
+	}
+	case TDA9887_SET_CONFIG:
+	{
+		int *i = arg;
+
+		t->config = *i;
+		tda9887_configure(t);
+		break;
+	}
+	/* --- v4l ioctls --- */
+	/* take care: bttv does userspace copying, we'll get a
+	   kernel pointer here... */
+	case /* VIDIOCSCHAN*/142:
+	{
+		/* struct video_channel *vc = arg; */
+
+		t->mode = T_ANALOG_TV;
+		tda9887_fixup_std(t);
+		tda9887_configure(t);
+		break;
+	}
+	case VIDIOC_S_STD:
+	{
+		v4l2_std_id *id = arg;
+
+		SWITCH_V4L2;
+		t->mode = T_ANALOG_TV;
+		t->std   = *id;
+		tda9887_fixup_std(t);
+		tda9887_configure(t);
+		break;
+	}
+	case VIDIOC_S_FREQUENCY:
+	{
+		/* struct v4l2_frequency *f = arg; */
+/*
+		SWITCH_V4L2;
+		if (V4L2_TUNER_ANALOG_TV == f->type) {
+			if (t->mode == T_ANALOG_TV)
+				return 0;
+			t->mode = T_ANALOG_TV;
+		}
+		if (V4L2_TUNER_RADIO == f->type) {
+			if (t->mode == T_RADIO)
+				return 0;
+			t->mode = T_RADIO;
+		}
+*/
+		tda9887_configure(t);
+		break;
+	}
+	case VIDIOC_G_TUNER:
+	{
+		static int AFC_BITS_2_kHz[] = {
+			-12500,  -37500,  -62500,  -97500,
+			-112500, -137500, -162500, -187500,
+			187500,  162500,  137500,  112500,
+			97500 ,  62500,   37500 ,  12500
+		};
+		struct v4l2_tuner* tuner = arg;
+
+		if (t->mode == T_RADIO) {
+			__u8 reg = 0;
+			tuner->afc=0;
+			if (1 == i2c_master_recv(t->client,&reg,1))
+				tuner->afc = AFC_BITS_2_kHz[(reg>>1)&0x0f];
+		}
+		break;
+	}
+	case VIDIOC_S_TUNER:
+	{
+		struct v4l2_tuner* tuner = arg;
+
+		if (t->mode == T_RADIO) {
+			t->radio_mode = tuner->audmode;
+			tda9887_configure (t);
+		}
+		break;
+	}
+	case VIDIOC_LOG_STATUS:
+	{
+		tda9887_info("Data bytes: b=%02x c=%02x e=%02x\n",
+                      t->data[1], t->data[2], t->data[3]);
+		break;
+	}
+	default:
+		/* nothing */
+		break;
+	}
+	return 0;
+}
+
diff --git a/sys/contrib/dev/cxm/tda9887.h b/sys/contrib/dev/cxm/tda9887.h
new file mode 100644
index 0000000..96776e9
--- /dev/null
+++ b/sys/contrib/dev/cxm/tda9887.h
@@ -0,0 +1,2 @@
+int tda9887_command(device_t client, unsigned int cmd, void *arg);
+int tda9887_initialize(device_t client);
diff --git a/sys/contrib/dev/cxm/tuner.h b/sys/contrib/dev/cxm/tuner.h
new file mode 100644
index 0000000..63e1b9e
--- /dev/null
+++ b/sys/contrib/dev/cxm/tuner.h
@@ -0,0 +1,162 @@
+/*
+    tuner.h - definition for different tuners
+
+    Copyright (C) 1997 Markus Schroeder (schroedm@uni-duesseldorf.de)
+    minor modifications by Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+    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.
+*/
+
+#ifndef _TUNER_H
+#define _TUNER_H
+
+#define TUNER_TEMIC_PAL     0	/* 4002 FH5 (3X 7756, 9483) */
+#define TUNER_PHILIPS_PAL_I 1
+#define TUNER_PHILIPS_NTSC  2
+#define TUNER_PHILIPS_SECAM 3	/* you must actively select B/G, L, L` */
+#define TUNER_ABSENT        4
+#define TUNER_PHILIPS_PAL   5
+#define TUNER_TEMIC_NTSC    6	/* 4032 FY5 (3X 7004, 9498, 9789)  */
+#define TUNER_TEMIC_PAL_I   7	/* 4062 FY5 (3X 8501, 9957)        */
+#define TUNER_TEMIC_4036FY5_NTSC 8	/* 4036 FY5 (3X 1223, 1981, 7686)  */
+#define TUNER_ALPS_TSBH1_NTSC 	 9
+#define TUNER_ALPS_TSBE1_PAL 	10
+#define TUNER_ALPS_TSBB5_PAL_I 	11
+#define TUNER_ALPS_TSBE5_PAL 	12
+#define TUNER_ALPS_TSBC5_PAL 	13
+#define TUNER_TEMIC_4006FH5_PAL	14	/* 4006 FH5 (3X 9500, 9501, 7291)     */
+#define TUNER_ALPS_TSHC6_NTSC 	15
+#define TUNER_TEMIC_PAL_DK	16	/* 4016 FY5 (3X 1392, 1393)     */
+#define TUNER_PHILIPS_NTSC_M	17
+#define TUNER_TEMIC_4066FY5_PAL_I       18	/* 4066 FY5 (3X 7032, 7035) */
+#define TUNER_TEMIC_4006FN5_MULTI_PAL   19	/* B/G, I and D/K autodetected (3X 7595, 7606, 7657) */
+#define TUNER_TEMIC_4009FR5_PAL         20	/* incl. FM radio (3X 7607, 7488, 7711) */
+#define TUNER_TEMIC_4039FR5_NTSC        21	/* incl. FM radio (3X 7246, 7578, 7732) */
+#define TUNER_TEMIC_4046FM5             22	/* you must actively select B/G, D/K, I, L, L` !  (3X 7804, 7806, 8103, 8104) */
+#define TUNER_PHILIPS_PAL_DK		23
+#define TUNER_PHILIPS_FQ1216ME		24	/* you must actively select B/G/D/K, I, L, L` */
+#define TUNER_LG_PAL_I_FM	25
+#define TUNER_LG_PAL_I		26
+#define TUNER_LG_NTSC_FM	27
+#define TUNER_LG_PAL_FM		28
+#define TUNER_LG_PAL		29
+#define TUNER_TEMIC_4009FN5_MULTI_PAL_FM	30	/* B/G, I and D/K autodetected (3X 8155, 8160, 8163) */
+#define TUNER_SHARP_2U5JF5540_NTSC  31
+#define TUNER_Samsung_PAL_TCPM9091PD27 32
+#define TUNER_MT2032 33
+#define TUNER_TEMIC_4106FH5 	34	/* 4106 FH5 (3X 7808, 7865) */
+#define TUNER_TEMIC_4012FY5	35	/* 4012 FY5 (3X 0971, 1099) */
+#define TUNER_TEMIC_4136FY5	36	/* 4136 FY5 (3X 7708, 7746) */
+#define TUNER_LG_PAL_NEW_TAPC   37
+#define TUNER_PHILIPS_FM1216ME_MK3  38
+#define TUNER_LG_NTSC_NEW_TAPC   39
+#define TUNER_HITACHI_NTSC       40
+#define TUNER_PHILIPS_PAL_MK     41
+#define TUNER_PHILIPS_ATSC       42
+#define TUNER_PHILIPS_FM1236_MK3 43
+#define TUNER_PHILIPS_4IN1       44	/* ATI TV Wonder Pro - Conexant */
+#define TUNER_MICROTUNE_4049FM5  45
+#define TUNER_LG_NTSC_TAPE       47
+#define TUNER_TNF_8831BGFF       48
+#define TUNER_MICROTUNE_4042FI5  49	/* FusionHDTV 3 Gold - 4042 FI5 (3X 8147) */
+#define TUNER_TCL_2002N          50
+#define TUNER_PHILIPS_FM1256_IH3 51
+#define TUNER_THOMSON_DTT7610    52
+#define TUNER_PHILIPS_FQ1286     53
+#define TUNER_PHILIPS_TDA8290    54
+#define TUNER_TCL_2002MB         55	/* Hauppauge PVR-150 PAL */
+#define TUNER_PHILIPS_FQ1216AME_MK4 56	/* Hauppauge PVR-150 PAL */
+#define TUNER_PHILIPS_FQ1236A_MK4 57	/* Hauppauge PVR-500MCE NTSC */
+#define TUNER_YMEC_TVF_8531MF 58
+#define TUNER_YMEC_TVF_5533MF 59	/* Pixelview Pro Ultra NTSC */
+#define TUNER_THOMSON_DTT7611 60	/* DViCO FusionHDTV 3 Gold-T */
+#define TUNER_TENA_9533_DI    61
+
+#define TUNER_TEA5767         62	/* Only FM Radio Tuner */
+#define TUNER_PHILIPS_FMD1216ME_MK3 63
+#define TUNER_LG_TDVS_H062F   64	/* DViCO FusionHDTV 5 */
+#define TUNER_YMEC_TVF66T5_B_DFF 65	/* Acorp Y878F */
+
+#define TUNER_LG_NTSC_TALN_MINI 66
+#define TUNER_PHILIPS_TD1316            67
+#define TUNER_SAMSUNG_TCPN_2121P30A     68
+
+#define NOTUNER 0
+#define PAL     1		/* PAL_BG */
+#define PAL_I   2
+#define NTSC    3
+#define SECAM   4
+#define ATSC    5
+#define RADIO   6
+
+#define NoTuner 0
+#define Philips 1
+#define TEMIC   2
+#define Sony    3
+#define Alps    4
+#define LGINNOTEK 5
+#define SHARP   6
+#define Samsung 7
+#define Microtune 8
+#define HITACHI 9
+#define Panasonic 10
+#define TCL     11
+#define THOMSON 12
+
+#define ADDR_UNSET (255)
+
+enum tuner_mode {
+	T_UNINITIALIZED = 0,
+	T_RADIO		= 1 << V4L2_TUNER_RADIO,
+	T_ANALOG_TV     = 1 << V4L2_TUNER_ANALOG_TV,
+	T_DIGITAL_TV     = 1 << 3,
+	T_STANDBY	= 1 << 31
+};
+
+struct tuner_setup {
+	unsigned short		addr;
+	unsigned int		type;
+	unsigned int		mode_mask;
+};
+
+#define TUNER_SET_TYPE_ADDR          _IOW('T',3,int)
+#define TUNER_SET_STANDBY            _IOW('T',4,int)
+
+#define TUNER_SET_TYPE               _IOW('t',1,int)	/* set tuner type */
+#if 0				/* obsolete */
+#define TUNER_SET_TVFREQ             _IOW('t',2,int)	/* set tv freq */
+# define TUNER_SET_RADIOFREQ         _IOW('t',3,int)	/* set radio freq */
+# define TUNER_SET_MODE              _IOW('t',4,int)	/* set tuner mode */
+#endif
+
+#define  TDA9887_SET_CONFIG          _IOW('t',5,int)
+/* tv card specific */
+# define TDA9887_PRESENT             (1<<0)
+# define TDA9887_PORT1_INACTIVE      (1<<1)
+# define TDA9887_PORT2_INACTIVE      (1<<2)
+# define TDA9887_QSS                 (1<<3)
+# define TDA9887_INTERCARRIER        (1<<4)
+# define TDA9887_PORT1_ACTIVE        (1<<5)
+# define TDA9887_PORT2_ACTIVE        (1<<6)
+# define TDA9887_INTERCARRIER_NTSC   (1<<7)
+/* config options */
+# define TDA9887_DEEMPHASIS_MASK     (3<<16)
+# define TDA9887_DEEMPHASIS_NONE     (1<<16)
+# define TDA9887_DEEMPHASIS_50       (2<<16)
+# define TDA9887_DEEMPHASIS_75       (3<<16)
+# define TDA9887_AUTOMUTE            (1<<18)
+# define TDA9887_LOW_GAIN            (1<<19)
+
+#endif
diff --git a/sys/contrib/dev/cxm/v4l2-common.h b/sys/contrib/dev/cxm/v4l2-common.h
new file mode 100644
index 0000000..d3fd481
--- /dev/null
+++ b/sys/contrib/dev/cxm/v4l2-common.h
@@ -0,0 +1,110 @@
+/*
+    v4l2 common internal API header
+
+    This header contains internal shared ioctl definitions for use by the
+    internal low-level v4l2 drivers.
+    Each ioctl begins with VIDIOC_INT_ to clearly mark that it is an internal
+    define,
+
+    Copyright (C) 2005  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 V4L2_COMMON_H_
+#define V4L2_COMMON_H_
+
+/* VIDIOC_INT_AUDIO_CLOCK_FREQ */
+enum v4l2_audio_clock_freq {
+	V4L2_AUDCLK_32_KHZ  = 32000,
+	V4L2_AUDCLK_441_KHZ = 44100,
+	V4L2_AUDCLK_48_KHZ  = 48000,
+};
+
+/* VIDIOC_INT_G_REGISTER and VIDIOC_INT_S_REGISTER */
+struct v4l2_register {
+	u32 i2c_id; 		/* I2C driver ID of the I2C chip. 0 for the I2C adapter. */
+	unsigned long reg;
+	u32 val;
+};
+
+/* VIDIOC_INT_DECODE_VBI_LINE */
+struct v4l2_decode_vbi_line {
+	u32 is_second_field;	/* Set to 0 for the first (odd) field,
+				   set to 1 for the second (even) field. */
+	u8 *p; 			/* Pointer to the sliced VBI data from the decoder.
+				   On exit points to the start of the payload. */
+	u32 line;		/* Line number of the sliced VBI data (1-23) */
+	u32 type;		/* VBI service type (V4L2_SLICED_*). 0 if no service found */
+};
+
+/* VIDIOC_INT_G_CHIP_IDENT: identifies the actual chip installed on the board */
+enum v4l2_chip_ident {
+	/* general idents: reserved range 0-49 */
+	V4L2_IDENT_UNKNOWN = 0,
+
+	/* module saa7115: reserved range 100-149 */
+	V4L2_IDENT_SAA7114 = 104,
+	V4L2_IDENT_SAA7115 = 105,
+
+	/* module saa7127: reserved range 150-199 */
+	V4L2_IDENT_SAA7127 = 157,
+	V4L2_IDENT_SAA7129 = 159,
+
+	/* module cx25840: reserved range 200-249 */
+	V4L2_IDENT_CX25840 = 240,
+	V4L2_IDENT_CX25841 = 241,
+	V4L2_IDENT_CX25842 = 242,
+	V4L2_IDENT_CX25843 = 243,
+};
+
+/* only implemented if CONFIG_VIDEO_ADV_DEBUG is defined */
+#define	VIDIOC_INT_S_REGISTER 		_IOR ('d', 100, struct v4l2_register)
+#define	VIDIOC_INT_G_REGISTER 		_IOWR('d', 101, struct v4l2_register)
+
+/* Reset the I2C chip */
+#define VIDIOC_INT_RESET            	_IO  ('d', 102)
+
+/* Set the frequency of the audio clock output.
+   Used to slave an audio processor to the video decoder, ensuring that audio
+   and video remain synchronized. */
+#define VIDIOC_INT_AUDIO_CLOCK_FREQ 	_IOR ('d', 103, enum v4l2_audio_clock_freq)
+
+/* Video decoders that support sliced VBI need to implement this ioctl.
+   Field p of the v4l2_sliced_vbi_line struct is set to the start of the VBI
+   data that was generated by the decoder. The driver then parses the sliced
+   VBI data and sets the other fields in the struct accordingly. The pointer p
+   is updated to point to the start of the payload which can be copied
+   verbatim into the data field of the v4l2_sliced_vbi_data struct. If no
+   valid VBI data was found, then the type field is set to 0 on return. */
+#define VIDIOC_INT_DECODE_VBI_LINE  	_IOWR('d', 104, struct v4l2_decode_vbi_line)
+
+/* Used to generate VBI signals on a video signal. v4l2_sliced_vbi_data is
+   filled with the data packets that should be output. Note that if you set
+   the line field to 0, then that VBI signal is disabled. */
+#define VIDIOC_INT_S_VBI_DATA 		_IOW ('d', 105, struct v4l2_sliced_vbi_data)
+
+/* Used to obtain the sliced VBI packet from a readback register. Not all
+   video decoders support this. If no data is available because the readback
+   register contains invalid or erroneous data -EIO is returned. Note that
+   you must fill in the 'id' member and the 'field' member (to determine
+   whether CC data from the first or second field should be obtained). */
+#define VIDIOC_INT_G_VBI_DATA 		_IOWR('d', 106, struct v4l2_sliced_vbi_data *)
+
+/* Returns the chip identifier or V4L2_IDENT_UNKNOWN if no identification can
+   be made. */
+#define VIDIOC_INT_G_CHIP_IDENT		_IOR ('d', 107, enum v4l2_chip_ident *)
+
+#endif /* V4L2_COMMON_H_ */
diff --git a/sys/contrib/dev/cxm/video_decoder.h b/sys/contrib/dev/cxm/video_decoder.h
new file mode 100644
index 0000000..1302c7f
--- /dev/null
+++ b/sys/contrib/dev/cxm/video_decoder.h
@@ -0,0 +1,37 @@
+#ifndef _LINUX_VIDEO_DECODER_H
+#define _LINUX_VIDEO_DECODER_H
+
+struct video_decoder_capability { /* this name is too long */
+	__u32	flags;
+#define	VIDEO_DECODER_PAL	1	/* can decode PAL signal */
+#define	VIDEO_DECODER_NTSC	2	/* can decode NTSC */
+#define	VIDEO_DECODER_SECAM	4	/* can decode SECAM */
+#define	VIDEO_DECODER_AUTO	8	/* can autosense norm */
+#define	VIDEO_DECODER_CCIR	16	/* CCIR-601 pixel rate (720 pixels per line) instead of square pixel rate */
+	int	inputs;			/* number of inputs */
+	int	outputs;		/* number of outputs */
+};
+
+/*
+DECODER_GET_STATUS returns the following flags.  The only one you need is
+DECODER_STATUS_GOOD, the others are just nice things to know.
+*/
+#define	DECODER_STATUS_GOOD	1	/* receiving acceptable input */
+#define	DECODER_STATUS_COLOR	2	/* receiving color information */
+#define	DECODER_STATUS_PAL	4	/* auto detected */
+#define	DECODER_STATUS_NTSC	8	/* auto detected */
+#define	DECODER_STATUS_SECAM	16	/* auto detected */
+
+
+#define	DECODER_GET_CAPABILITIES _IOR('d', 1, struct video_decoder_capability)
+#define	DECODER_GET_STATUS    	_IOR('d', 2, int)
+#define	DECODER_SET_NORM	_IOW('d', 3, int)
+#define	DECODER_SET_INPUT	_IOW('d', 4, int)	/* 0 <= input < #inputs */
+#define	DECODER_SET_OUTPUT	_IOW('d', 5, int)	/* 0 <= output < #outputs */
+#define	DECODER_ENABLE_OUTPUT	_IOW('d', 6, int)	/* boolean output enable control */
+#define	DECODER_SET_PICTURE   	_IOW('d', 7, struct video_picture)
+
+#define	DECODER_DUMP		_IO('d', 192)		/* debug hook */
+
+
+#endif
diff --git a/sys/contrib/dev/cxm/videodev.h b/sys/contrib/dev/cxm/videodev.h
new file mode 100644
index 0000000..061ce5a
--- /dev/null
+++ b/sys/contrib/dev/cxm/videodev.h
@@ -0,0 +1,368 @@
+#ifndef __LINUX_VIDEODEV_H
+#define __LINUX_VIDEODEV_H
+
+#define HAVE_V4L1 1
+
+#ifdef __KERNEL__
+
+extern struct video_device* video_devdata(struct file*);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+#define to_video_device(cd) container_of(cd, struct video_device, class_dev)
+static inline void
+video_device_create_file(struct video_device *vfd,
+			 struct class_device_attribute *attr)
+{
+	class_device_create_file(&vfd->class_dev, attr);
+}
+static inline void
+video_device_remove_file(struct video_device *vfd,
+			 struct class_device_attribute *attr)
+{
+	class_device_remove_file(&vfd->class_dev, attr);
+}
+#endif
+
+#if OBSOLETE_OWNER /* to be removed in 2.6.15 */
+/* helper functions to access driver private data. */
+static inline void *video_get_drvdata(struct video_device *dev)
+{
+	return dev->priv;
+}
+
+static inline void video_set_drvdata(struct video_device *dev, void *data)
+{
+	dev->priv = data;
+}
+#endif
+
+extern int video_exclusive_open(struct inode *inode, struct file *file);
+extern int video_exclusive_release(struct inode *inode, struct file *file);
+#endif /* __KERNEL__ */
+
+struct video_capability
+{
+	char name[32];
+	int type;
+	int channels;	/* Num channels */
+	int audios;	/* Num audio devices */
+	int maxwidth;	/* Supported width */
+	int maxheight;	/* And height */
+	int minwidth;	/* Supported width */
+	int minheight;	/* And height */
+};
+
+
+struct video_channel
+{
+	int channel;
+	char name[32];
+	int tuners;
+	__u32  flags;
+#define VIDEO_VC_TUNER		1	/* Channel has a tuner */
+#define VIDEO_VC_AUDIO		2	/* Channel has audio */
+	__u16  type;
+#define VIDEO_TYPE_TV		1
+#define VIDEO_TYPE_CAMERA	2
+	__u16 norm;			/* Norm set by channel */
+};
+
+struct video_tuner
+{
+	int tuner;
+	char name[32];
+	unsigned long rangelow, rangehigh;	/* Tuner range */
+	__u32 flags;
+#define VIDEO_TUNER_PAL		1
+#define VIDEO_TUNER_NTSC	2
+#define VIDEO_TUNER_SECAM	4
+#define VIDEO_TUNER_LOW		8	/* Uses KHz not MHz */
+#define VIDEO_TUNER_NORM	16	/* Tuner can set norm */
+#define VIDEO_TUNER_STEREO_ON	128	/* Tuner is seeing stereo */
+#define VIDEO_TUNER_RDS_ON      256     /* Tuner is seeing an RDS datastream */
+#define VIDEO_TUNER_MBS_ON      512     /* Tuner is seeing an MBS datastream */
+	__u16 mode;			/* PAL/NTSC/SECAM/OTHER */
+#define VIDEO_MODE_PAL		0
+#define VIDEO_MODE_NTSC		1
+#define VIDEO_MODE_SECAM	2
+#define VIDEO_MODE_AUTO		3
+	__u16 signal;			/* Signal strength 16bit scale */
+};
+
+struct video_picture
+{
+	__u16	brightness;
+	__u16	hue;
+	__u16	colour;
+	__u16	contrast;
+	__u16	whiteness;	/* Black and white only */
+	__u16	depth;		/* Capture depth */
+	__u16   palette;	/* Palette in use */
+#define VIDEO_PALETTE_GREY	1	/* Linear greyscale */
+#define VIDEO_PALETTE_HI240	2	/* High 240 cube (BT848) */
+#define VIDEO_PALETTE_RGB565	3	/* 565 16 bit RGB */
+#define VIDEO_PALETTE_RGB24	4	/* 24bit RGB */
+#define VIDEO_PALETTE_RGB32	5	/* 32bit RGB */
+#define VIDEO_PALETTE_RGB555	6	/* 555 15bit RGB */
+#define VIDEO_PALETTE_YUV422	7	/* YUV422 capture */
+#define VIDEO_PALETTE_YUYV	8
+#define VIDEO_PALETTE_UYVY	9	/* The great thing about standards is ... */
+#define VIDEO_PALETTE_YUV420	10
+#define VIDEO_PALETTE_YUV411	11	/* YUV411 capture */
+#define VIDEO_PALETTE_RAW	12	/* RAW capture (BT848) */
+#define VIDEO_PALETTE_YUV422P	13	/* YUV 4:2:2 Planar */
+#define VIDEO_PALETTE_YUV411P	14	/* YUV 4:1:1 Planar */
+#define VIDEO_PALETTE_YUV420P	15	/* YUV 4:2:0 Planar */
+#define VIDEO_PALETTE_YUV410P	16	/* YUV 4:1:0 Planar */
+#define VIDEO_PALETTE_PLANAR	13	/* start of planar entries */
+#define VIDEO_PALETTE_COMPONENT 7	/* start of component entries */
+};
+
+struct video_audio
+{
+	int	audio;		/* Audio channel */
+	__u16	volume;		/* If settable */
+	__u16	bass, treble;
+	__u32	flags;
+#define VIDEO_AUDIO_MUTE	1
+#define VIDEO_AUDIO_MUTABLE	2
+#define VIDEO_AUDIO_VOLUME	4
+#define VIDEO_AUDIO_BASS	8
+#define VIDEO_AUDIO_TREBLE	16
+#define VIDEO_AUDIO_BALANCE	32
+	char    name[16];
+#define VIDEO_SOUND_MONO	1
+#define VIDEO_SOUND_STEREO	2
+#define VIDEO_SOUND_LANG1	4
+#define VIDEO_SOUND_LANG2	8
+	__u16   mode;
+	__u16	balance;	/* Stereo balance */
+	__u16	step;		/* Step actual volume uses */
+};
+
+struct video_clip
+{
+	__s32	x,y;
+	__s32	width, height;
+	struct	video_clip *next;	/* For user use/driver use only */
+};
+
+struct video_window
+{
+	__u32	x,y;			/* Position of window */
+	__u32	width,height;		/* Its size */
+	__u32	chromakey;
+	__u32	flags;
+	struct	video_clip  *clips;	/* Set only */
+	int	clipcount;
+#define VIDEO_WINDOW_INTERLACE	1
+#define VIDEO_WINDOW_CHROMAKEY	16	/* Overlay by chromakey */
+#define VIDEO_CLIP_BITMAP	-1
+/* bitmap is 1024x625, a '1' bit represents a clipped pixel */
+#define VIDEO_CLIPMAP_SIZE	(128 * 625)
+};
+
+struct video_capture
+{
+	__u32 	x,y;			/* Offsets into image */
+	__u32	width, height;		/* Area to capture */
+	__u16	decimation;		/* Decimation divider */
+	__u16	flags;			/* Flags for capture */
+#define VIDEO_CAPTURE_ODD		0	/* Temporal */
+#define VIDEO_CAPTURE_EVEN		1
+};
+
+struct video_buffer
+{
+	void	*base;
+	int	height,width;
+	int	depth;
+	int	bytesperline;
+};
+
+struct video_mmap
+{
+	unsigned	int frame;		/* Frame (0 - n) for double buffer */
+	int		height,width;
+	unsigned	int format;		/* should be VIDEO_PALETTE_* */
+};
+
+struct video_key
+{
+	__u8	key[8];
+	__u32	flags;
+};
+
+struct video_mbuf
+{
+	int	size;		/* Total memory to map */
+	int	frames;		/* Frames */
+	int	offsets[VIDEO_MAX_FRAME];
+};
+
+#define 	VIDEO_NO_UNIT	(-1)
+
+struct video_unit
+{
+	int 	video;		/* Video minor */
+	int	vbi;		/* VBI minor */
+	int	radio;		/* Radio minor */
+	int	audio;		/* Audio minor */
+	int	teletext;	/* Teletext minor */
+};
+
+struct vbi_format {
+	__u32	sampling_rate;	/* in Hz */
+	__u32	samples_per_line;
+	__u32	sample_format;	/* VIDEO_PALETTE_RAW only (1 byte) */
+	__s32	start[2];	/* starting line for each frame */
+	__u32	count[2];	/* count of lines for each frame */
+	__u32	flags;
+#define	VBI_UNSYNC	1	/* can distingues between top/bottom field */
+#define	VBI_INTERLACED	2	/* lines are interlaced */
+};
+
+/* video_info is biased towards hardware mpeg encode/decode */
+/* but it could apply generically to any hardware compressor/decompressor */
+struct video_info
+{
+	__u32	frame_count;	/* frames output since decode/encode began */
+	__u32	h_size;		/* current unscaled horizontal size */
+	__u32	v_size;		/* current unscaled veritcal size */
+	__u32	smpte_timecode;	/* current SMPTE timecode (for current GOP) */
+	__u32	picture_type;	/* current picture type */
+	__u32	temporal_reference;	/* current temporal reference */
+	__u8	user_data[256];	/* user data last found in compressed stream */
+	/* user_data[0] contains user data flags, user_data[1] has count */
+};
+
+/* generic structure for setting playback modes */
+struct video_play_mode
+{
+	int	mode;
+	int	p1;
+	int	p2;
+};
+
+/* for loading microcode / fpga programming */
+struct video_code
+{
+	char	loadwhat[16];	/* name or tag of file being passed */
+	int	datasize;
+	__u8	*data;
+};
+
+#define VIDIOCGCAP		_IOR('v',1,struct video_capability)	/* Get capabilities */
+#define VIDIOCGCHAN		_IOWR('v',2,struct video_channel)	/* Get channel info (sources) */
+#define VIDIOCSCHAN		_IOW('v',3,struct video_channel)	/* Set channel 	*/
+#define VIDIOCGTUNER		_IOWR('v',4,struct video_tuner)		/* Get tuner abilities */
+#define VIDIOCSTUNER		_IOW('v',5,struct video_tuner)		/* Tune the tuner for the current channel */
+#define VIDIOCGPICT		_IOR('v',6,struct video_picture)	/* Get picture properties */
+#define VIDIOCSPICT		_IOW('v',7,struct video_picture)	/* Set picture properties */
+#define VIDIOCCAPTURE		_IOW('v',8,int)				/* Start, end capture */
+#define VIDIOCGWIN		_IOR('v',9, struct video_window)	/* Get the video overlay window */
+#define VIDIOCSWIN		_IOW('v',10, struct video_window)	/* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */
+#define VIDIOCGFBUF		_IOR('v',11, struct video_buffer)	/* Get frame buffer */
+#define VIDIOCSFBUF		_IOW('v',12, struct video_buffer)	/* Set frame buffer - root only */
+#define VIDIOCKEY		_IOR('v',13, struct video_key)		/* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */
+#define VIDIOCGFREQ		_IOR('v',14, unsigned long)		/* Set tuner */
+#define VIDIOCSFREQ		_IOW('v',15, unsigned long)		/* Set tuner */
+#define VIDIOCGAUDIO		_IOR('v',16, struct video_audio)	/* Get audio info */
+#define VIDIOCSAUDIO		_IOW('v',17, struct video_audio)	/* Audio source, mute etc */
+#define VIDIOCSYNC		_IOW('v',18, int)			/* Sync with mmap grabbing */
+#define VIDIOCMCAPTURE		_IOW('v',19, struct video_mmap)		/* Grab frames */
+#define VIDIOCGMBUF		_IOR('v',20, struct video_mbuf)		/* Memory map buffer info */
+#define VIDIOCGUNIT		_IOR('v',21, struct video_unit)		/* Get attached units */
+#define VIDIOCGCAPTURE		_IOR('v',22, struct video_capture)	/* Get subcapture */
+#define VIDIOCSCAPTURE		_IOW('v',23, struct video_capture)	/* Set subcapture */
+#define VIDIOCSPLAYMODE		_IOW('v',24, struct video_play_mode)	/* Set output video mode/feature */
+#define VIDIOCSWRITEMODE	_IOW('v',25, int)			/* Set write mode */
+#define VIDIOCGPLAYINFO		_IOR('v',26, struct video_info)		/* Get current playback info from hardware */
+#define VIDIOCSMICROCODE	_IOW('v',27, struct video_code)		/* Load microcode into hardware */
+#define	VIDIOCGVBIFMT		_IOR('v',28, struct vbi_format)		/* Get VBI information */
+#define	VIDIOCSVBIFMT		_IOW('v',29, struct vbi_format)		/* Set VBI information */
+
+
+#define BASE_VIDIOCPRIVATE	192		/* 192-255 are private */
+
+/* VIDIOCSWRITEMODE */
+#define VID_WRITE_MPEG_AUD		0
+#define VID_WRITE_MPEG_VID		1
+#define VID_WRITE_OSD			2
+#define VID_WRITE_TTX			3
+#define VID_WRITE_CC			4
+#define VID_WRITE_MJPEG			5
+
+/* VIDIOCSPLAYMODE */
+#define VID_PLAY_VID_OUT_MODE		0
+	/* p1: = VIDEO_MODE_PAL, VIDEO_MODE_NTSC, etc ... */
+#define VID_PLAY_GENLOCK		1
+	/* p1: 0 = OFF, 1 = ON */
+	/* p2: GENLOCK FINE DELAY value */
+#define VID_PLAY_NORMAL			2
+#define VID_PLAY_PAUSE			3
+#define VID_PLAY_SINGLE_FRAME		4
+#define VID_PLAY_FAST_FORWARD		5
+#define VID_PLAY_SLOW_MOTION		6
+#define VID_PLAY_IMMEDIATE_NORMAL	7
+#define VID_PLAY_SWITCH_CHANNELS	8
+#define VID_PLAY_FREEZE_FRAME		9
+#define VID_PLAY_STILL_MODE		10
+#define VID_PLAY_MASTER_MODE		11
+	/* p1: see below */
+#define		VID_PLAY_MASTER_NONE	1
+#define		VID_PLAY_MASTER_VIDEO	2
+#define		VID_PLAY_MASTER_AUDIO	3
+#define VID_PLAY_ACTIVE_SCANLINES	12
+	/* p1 = first active; p2 = last active */
+#define VID_PLAY_RESET			13
+#define VID_PLAY_END_MARK		14
+
+
+
+#define VID_HARDWARE_BT848	1
+#define VID_HARDWARE_QCAM_BW	2
+#define VID_HARDWARE_PMS	3
+#define VID_HARDWARE_QCAM_C	4
+#define VID_HARDWARE_PSEUDO	5
+#define VID_HARDWARE_SAA5249	6
+#define VID_HARDWARE_AZTECH	7
+#define VID_HARDWARE_SF16MI	8
+#define VID_HARDWARE_RTRACK	9
+#define VID_HARDWARE_ZOLTRIX	10
+#define VID_HARDWARE_SAA7146    11
+#define VID_HARDWARE_VIDEUM	12	/* Reserved for Winnov videum */
+#define VID_HARDWARE_RTRACK2	13
+#define VID_HARDWARE_PERMEDIA2	14	/* Reserved for Permedia2 */
+#define VID_HARDWARE_RIVA128	15	/* Reserved for RIVA 128 */
+#define VID_HARDWARE_PLANB	16	/* PowerMac motherboard video-in */
+#define VID_HARDWARE_BROADWAY	17	/* Broadway project */
+#define VID_HARDWARE_GEMTEK	18
+#define VID_HARDWARE_TYPHOON	19
+#define VID_HARDWARE_VINO	20	/* SGI Indy Vino */
+#define VID_HARDWARE_CADET	21	/* Cadet radio */
+#define VID_HARDWARE_TRUST	22	/* Trust FM Radio */
+#define VID_HARDWARE_TERRATEC	23	/* TerraTec ActiveRadio */
+#define VID_HARDWARE_CPIA	24
+#define VID_HARDWARE_ZR36120	25	/* Zoran ZR36120/ZR36125 */
+#define VID_HARDWARE_ZR36067	26	/* Zoran ZR36067/36060 */
+#define VID_HARDWARE_OV511	27
+#define VID_HARDWARE_ZR356700	28	/* Zoran 36700 series */
+#define VID_HARDWARE_W9966	29
+#define VID_HARDWARE_SE401	30	/* SE401 USB webcams */
+#define VID_HARDWARE_PWC	31	/* Philips webcams */
+#define VID_HARDWARE_MEYE	32	/* Sony Vaio MotionEye cameras */
+#define VID_HARDWARE_CPIA2	33
+#define VID_HARDWARE_VICAM      34
+#define VID_HARDWARE_SF16FMR2	35
+#define VID_HARDWARE_W9968CF	36
+#define VID_HARDWARE_SAA7114H   37
+#define VID_HARDWARE_SN9C102	38
+#define VID_HARDWARE_ARV	39
+#endif /* __LINUX_VIDEODEV_H */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/sys/contrib/dev/cxm/videodev2.h b/sys/contrib/dev/cxm/videodev2.h
new file mode 100644
index 0000000..6a62488
--- /dev/null
+++ b/sys/contrib/dev/cxm/videodev2.h
@@ -0,0 +1,1145 @@
+#ifndef __LINUX_VIDEODEV2_H
+#define __LINUX_VIDEODEV2_H
+/*
+ *	Video for Linux Two
+ *
+ *	Header file for v4l or V4L2 drivers and applications, for
+ *	Linux kernels 2.2.x or 2.4.x.
+ *
+ *	v4l2 documentation.
+ *
+ *	Author: Bill Dirks <bdirks@pacbell.net>
+ *		Justin Schoeman
+ *		et al.
+ */
+
+#define OBSOLETE_OWNER 1 /* It will be removed for 2.6.15 */
+#define HAVE_V4L2 1
+
+/*
+ * Common stuff for both V4L1 and V4L2
+ * Moved from videodev.h
+ */
+
+#define VIDEO_MAX_FRAME               32
+
+#define VID_TYPE_CAPTURE	1	/* Can capture */
+#define VID_TYPE_TUNER		2	/* Can tune */
+#define VID_TYPE_TELETEXT	4	/* Does teletext */
+#define VID_TYPE_OVERLAY	8	/* Overlay onto frame buffer */
+#define VID_TYPE_CHROMAKEY	16	/* Overlay by chromakey */
+#define VID_TYPE_CLIPPING	32	/* Can clip */
+#define VID_TYPE_FRAMERAM	64	/* Uses the frame buffer memory */
+#define VID_TYPE_SCALES		128	/* Scalable */
+#define VID_TYPE_MONOCHROME	256	/* Monochrome only */
+#define VID_TYPE_SUBCAPTURE	512	/* Can capture subareas of the image */
+#define VID_TYPE_MPEG_DECODER	1024	/* Can decode MPEG streams */
+#define VID_TYPE_MPEG_ENCODER	2048	/* Can encode MPEG streams */
+#define VID_TYPE_MJPEG_DECODER	4096	/* Can decode MJPEG streams */
+#define VID_TYPE_MJPEG_ENCODER	8192	/* Can encode MJPEG streams */
+
+#ifdef __KERNEL__
+
+#define VFL_TYPE_GRABBER	0
+#define VFL_TYPE_VBI		1
+#define VFL_TYPE_RADIO		2
+#define VFL_TYPE_VTX		3
+
+struct video_device
+{
+	/* device info */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	struct device *dev;
+#endif
+	char name[32];
+	int type;       /* v4l1 */
+	int type2;      /* v4l2 */
+	int hardware;
+	int minor;
+
+	/* device ops + callbacks */
+	struct file_operations *fops;
+	void (*release)(struct video_device *vfd);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+	/* old, obsolete interface -- dropped in 2.5.x, don't use it */
+	int (*open)(struct video_device *, int mode);
+	void (*close)(struct video_device *);
+	long (*read)(struct video_device *, char *, unsigned long, int noblock);
+	long (*write)(struct video_device *, const char *, unsigned long, int noblock);
+	unsigned int (*poll)(struct video_device *, struct file *, poll_table *);
+	int (*ioctl)(struct video_device *, unsigned int , void *);
+	int (*mmap)(struct video_device *, const char *, unsigned long);
+	int (*initialize)(struct video_device *);
+#endif
+
+#if OBSOLETE_OWNER /* to be removed in 2.6.15 */
+	/* obsolete -- fops->owner is used instead */
+	struct module *owner;
+	/* dev->driver_data will be used instead some day.
+	 * Use the video_{get|set}_drvdata() helper functions,
+	 * so the switch over will be transparent for you.
+	 * Or use {pci|usb}_{get|set}_drvdata() directly. */
+	void *priv;
+#endif
+
+	/* for videodev.c intenal usage -- please don't touch */
+	int users;                     /* video_exclusive_{open|close} ... */
+	struct semaphore lock;         /* ... helper function uses these   */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,69)
+	devfs_handle_t devfs_handle;   /* devfs */
+#else
+	char devfs_name[64];           /* devfs */
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+	struct class_device class_dev; /* sysfs */
+#endif
+};
+
+#define VIDEO_MAJOR	81
+
+extern int video_register_device(struct video_device *, int type, int nr);
+extern void video_unregister_device(struct video_device *);
+extern int video_usercopy(struct inode *inode, struct file *file,
+			  unsigned int cmd, unsigned long arg,
+			  int (*func)(struct inode *inode, struct file *file,
+				      unsigned int cmd, void *arg));
+
+/* helper functions to alloc / release struct video_device, the
+   later can be used for video_device->release() */
+struct video_device *video_device_alloc(void);
+void video_device_release(struct video_device *vfd);
+
+#endif
+
+/*
+ *	M I S C E L L A N E O U S
+ */
+
+/*  Four-character-code (FOURCC) */
+#define v4l2_fourcc(a,b,c,d)\
+	(((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24))
+
+/*
+ *	E N U M S
+ */
+enum v4l2_field {
+	V4L2_FIELD_ANY        = 0, /* driver can choose from none,
+				      top, bottom, interlaced
+				      depending on whatever it thinks
+				      is approximate ... */
+	V4L2_FIELD_NONE       = 1, /* this device has no fields ... */
+	V4L2_FIELD_TOP        = 2, /* top field only */
+	V4L2_FIELD_BOTTOM     = 3, /* bottom field only */
+	V4L2_FIELD_INTERLACED = 4, /* both fields interlaced */
+	V4L2_FIELD_SEQ_TB     = 5, /* both fields sequential into one
+				      buffer, top-bottom order */
+	V4L2_FIELD_SEQ_BT     = 6, /* same as above + bottom-top order */
+	V4L2_FIELD_ALTERNATE  = 7, /* both fields alternating into
+				      separate buffers */
+};
+#define V4L2_FIELD_HAS_TOP(field)	\
+	((field) == V4L2_FIELD_TOP 	||\
+	 (field) == V4L2_FIELD_INTERLACED ||\
+	 (field) == V4L2_FIELD_SEQ_TB	||\
+	 (field) == V4L2_FIELD_SEQ_BT)
+#define V4L2_FIELD_HAS_BOTTOM(field)	\
+	((field) == V4L2_FIELD_BOTTOM 	||\
+	 (field) == V4L2_FIELD_INTERLACED ||\
+	 (field) == V4L2_FIELD_SEQ_TB	||\
+	 (field) == V4L2_FIELD_SEQ_BT)
+#define V4L2_FIELD_HAS_BOTH(field)	\
+	((field) == V4L2_FIELD_INTERLACED ||\
+	 (field) == V4L2_FIELD_SEQ_TB	||\
+	 (field) == V4L2_FIELD_SEQ_BT)
+
+enum v4l2_buf_type {
+	V4L2_BUF_TYPE_VIDEO_CAPTURE      = 1,
+	V4L2_BUF_TYPE_VIDEO_OUTPUT       = 2,
+	V4L2_BUF_TYPE_VIDEO_OVERLAY      = 3,
+	V4L2_BUF_TYPE_VBI_CAPTURE        = 4,
+	V4L2_BUF_TYPE_VBI_OUTPUT         = 5,
+#if 1 /*KEEP*/
+	/* Experimental Sliced VBI */
+	V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6,
+	V4L2_BUF_TYPE_SLICED_VBI_OUTPUT  = 7,
+#endif
+	V4L2_BUF_TYPE_PRIVATE            = 0x80,
+};
+
+enum v4l2_ctrl_type {
+	V4L2_CTRL_TYPE_INTEGER	     = 1,
+	V4L2_CTRL_TYPE_BOOLEAN	     = 2,
+	V4L2_CTRL_TYPE_MENU	     = 3,
+	V4L2_CTRL_TYPE_BUTTON	     = 4,
+};
+
+enum v4l2_tuner_type {
+	V4L2_TUNER_RADIO	     = 1,
+	V4L2_TUNER_ANALOG_TV	     = 2,
+	V4L2_TUNER_DIGITAL_TV	     = 3,
+};
+
+enum v4l2_memory {
+	V4L2_MEMORY_MMAP             = 1,
+	V4L2_MEMORY_USERPTR          = 2,
+	V4L2_MEMORY_OVERLAY          = 3,
+};
+
+enum v4l2_colorspace {
+	/* ITU-R 601 -- broadcast NTSC/PAL */
+	V4L2_COLORSPACE_SMPTE170M     = 1,
+
+	/* 1125-Line (US) HDTV */
+	V4L2_COLORSPACE_SMPTE240M     = 2,
+
+	/* HD and modern captures. */
+	V4L2_COLORSPACE_REC709        = 3,
+
+	/* broken BT878 extents (601, luma range 16-253 instead of 16-235) */
+	V4L2_COLORSPACE_BT878         = 4,
+
+	/* These should be useful.  Assume 601 extents. */
+	V4L2_COLORSPACE_470_SYSTEM_M  = 5,
+	V4L2_COLORSPACE_470_SYSTEM_BG = 6,
+
+	/* I know there will be cameras that send this.  So, this is
+	 * unspecified chromaticities and full 0-255 on each of the
+	 * Y'CbCr components
+	 */
+	V4L2_COLORSPACE_JPEG          = 7,
+
+	/* For RGB colourspaces, this is probably a good start. */
+	V4L2_COLORSPACE_SRGB          = 8,
+};
+
+enum v4l2_priority {
+	V4L2_PRIORITY_UNSET       = 0,  /* not initialized */
+	V4L2_PRIORITY_BACKGROUND  = 1,
+	V4L2_PRIORITY_INTERACTIVE = 2,
+	V4L2_PRIORITY_RECORD      = 3,
+	V4L2_PRIORITY_DEFAULT     = V4L2_PRIORITY_INTERACTIVE,
+};
+
+struct v4l2_rect {
+	__s32   left;
+	__s32   top;
+	__s32   width;
+	__s32   height;
+};
+
+struct v4l2_fract {
+	__u32   numerator;
+	__u32   denominator;
+};
+
+/*
+ *	D R I V E R   C A P A B I L I T I E S
+ */
+struct v4l2_capability
+{
+	__u8	driver[16];	/* i.e. "bttv" */
+	__u8	card[32];	/* i.e. "Hauppauge WinTV" */
+	__u8	bus_info[32];	/* "PCI:" + pci_name(pci_dev) */
+	__u32   version;        /* should use KERNEL_VERSION() */
+	__u32	capabilities;	/* Device capabilities */
+	__u32	reserved[4];
+};
+
+/* Values for 'capabilities' field */
+#define V4L2_CAP_VIDEO_CAPTURE		0x00000001  /* Is a video capture device */
+#define V4L2_CAP_VIDEO_OUTPUT		0x00000002  /* Is a video output device */
+#define V4L2_CAP_VIDEO_OVERLAY		0x00000004  /* Can do video overlay */
+#define V4L2_CAP_VBI_CAPTURE		0x00000010  /* Is a raw VBI capture device */
+#define V4L2_CAP_VBI_OUTPUT		0x00000020  /* Is a raw VBI output device */
+#if 1 /*KEEP*/
+#define V4L2_CAP_SLICED_VBI_CAPTURE	0x00000040  /* Is a sliced VBI capture device */
+#define V4L2_CAP_SLICED_VBI_OUTPUT	0x00000080  /* Is a sliced VBI output device */
+#endif
+#define V4L2_CAP_RDS_CAPTURE		0x00000100  /* RDS data capture */
+
+#define V4L2_CAP_TUNER			0x00010000  /* has a tuner */
+#define V4L2_CAP_AUDIO			0x00020000  /* has audio support */
+#define V4L2_CAP_RADIO			0x00040000  /* is a radio device */
+
+#define V4L2_CAP_READWRITE              0x01000000  /* read/write systemcalls */
+#define V4L2_CAP_ASYNCIO                0x02000000  /* async I/O */
+#define V4L2_CAP_STREAMING              0x04000000  /* streaming I/O ioctls */
+
+/*
+ *	V I D E O   I M A G E   F O R M A T
+ */
+
+struct v4l2_pix_format
+{
+	__u32         		width;
+	__u32			height;
+	__u32			pixelformat;
+	enum v4l2_field  	field;
+	__u32            	bytesperline;	/* for padding, zero if unused */
+	__u32          		sizeimage;
+	enum v4l2_colorspace	colorspace;
+	__u32			priv;		/* private data, depends on pixelformat */
+};
+
+/*           Pixel format    FOURCC                  depth  Description   */
+#define V4L2_PIX_FMT_RGB332  v4l2_fourcc('R','G','B','1') /*  8  RGB-3-3-2     */
+#define V4L2_PIX_FMT_RGB555  v4l2_fourcc('R','G','B','O') /* 16  RGB-5-5-5     */
+#define V4L2_PIX_FMT_RGB565  v4l2_fourcc('R','G','B','P') /* 16  RGB-5-6-5     */
+#define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R','G','B','Q') /* 16  RGB-5-5-5 BE  */
+#define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R','G','B','R') /* 16  RGB-5-6-5 BE  */
+#define V4L2_PIX_FMT_BGR24   v4l2_fourcc('B','G','R','3') /* 24  BGR-8-8-8     */
+#define V4L2_PIX_FMT_RGB24   v4l2_fourcc('R','G','B','3') /* 24  RGB-8-8-8     */
+#define V4L2_PIX_FMT_BGR32   v4l2_fourcc('B','G','R','4') /* 32  BGR-8-8-8-8   */
+#define V4L2_PIX_FMT_RGB32   v4l2_fourcc('R','G','B','4') /* 32  RGB-8-8-8-8   */
+#define V4L2_PIX_FMT_GREY    v4l2_fourcc('G','R','E','Y') /*  8  Greyscale     */
+#define V4L2_PIX_FMT_YVU410  v4l2_fourcc('Y','V','U','9') /*  9  YVU 4:1:0     */
+#define V4L2_PIX_FMT_YVU420  v4l2_fourcc('Y','V','1','2') /* 12  YVU 4:2:0     */
+#define V4L2_PIX_FMT_YUYV    v4l2_fourcc('Y','U','Y','V') /* 16  YUV 4:2:2     */
+#define V4L2_PIX_FMT_UYVY    v4l2_fourcc('U','Y','V','Y') /* 16  YUV 4:2:2     */
+#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4','2','2','P') /* 16  YVU422 planar */
+#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4','1','1','P') /* 16  YVU411 planar */
+#define V4L2_PIX_FMT_Y41P    v4l2_fourcc('Y','4','1','P') /* 12  YUV 4:1:1     */
+
+/* two planes -- one Y, one Cr + Cb interleaved  */
+#define V4L2_PIX_FMT_NV12    v4l2_fourcc('N','V','1','2') /* 12  Y/CbCr 4:2:0  */
+#define V4L2_PIX_FMT_NV21    v4l2_fourcc('N','V','2','1') /* 12  Y/CrCb 4:2:0  */
+
+/*  The following formats are not defined in the V4L2 specification */
+#define V4L2_PIX_FMT_YUV410  v4l2_fourcc('Y','U','V','9') /*  9  YUV 4:1:0     */
+#define V4L2_PIX_FMT_YUV420  v4l2_fourcc('Y','U','1','2') /* 12  YUV 4:2:0     */
+#define V4L2_PIX_FMT_YYUV    v4l2_fourcc('Y','Y','U','V') /* 16  YUV 4:2:2     */
+#define V4L2_PIX_FMT_HI240   v4l2_fourcc('H','I','2','4') /*  8  8-bit color   */
+
+#define V4L2_PIX_FMT_SBGGR8  v4l2_fourcc('B','A','8','1') /*  8  BGBG.. GRGR.. */
+
+/* compressed formats */
+#define V4L2_PIX_FMT_MJPEG    v4l2_fourcc('M','J','P','G') /* Motion-JPEG   */
+#define V4L2_PIX_FMT_JPEG     v4l2_fourcc('J','P','E','G') /* JFIF JPEG     */
+#define V4L2_PIX_FMT_DV       v4l2_fourcc('d','v','s','d') /* 1394          */
+#define V4L2_PIX_FMT_MPEG     v4l2_fourcc('M','P','E','G') /* MPEG          */
+
+/*  Vendor-specific formats   */
+#define V4L2_PIX_FMT_WNVA     v4l2_fourcc('W','N','V','A') /* Winnov hw compress */
+#define V4L2_PIX_FMT_SN9C10X  v4l2_fourcc('S','9','1','0') /* SN9C10x compression */
+#define V4L2_PIX_FMT_PWC1     v4l2_fourcc('P','W','C','1') /* pwc older webcam */
+#define V4L2_PIX_FMT_PWC2     v4l2_fourcc('P','W','C','2') /* pwc newer webcam */
+
+/*
+ *	F O R M A T   E N U M E R A T I O N
+ */
+struct v4l2_fmtdesc
+{
+	__u32		    index;             /* Format number      */
+	enum v4l2_buf_type  type;              /* buffer type        */
+	__u32               flags;
+	__u8		    description[32];   /* Description string */
+	__u32		    pixelformat;       /* Format fourcc      */
+	__u32		    reserved[4];
+};
+
+#define V4L2_FMT_FLAG_COMPRESSED 0x0001
+
+
+/*
+ *	T I M E C O D E
+ */
+struct v4l2_timecode
+{
+	__u32	type;
+	__u32	flags;
+	__u8	frames;
+	__u8	seconds;
+	__u8	minutes;
+	__u8	hours;
+	__u8	userbits[4];
+};
+
+/*  Type  */
+#define V4L2_TC_TYPE_24FPS		1
+#define V4L2_TC_TYPE_25FPS		2
+#define V4L2_TC_TYPE_30FPS		3
+#define V4L2_TC_TYPE_50FPS		4
+#define V4L2_TC_TYPE_60FPS		5
+
+/*  Flags  */
+#define V4L2_TC_FLAG_DROPFRAME		0x0001 /* "drop-frame" mode */
+#define V4L2_TC_FLAG_COLORFRAME		0x0002
+#define V4L2_TC_USERBITS_field		0x000C
+#define V4L2_TC_USERBITS_USERDEFINED	0x0000
+#define V4L2_TC_USERBITS_8BITCHARS	0x0008
+/* The above is based on SMPTE timecodes */
+
+
+#if 1
+/*
+ *	M P E G   C O M P R E S S I O N   P A R A M E T E R S
+ *
+ *  ### WARNING: this is still work-in-progress right now, most likely
+ *  ###          there will be some incompatible changes.
+ *
+ */
+
+
+enum v4l2_bitrate_mode {
+	V4L2_BITRATE_NONE = 0,	/* not specified */
+	V4L2_BITRATE_CBR,	/* constant bitrate */
+	V4L2_BITRATE_VBR,	/* variable bitrate */
+};
+struct v4l2_bitrate {
+	/* rates are specified in kbit/sec */
+	enum v4l2_bitrate_mode	mode;
+	__u32			min;
+	__u32			target;  /* use this one for CBR */
+	__u32			max;
+};
+
+enum v4l2_mpeg_streamtype {
+	V4L2_MPEG_SS_1,		/* MPEG-1 system stream */
+	V4L2_MPEG_PS_2,		/* MPEG-2 program stream */
+	V4L2_MPEG_TS_2,		/* MPEG-2 transport stream */
+	V4L2_MPEG_PS_DVD,      	/* MPEG-2 program stream with DVD header fixups */
+};
+enum v4l2_mpeg_audiotype {
+	V4L2_MPEG_AU_2_I,	/* MPEG-2 layer 1 */
+	V4L2_MPEG_AU_2_II,	/* MPEG-2 layer 2 */
+	V4L2_MPEG_AU_2_III,	/* MPEG-2 layer 3 */
+	V4L2_MPEG_AC3,		/* AC3 */
+	V4L2_MPEG_LPCM,		/* LPCM */
+};
+enum v4l2_mpeg_videotype {
+	V4L2_MPEG_VI_1,		/* MPEG-1 */
+	V4L2_MPEG_VI_2,		/* MPEG-2 */
+};
+enum v4l2_mpeg_aspectratio {
+	V4L2_MPEG_ASPECT_SQUARE = 1,   /* square pixel */
+	V4L2_MPEG_ASPECT_4_3    = 2,   /*  4 : 3       */
+	V4L2_MPEG_ASPECT_16_9   = 3,   /* 16 : 9       */
+	V4L2_MPEG_ASPECT_1_221  = 4,   /*  1 : 2,21    */
+};
+
+struct v4l2_mpeg_compression {
+	/* general */
+	enum v4l2_mpeg_streamtype	st_type;
+	struct v4l2_bitrate		st_bitrate;
+
+	/* transport streams */
+	__u16				ts_pid_pmt;
+	__u16				ts_pid_audio;
+	__u16				ts_pid_video;
+	__u16				ts_pid_pcr;
+
+	/* program stream */
+	__u16				ps_size;
+	__u16				reserved_1;    /* align */
+
+	/* audio */
+	enum v4l2_mpeg_audiotype	au_type;
+	struct v4l2_bitrate		au_bitrate;
+	__u32				au_sample_rate;
+	__u8                            au_pesid;
+	__u8                            reserved_2[3]; /* align */
+
+	/* video */
+	enum v4l2_mpeg_videotype	vi_type;
+	enum v4l2_mpeg_aspectratio	vi_aspect_ratio;
+	struct v4l2_bitrate		vi_bitrate;
+	__u32				vi_frame_rate;
+	__u16				vi_frames_per_gop;
+	__u16				vi_bframes_count;
+	__u8                            vi_pesid;
+	__u8                            reserved_3[3]; /* align */
+
+	/* misc flags */
+	__u32                           closed_gops:1;
+	__u32                           pulldown:1;
+	__u32                           reserved_4:30; /* align */
+
+	/* I don't expect the above being perfect yet ;) */
+	__u32				reserved_5[8];
+};
+#endif
+
+struct v4l2_jpegcompression
+{
+	int quality;
+
+	int  APPn;              /* Number of APP segment to be written,
+				 * must be 0..15 */
+	int  APP_len;           /* Length of data in JPEG APPn segment */
+	char APP_data[60];      /* Data in the JPEG APPn segment. */
+
+	int  COM_len;           /* Length of data in JPEG COM segment */
+	char COM_data[60];      /* Data in JPEG COM segment */
+
+	__u32 jpeg_markers;     /* Which markers should go into the JPEG
+				 * output. Unless you exactly know what
+				 * you do, leave them untouched.
+				 * Inluding less markers will make the
+				 * resulting code smaller, but there will
+				 * be fewer aplications which can read it.
+				 * The presence of the APP and COM marker
+				 * is influenced by APP_len and COM_len
+				 * ONLY, not by this property! */
+
+#define V4L2_JPEG_MARKER_DHT (1<<3)    /* Define Huffman Tables */
+#define V4L2_JPEG_MARKER_DQT (1<<4)    /* Define Quantization Tables */
+#define V4L2_JPEG_MARKER_DRI (1<<5)    /* Define Restart Interval */
+#define V4L2_JPEG_MARKER_COM (1<<6)    /* Comment segment */
+#define V4L2_JPEG_MARKER_APP (1<<7)    /* App segment, driver will
+					* allways use APP0 */
+};
+
+
+/*
+ *	M E M O R Y - M A P P I N G   B U F F E R S
+ */
+struct v4l2_requestbuffers
+{
+	__u32			count;
+	enum v4l2_buf_type      type;
+	enum v4l2_memory        memory;
+	__u32			reserved[2];
+};
+
+struct v4l2_buffer
+{
+	__u32			index;
+	enum v4l2_buf_type      type;
+	__u32			bytesused;
+	__u32			flags;
+	enum v4l2_field		field;
+	struct timeval		timestamp;
+	struct v4l2_timecode	timecode;
+	__u32			sequence;
+
+	/* memory location */
+	enum v4l2_memory        memory;
+	union {
+		__u32           offset;
+		unsigned long   userptr;
+	} m;
+	__u32			length;
+	__u32			input;
+	__u32			reserved;
+};
+
+/*  Flags for 'flags' field */
+#define V4L2_BUF_FLAG_MAPPED	0x0001  /* Buffer is mapped (flag) */
+#define V4L2_BUF_FLAG_QUEUED	0x0002	/* Buffer is queued for processing */
+#define V4L2_BUF_FLAG_DONE	0x0004	/* Buffer is ready */
+#define V4L2_BUF_FLAG_KEYFRAME	0x0008	/* Image is a keyframe (I-frame) */
+#define V4L2_BUF_FLAG_PFRAME	0x0010	/* Image is a P-frame */
+#define V4L2_BUF_FLAG_BFRAME	0x0020	/* Image is a B-frame */
+#define V4L2_BUF_FLAG_TIMECODE	0x0100	/* timecode field is valid */
+#define V4L2_BUF_FLAG_INPUT     0x0200  /* input field is valid */
+
+/*
+ *	O V E R L A Y   P R E V I E W
+ */
+struct v4l2_framebuffer
+{
+	__u32			capability;
+	__u32			flags;
+/* FIXME: in theory we should pass something like PCI device + memory
+ * region + offset instead of some physical address */
+	void*                   base;
+	struct v4l2_pix_format	fmt;
+};
+/*  Flags for the 'capability' field. Read only */
+#define V4L2_FBUF_CAP_EXTERNOVERLAY	0x0001
+#define V4L2_FBUF_CAP_CHROMAKEY		0x0002
+#define V4L2_FBUF_CAP_LIST_CLIPPING     0x0004
+#define V4L2_FBUF_CAP_BITMAP_CLIPPING	0x0008
+/*  Flags for the 'flags' field. */
+#define V4L2_FBUF_FLAG_PRIMARY		0x0001
+#define V4L2_FBUF_FLAG_OVERLAY		0x0002
+#define V4L2_FBUF_FLAG_CHROMAKEY	0x0004
+
+struct v4l2_clip
+{
+	struct v4l2_rect        c;
+	struct v4l2_clip	*next;
+};
+
+struct v4l2_window
+{
+	struct v4l2_rect        w;
+	enum v4l2_field  	field;
+	__u32			chromakey;
+	struct v4l2_clip	 *clips;
+	__u32			clipcount;
+	void			 *bitmap;
+};
+
+
+/*
+ *	C A P T U R E   P A R A M E T E R S
+ */
+struct v4l2_captureparm
+{
+	__u32		   capability;	  /*  Supported modes */
+	__u32		   capturemode;	  /*  Current mode */
+	struct v4l2_fract  timeperframe;  /*  Time per frame in .1us units */
+	__u32		   extendedmode;  /*  Driver-specific extensions */
+	__u32              readbuffers;   /*  # of buffers for read */
+	__u32		   reserved[4];
+};
+/*  Flags for 'capability' and 'capturemode' fields */
+#define V4L2_MODE_HIGHQUALITY	0x0001	/*  High quality imaging mode */
+#define V4L2_CAP_TIMEPERFRAME	0x1000	/*  timeperframe field is supported */
+
+struct v4l2_outputparm
+{
+	__u32		   capability;	 /*  Supported modes */
+	__u32		   outputmode;	 /*  Current mode */
+	struct v4l2_fract  timeperframe; /*  Time per frame in seconds */
+	__u32		   extendedmode; /*  Driver-specific extensions */
+	__u32              writebuffers; /*  # of buffers for write */
+	__u32		   reserved[4];
+};
+
+/*
+ *	I N P U T   I M A G E   C R O P P I N G
+ */
+
+struct v4l2_cropcap {
+	enum v4l2_buf_type      type;
+	struct v4l2_rect        bounds;
+	struct v4l2_rect        defrect;
+	struct v4l2_fract       pixelaspect;
+};
+
+struct v4l2_crop {
+	enum v4l2_buf_type      type;
+	struct v4l2_rect        c;
+};
+
+/*
+ *      A N A L O G   V I D E O   S T A N D A R D
+ */
+
+typedef __u64 v4l2_std_id;
+
+/* one bit for each */
+#define V4L2_STD_PAL_B          ((v4l2_std_id)0x00000001)
+#define V4L2_STD_PAL_B1         ((v4l2_std_id)0x00000002)
+#define V4L2_STD_PAL_G          ((v4l2_std_id)0x00000004)
+#define V4L2_STD_PAL_H          ((v4l2_std_id)0x00000008)
+#define V4L2_STD_PAL_I          ((v4l2_std_id)0x00000010)
+#define V4L2_STD_PAL_D          ((v4l2_std_id)0x00000020)
+#define V4L2_STD_PAL_D1         ((v4l2_std_id)0x00000040)
+#define V4L2_STD_PAL_K          ((v4l2_std_id)0x00000080)
+
+#define V4L2_STD_PAL_M          ((v4l2_std_id)0x00000100)
+#define V4L2_STD_PAL_N          ((v4l2_std_id)0x00000200)
+#define V4L2_STD_PAL_Nc         ((v4l2_std_id)0x00000400)
+#define V4L2_STD_PAL_60         ((v4l2_std_id)0x00000800)
+
+#define V4L2_STD_NTSC_M         ((v4l2_std_id)0x00001000)
+#define V4L2_STD_NTSC_M_JP      ((v4l2_std_id)0x00002000)
+#define V4L2_STD_NTSC_443       ((v4l2_std_id)0x00004000)
+
+#define V4L2_STD_SECAM_B        ((v4l2_std_id)0x00010000)
+#define V4L2_STD_SECAM_D        ((v4l2_std_id)0x00020000)
+#define V4L2_STD_SECAM_G        ((v4l2_std_id)0x00040000)
+#define V4L2_STD_SECAM_H        ((v4l2_std_id)0x00080000)
+#define V4L2_STD_SECAM_K        ((v4l2_std_id)0x00100000)
+#define V4L2_STD_SECAM_K1       ((v4l2_std_id)0x00200000)
+#define V4L2_STD_SECAM_L        ((v4l2_std_id)0x00400000)
+#define V4L2_STD_SECAM_LC       ((v4l2_std_id)0x00800000)
+
+/* ATSC/HDTV */
+#define V4L2_STD_ATSC_8_VSB     ((v4l2_std_id)0x01000000)
+#define V4L2_STD_ATSC_16_VSB    ((v4l2_std_id)0x02000000)
+
+/* some common needed stuff */
+#define V4L2_STD_PAL_BG		(V4L2_STD_PAL_B		|\
+				 V4L2_STD_PAL_B1	|\
+				 V4L2_STD_PAL_G)
+#define V4L2_STD_PAL_DK		(V4L2_STD_PAL_D		|\
+				 V4L2_STD_PAL_D1	|\
+				 V4L2_STD_PAL_K)
+#define V4L2_STD_PAL		(V4L2_STD_PAL_BG	|\
+				 V4L2_STD_PAL_DK	|\
+				 V4L2_STD_PAL_H		|\
+				 V4L2_STD_PAL_I)
+#define V4L2_STD_NTSC           (V4L2_STD_NTSC_M	|\
+				 V4L2_STD_NTSC_M_JP)
+#define V4L2_STD_SECAM_DK      	(V4L2_STD_SECAM_D	|\
+				 V4L2_STD_SECAM_K	|\
+				 V4L2_STD_SECAM_K1)
+#define V4L2_STD_SECAM		(V4L2_STD_SECAM_B	|\
+				 V4L2_STD_SECAM_G	|\
+				 V4L2_STD_SECAM_H	|\
+				 V4L2_STD_SECAM_DK	|\
+				 V4L2_STD_SECAM_L)
+
+#define V4L2_STD_525_60		(V4L2_STD_PAL_M		|\
+				 V4L2_STD_PAL_60	|\
+				 V4L2_STD_NTSC		|\
+				 V4L2_STD_NTSC_443)
+#define V4L2_STD_625_50		(V4L2_STD_PAL		|\
+				 V4L2_STD_PAL_N		|\
+				 V4L2_STD_PAL_Nc	|\
+				 V4L2_STD_SECAM)
+#define V4L2_STD_ATSC           (V4L2_STD_ATSC_8_VSB    |\
+				 V4L2_STD_ATSC_16_VSB)
+
+#define V4L2_STD_UNKNOWN        0
+#define V4L2_STD_ALL            (V4L2_STD_525_60	|\
+				 V4L2_STD_625_50)
+
+struct v4l2_standard
+{
+	__u32		     index;
+	v4l2_std_id          id;
+	__u8		     name[24];
+	struct v4l2_fract    frameperiod; /* Frames, not fields */
+	__u32		     framelines;
+	__u32		     reserved[4];
+};
+
+
+/*
+ *	V I D E O   I N P U T S
+ */
+struct v4l2_input
+{
+	__u32	     index;		/*  Which input */
+	__u8	     name[32];		/*  Label */
+	__u32	     type;		/*  Type of input */
+	__u32	     audioset;		/*  Associated audios (bitfield) */
+	__u32        tuner;             /*  Associated tuner */
+	v4l2_std_id  std;
+	__u32	     status;
+	__u32	     reserved[4];
+};
+/*  Values for the 'type' field */
+#define V4L2_INPUT_TYPE_TUNER		1
+#define V4L2_INPUT_TYPE_CAMERA		2
+
+/* field 'status' - general */
+#define V4L2_IN_ST_NO_POWER    0x00000001  /* Attached device is off */
+#define V4L2_IN_ST_NO_SIGNAL   0x00000002
+#define V4L2_IN_ST_NO_COLOR    0x00000004
+
+/* field 'status' - analog */
+#define V4L2_IN_ST_NO_H_LOCK   0x00000100  /* No horizontal sync lock */
+#define V4L2_IN_ST_COLOR_KILL  0x00000200  /* Color killer is active */
+
+/* field 'status' - digital */
+#define V4L2_IN_ST_NO_SYNC     0x00010000  /* No synchronization lock */
+#define V4L2_IN_ST_NO_EQU      0x00020000  /* No equalizer lock */
+#define V4L2_IN_ST_NO_CARRIER  0x00040000  /* Carrier recovery failed */
+
+/* field 'status' - VCR and set-top box */
+#define V4L2_IN_ST_MACROVISION 0x01000000  /* Macrovision detected */
+#define V4L2_IN_ST_NO_ACCESS   0x02000000  /* Conditional access denied */
+#define V4L2_IN_ST_VTR         0x04000000  /* VTR time constant */
+
+/*
+ *	V I D E O   O U T P U T S
+ */
+struct v4l2_output
+{
+	__u32	     index;		/*  Which output */
+	__u8	     name[32];		/*  Label */
+	__u32	     type;		/*  Type of output */
+	__u32	     audioset;		/*  Associated audios (bitfield) */
+	__u32	     modulator;         /*  Associated modulator */
+	v4l2_std_id  std;
+	__u32	     reserved[4];
+};
+/*  Values for the 'type' field */
+#define V4L2_OUTPUT_TYPE_MODULATOR		1
+#define V4L2_OUTPUT_TYPE_ANALOG			2
+#define V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY	3
+
+/*
+ *	C O N T R O L S
+ */
+struct v4l2_control
+{
+	__u32		     id;
+	__s32		     value;
+};
+
+/*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
+struct v4l2_queryctrl
+{
+	__u32		     id;
+	enum v4l2_ctrl_type  type;
+	__u8		     name[32];	/* Whatever */
+	__s32		     minimum;	/* Note signedness */
+	__s32		     maximum;
+	__s32		     step;
+	__s32		     default_value;
+	__u32                flags;
+	__u32		     reserved[2];
+};
+
+/*  Used in the VIDIOC_QUERYMENU ioctl for querying menu items */
+struct v4l2_querymenu
+{
+	__u32		id;
+	__u32		index;
+	__u8		name[32];	/* Whatever */
+	__u32		reserved;
+};
+
+/*  Control flags  */
+#define V4L2_CTRL_FLAG_DISABLED		0x0001
+#define V4L2_CTRL_FLAG_GRABBED		0x0002
+
+/*  Control IDs defined by V4L2 */
+#define V4L2_CID_BASE			0x00980900
+/*  IDs reserved for driver specific controls */
+#define V4L2_CID_PRIVATE_BASE		0x08000000
+
+#define V4L2_CID_BRIGHTNESS		(V4L2_CID_BASE+0)
+#define V4L2_CID_CONTRAST		(V4L2_CID_BASE+1)
+#define V4L2_CID_SATURATION		(V4L2_CID_BASE+2)
+#define V4L2_CID_HUE			(V4L2_CID_BASE+3)
+#define V4L2_CID_AUDIO_VOLUME		(V4L2_CID_BASE+5)
+#define V4L2_CID_AUDIO_BALANCE		(V4L2_CID_BASE+6)
+#define V4L2_CID_AUDIO_BASS		(V4L2_CID_BASE+7)
+#define V4L2_CID_AUDIO_TREBLE		(V4L2_CID_BASE+8)
+#define V4L2_CID_AUDIO_MUTE		(V4L2_CID_BASE+9)
+#define V4L2_CID_AUDIO_LOUDNESS		(V4L2_CID_BASE+10)
+#define V4L2_CID_BLACK_LEVEL		(V4L2_CID_BASE+11)
+#define V4L2_CID_AUTO_WHITE_BALANCE	(V4L2_CID_BASE+12)
+#define V4L2_CID_DO_WHITE_BALANCE	(V4L2_CID_BASE+13)
+#define V4L2_CID_RED_BALANCE		(V4L2_CID_BASE+14)
+#define V4L2_CID_BLUE_BALANCE		(V4L2_CID_BASE+15)
+#define V4L2_CID_GAMMA			(V4L2_CID_BASE+16)
+#define V4L2_CID_WHITENESS		(V4L2_CID_GAMMA) /* ? Not sure */
+#define V4L2_CID_EXPOSURE		(V4L2_CID_BASE+17)
+#define V4L2_CID_AUTOGAIN		(V4L2_CID_BASE+18)
+#define V4L2_CID_GAIN			(V4L2_CID_BASE+19)
+#define V4L2_CID_HFLIP			(V4L2_CID_BASE+20)
+#define V4L2_CID_VFLIP			(V4L2_CID_BASE+21)
+#define V4L2_CID_HCENTER		(V4L2_CID_BASE+22)
+#define V4L2_CID_VCENTER		(V4L2_CID_BASE+23)
+#define V4L2_CID_LASTP1			(V4L2_CID_BASE+24) /* last CID + 1 */
+
+/*
+ *	T U N I N G
+ */
+struct v4l2_tuner
+{
+	__u32                   index;
+	__u8			name[32];
+	enum v4l2_tuner_type    type;
+	__u32			capability;
+	__u32			rangelow;
+	__u32			rangehigh;
+	__u32			rxsubchans;
+	__u32			audmode;
+	__s32			signal;
+	__s32			afc;
+	__u32			reserved[4];
+};
+
+struct v4l2_modulator
+{
+	__u32			index;
+	__u8			name[32];
+	__u32			capability;
+	__u32			rangelow;
+	__u32			rangehigh;
+	__u32			txsubchans;
+	__u32			reserved[4];
+};
+
+/*  Flags for the 'capability' field */
+#define V4L2_TUNER_CAP_LOW		0x0001
+#define V4L2_TUNER_CAP_NORM		0x0002
+#define V4L2_TUNER_CAP_STEREO		0x0010
+#define V4L2_TUNER_CAP_LANG2		0x0020
+#define V4L2_TUNER_CAP_SAP		0x0020
+#define V4L2_TUNER_CAP_LANG1		0x0040
+
+/*  Flags for the 'rxsubchans' field */
+#define V4L2_TUNER_SUB_MONO		0x0001
+#define V4L2_TUNER_SUB_STEREO		0x0002
+#define V4L2_TUNER_SUB_LANG2		0x0004
+#define V4L2_TUNER_SUB_SAP		0x0004
+#define V4L2_TUNER_SUB_LANG1		0x0008
+
+/*  Values for the 'audmode' field */
+#define V4L2_TUNER_MODE_MONO		0x0000
+#define V4L2_TUNER_MODE_STEREO		0x0001
+#define V4L2_TUNER_MODE_LANG2		0x0002
+#define V4L2_TUNER_MODE_SAP		0x0002
+#define V4L2_TUNER_MODE_LANG1		0x0003
+
+struct v4l2_frequency
+{
+	__u32		      tuner;
+	enum v4l2_tuner_type  type;
+	__u32		      frequency;
+	__u32		      reserved[8];
+};
+
+/*
+ *	A U D I O
+ */
+struct v4l2_audio
+{
+	__u32	index;
+	__u8	name[32];
+	__u32	capability;
+	__u32	mode;
+	__u32	reserved[2];
+};
+/*  Flags for the 'capability' field */
+#define V4L2_AUDCAP_STEREO		0x00001
+#define V4L2_AUDCAP_AVL			0x00002
+
+/*  Flags for the 'mode' field */
+#define V4L2_AUDMODE_AVL		0x00001
+#define V4L2_AUDMODE_32BITS		0x00002
+
+struct v4l2_audioout
+{
+	__u32	index;
+	__u8	name[32];
+	__u32	capability;
+	__u32	mode;
+	__u32	reserved[2];
+};
+
+/*
+ *	D A T A   S E R V I C E S   ( V B I )
+ *
+ *	Data services API by Michael Schimek
+ */
+
+/* Raw VBI */
+
+struct v4l2_vbi_format
+{
+	__u32	sampling_rate;		/* in 1 Hz */
+	__u32	offset;
+	__u32	samples_per_line;
+	__u32	sample_format;		/* V4L2_PIX_FMT_* */
+	__s32	start[2];
+	__u32	count[2];
+	__u32	flags;			/* V4L2_VBI_* */
+	__u32	reserved[2];		/* must be zero */
+};
+
+/*  VBI flags  */
+#define V4L2_VBI_UNSYNC		(1<< 0)
+#define V4L2_VBI_INTERLACED	(1<< 1)
+
+#if 1 /*KEEP*/
+/* Sliced VBI
+ *
+ *    This implements is a proposal V4L2 API to allow SLICED VBI
+ * required for some hardware encoders. It should change without
+ * notice in the definitive implementation.
+ */
+
+struct v4l2_sliced_vbi_format
+{
+	__u16   service_set;
+	/* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field
+	   service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field
+				 (equals frame lines 313-336 for 625 line video
+				  standards, 263-286 for 525 line standards) */
+	__u16   service_lines[2][24];
+	__u32   io_size;
+	__u32   reserved[2];            /* must be zero */
+};
+
+#define V4L2_SLICED_TELETEXT_B          (0x0001)
+#define V4L2_SLICED_VPS                 (0x0400)
+#define V4L2_SLICED_CAPTION_525         (0x1000)
+#define V4L2_SLICED_WSS_625             (0x4000)
+
+#define V4L2_SLICED_VBI_525             (V4L2_SLICED_CAPTION_525)
+#define V4L2_SLICED_VBI_625             (V4L2_SLICED_TELETEXT_B | V4L2_SLICED_VPS | V4L2_SLICED_WSS_625)
+
+struct v4l2_sliced_vbi_cap
+{
+	__u16   service_set;
+	/* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field
+	   service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field
+				 (equals frame lines 313-336 for 625 line video
+				  standards, 263-286 for 525 line standards) */
+	__u16   service_lines[2][24];
+	__u32   reserved[4];    /* must be 0 */
+};
+
+struct v4l2_sliced_vbi_data
+{
+	__u32   id;
+	__u32   field;          /* 0: first field, 1: second field */
+	__u32   line;           /* 1-23 */
+	__u32   reserved;       /* must be 0 */
+	__u8    data[48];
+};
+#endif
+
+/*
+ *	A G G R E G A T E   S T R U C T U R E S
+ */
+
+/*	Stream data format
+ */
+struct v4l2_format
+{
+	enum v4l2_buf_type type;
+	union
+	{
+		struct v4l2_pix_format		pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
+		struct v4l2_window		win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
+		struct v4l2_vbi_format		vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */
+#if 1 /*KEEP*/
+		struct v4l2_sliced_vbi_format	sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
+#endif
+		__u8	raw_data[200];                   /* user-defined */
+	} fmt;
+};
+
+
+/*	Stream type-dependent parameters
+ */
+struct v4l2_streamparm
+{
+	enum v4l2_buf_type type;
+	union
+	{
+		struct v4l2_captureparm	capture;
+		struct v4l2_outputparm	output;
+		__u8	raw_data[200];  /* user-defined */
+	} parm;
+};
+
+
+
+/*
+ *	I O C T L   C O D E S   F O R   V I D E O   D E V I C E S
+ *
+ */
+#define VIDIOC_QUERYCAP		_IOR  ('V',  0, struct v4l2_capability)
+#define VIDIOC_RESERVED		_IO   ('V',  1)
+#define VIDIOC_ENUM_FMT         _IOWR ('V',  2, struct v4l2_fmtdesc)
+#define VIDIOC_G_FMT		_IOWR ('V',  4, struct v4l2_format)
+#define VIDIOC_S_FMT		_IOWR ('V',  5, struct v4l2_format)
+#if 1 /* experimental */
+#define VIDIOC_G_MPEGCOMP       _IOR  ('V',  6, struct v4l2_mpeg_compression)
+#define VIDIOC_S_MPEGCOMP     	_IOW  ('V',  7, struct v4l2_mpeg_compression)
+#endif
+#define VIDIOC_REQBUFS		_IOWR ('V',  8, struct v4l2_requestbuffers)
+#define VIDIOC_QUERYBUF		_IOWR ('V',  9, struct v4l2_buffer)
+#define VIDIOC_G_FBUF		_IOR  ('V', 10, struct v4l2_framebuffer)
+#define VIDIOC_S_FBUF		_IOW  ('V', 11, struct v4l2_framebuffer)
+#define VIDIOC_OVERLAY		_IOW  ('V', 14, int)
+#define VIDIOC_QBUF		_IOWR ('V', 15, struct v4l2_buffer)
+#define VIDIOC_DQBUF		_IOWR ('V', 17, struct v4l2_buffer)
+#define VIDIOC_STREAMON		_IOW  ('V', 18, int)
+#define VIDIOC_STREAMOFF	_IOW  ('V', 19, int)
+#define VIDIOC_G_PARM		_IOWR ('V', 21, struct v4l2_streamparm)
+#define VIDIOC_S_PARM		_IOWR ('V', 22, struct v4l2_streamparm)
+#define VIDIOC_G_STD		_IOR  ('V', 23, v4l2_std_id)
+#define VIDIOC_S_STD		_IOW  ('V', 24, v4l2_std_id)
+#define VIDIOC_ENUMSTD		_IOWR ('V', 25, struct v4l2_standard)
+#define VIDIOC_ENUMINPUT	_IOWR ('V', 26, struct v4l2_input)
+#define VIDIOC_G_CTRL		_IOWR ('V', 27, struct v4l2_control)
+#define VIDIOC_S_CTRL		_IOWR ('V', 28, struct v4l2_control)
+#define VIDIOC_G_TUNER		_IOWR ('V', 29, struct v4l2_tuner)
+#define VIDIOC_S_TUNER		_IOW  ('V', 30, struct v4l2_tuner)
+#define VIDIOC_G_AUDIO		_IOR  ('V', 33, struct v4l2_audio)
+#define VIDIOC_S_AUDIO		_IOW  ('V', 34, struct v4l2_audio)
+#define VIDIOC_QUERYCTRL	_IOWR ('V', 36, struct v4l2_queryctrl)
+#define VIDIOC_QUERYMENU	_IOWR ('V', 37, struct v4l2_querymenu)
+#define VIDIOC_G_INPUT		_IOR  ('V', 38, int)
+#define VIDIOC_S_INPUT		_IOWR ('V', 39, int)
+#define VIDIOC_G_OUTPUT		_IOR  ('V', 46, int)
+#define VIDIOC_S_OUTPUT		_IOWR ('V', 47, int)
+#define VIDIOC_ENUMOUTPUT	_IOWR ('V', 48, struct v4l2_output)
+#define VIDIOC_G_AUDOUT		_IOR  ('V', 49, struct v4l2_audioout)
+#define VIDIOC_S_AUDOUT		_IOW  ('V', 50, struct v4l2_audioout)
+#define VIDIOC_G_MODULATOR	_IOWR ('V', 54, struct v4l2_modulator)
+#define VIDIOC_S_MODULATOR	_IOW  ('V', 55, struct v4l2_modulator)
+#define VIDIOC_G_FREQUENCY	_IOWR ('V', 56, struct v4l2_frequency)
+#define VIDIOC_S_FREQUENCY	_IOW  ('V', 57, struct v4l2_frequency)
+#define VIDIOC_CROPCAP		_IOWR ('V', 58, struct v4l2_cropcap)
+#define VIDIOC_G_CROP		_IOWR ('V', 59, struct v4l2_crop)
+#define VIDIOC_S_CROP		_IOW  ('V', 60, struct v4l2_crop)
+#define VIDIOC_G_JPEGCOMP	_IOR  ('V', 61, struct v4l2_jpegcompression)
+#define VIDIOC_S_JPEGCOMP	_IOW  ('V', 62, struct v4l2_jpegcompression)
+#define VIDIOC_QUERYSTD      	_IOR  ('V', 63, v4l2_std_id)
+#define VIDIOC_TRY_FMT      	_IOWR ('V', 64, struct v4l2_format)
+#define VIDIOC_ENUMAUDIO	_IOWR ('V', 65, struct v4l2_audio)
+#define VIDIOC_ENUMAUDOUT	_IOWR ('V', 66, struct v4l2_audioout)
+#define VIDIOC_G_PRIORITY       _IOR  ('V', 67, enum v4l2_priority)
+#define VIDIOC_S_PRIORITY       _IOW  ('V', 68, enum v4l2_priority)
+#if 1 /*KEEP*/
+#define VIDIOC_G_SLICED_VBI_CAP _IOR  ('V', 69, struct v4l2_sliced_vbi_cap)
+#endif
+#define VIDIOC_LOG_STATUS       _IO   ('V', 70)
+
+/* for compatibility, will go away some day */
+#define VIDIOC_OVERLAY_OLD     	_IOWR ('V', 14, int)
+#define VIDIOC_S_PARM_OLD      	_IOW  ('V', 22, struct v4l2_streamparm)
+#define VIDIOC_S_CTRL_OLD      	_IOW  ('V', 28, struct v4l2_control)
+#define VIDIOC_G_AUDIO_OLD     	_IOWR ('V', 33, struct v4l2_audio)
+#define VIDIOC_G_AUDOUT_OLD    	_IOWR ('V', 49, struct v4l2_audioout)
+#define VIDIOC_CROPCAP_OLD     	_IOR  ('V', 58, struct v4l2_cropcap)
+
+#define BASE_VIDIOC_PRIVATE	192		/* 192-255 are private */
+
+
+#ifdef __KERNEL__
+/*
+ *
+ *	V 4 L 2   D R I V E R   H E L P E R   A P I
+ *
+ *	Some commonly needed functions for drivers (v4l2-common.o module)
+ */
+
+/*  Video standard functions  */
+extern unsigned int v4l2_video_std_fps(struct v4l2_standard *vs);
+extern int v4l2_video_std_construct(struct v4l2_standard *vs,
+				    int id, char *name);
+
+/* prority handling */
+struct v4l2_prio_state {
+	atomic_t prios[4];
+};
+int v4l2_prio_init(struct v4l2_prio_state *global);
+int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local,
+		     enum v4l2_priority new);
+int v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local);
+int v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority *local);
+enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global);
+int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority *local);
+
+/* names for fancy debug output */
+extern char *v4l2_field_names[];
+extern char *v4l2_type_names[];
+extern char *v4l2_ioctl_names[];
+
+/*  Compatibility layer interface  --  v4l1-compat module */
+typedef int (*v4l2_kioctl)(struct inode *inode, struct file *file,
+			   unsigned int cmd, void *arg);
+int v4l_compat_translate_ioctl(struct inode *inode, struct file *file,
+			       int cmd, void *arg, v4l2_kioctl driver_ioctl);
+
+/* 32 Bits compatibility layer for 64 bits processors */
+extern long v4l_compat_ioctl32(struct file *file, unsigned int cmd,
+				unsigned long arg);
+
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_VIDEODEV2_H */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/sys/dev/video/Makefile b/sys/dev/video/Makefile
index 813b4cf..aa8adb6 100644
--- a/sys/dev/video/Makefile
+++ b/sys/dev/video/Makefile
@@ -1,6 +1,6 @@
 # $DragonFly: src/sys/dev/video/Makefile,v 1.2 2003/10/23 17:32:52 dillon Exp $
 #
 
-SUBDIR=bktr fb ${MACHINE_ARCH}
+SUBDIR=bktr cxm fb ${MACHINE_ARCH}
 
 .include <bsd.subdir.mk>
diff --git a/sys/dev/video/cxm/Makefile b/sys/dev/video/cxm/Makefile
new file mode 100644
index 0000000..df4a667
--- /dev/null
+++ b/sys/dev/video/cxm/Makefile
@@ -0,0 +1,4 @@
+SUBDIR = cxm
+SUBDIR += cxm_iic
+
+.include <bsd.subdir.mk>
diff --git a/sys/dev/video/cxm/cxm.c b/sys/dev/video/cxm/cxm.c
new file mode 100644
index 0000000..7aa0d9c
--- /dev/null
+++ b/sys/dev/video/cxm/cxm.c
@@ -0,0 +1,3129 @@
+/*
+ * Copyright (c) 2003, 2004, 2005
+ *	John Wehle <john@feith.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by John Wehle.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Conexant MPEG-2 Codec driver. Supports the CX23415 / CX23416
+ * chips that are on the Hauppauge PVR-250 and PVR-350 video
+ * capture cards.  Currently only the encoder is supported.
+ *
+ * This driver was written using the invaluable information
+ * compiled by The IvyTV Project (ivtv.sourceforge.net).
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/mman.h>
+#include <sys/module.h>
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/signalvar.h>
+#include <sys/thread2.h>
+#include <sys/vnode.h>
+#include <sys/select.h>
+#include <sys/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <machine/clock.h>
+
+#include <dev/video/meteor/ioctl_meteor.h>
+#include <dev/video/bktr/ioctl_bt848.h>
+
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+
+#include <dev/video/cxm/cxm.h>
+
+#include <bus/iicbus/iiconf.h>
+
+#ifdef CXM_PVR150
+#include <contrib/dev/cxm/fbsd-compat.h>
+#include <contrib/dev/cxm/cx25840.h>
+#include <contrib/dev/cxm/tda9887.h>
+#include <contrib/dev/cxm/audiochip.h>
+#include <contrib/dev/cxm/tuner.h>
+#endif
+
+/*
+ * Various supported device vendors/types and their names.
+ */
+static struct cxm_dev cxm_devs[] = {
+	{ PCI_VENDOR_ICOMPRESSION, PCI_PRODUCT_ICOMPRESSION_ITVC15,
+		"Conexant iTVC15 MPEG Coder" },
+	{ PCI_VENDOR_ICOMPRESSION, PCI_PRODUCT_ICOMPRESSION_ITVC16,
+		"Conexant iTVC16 MPEG Coder" },
+	{ 0, 0, NULL }
+};
+
+
+static int	cxm_probe( device_t dev );
+static int	cxm_attach( device_t dev );
+static int	cxm_detach( device_t dev );
+static int	cxm_shutdown( device_t dev );
+static void	cxm_intr( void *arg );
+
+static void	cxm_child_detached( device_t dev, device_t child );
+static int	cxm_read_ivar( device_t bus, device_t dev,
+			       int index, uintptr_t* val);
+static int	cxm_write_ivar( device_t bus, device_t dev,
+				int index, uintptr_t val);
+
+
+static device_method_t cxm_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,         cxm_probe),
+	DEVMETHOD(device_attach,        cxm_attach),
+	DEVMETHOD(device_detach,        cxm_detach),
+	DEVMETHOD(device_shutdown,      cxm_shutdown),
+
+	/* bus interface */
+	DEVMETHOD(bus_child_detached,   cxm_child_detached),
+	DEVMETHOD(bus_print_child,      bus_generic_print_child),
+	DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
+	DEVMETHOD(bus_read_ivar,        cxm_read_ivar),
+	DEVMETHOD(bus_write_ivar,       cxm_write_ivar),
+
+	{ 0, 0 }
+};
+
+static driver_t cxm_driver = {
+	"cxm",
+	cxm_methods,
+	sizeof(struct cxm_softc),
+};
+
+static devclass_t cxm_devclass;
+
+static	d_open_t	cxm_open;
+static	d_close_t	cxm_close;
+static	d_read_t	cxm_read;
+static	d_ioctl_t	cxm_ioctl;
+static	d_poll_t	cxm_poll;
+
+#define CDEV_MAJOR 93
+
+static struct dev_ops cxm_ops = {
+	{ "cxm", CDEV_MAJOR, 0 },
+	.d_open =	cxm_open,
+	.d_close =	cxm_close,
+	.d_read =	cxm_read,
+	.d_ioctl =	cxm_ioctl,
+	.d_poll =	cxm_poll
+};
+
+MODULE_DEPEND(cxm, cxm_iic, 1, 1, 1);
+DRIVER_MODULE(cxm, pci, cxm_driver, cxm_devclass, 0, 0);
+
+
+static struct cxm_codec_audio_format codec_audio_formats[] = {
+	{ 44100, 0xb8 }, /* 44.1 Khz, MPEG-1 Layer II, 224 kb/s */
+	{ 48000, 0xe9 }  /* 48 Khz, MPEG-1 Layer II, 384 kb/s */
+};
+
+
+/*
+ * Various profiles.
+ */
+static struct cxm_codec_profile vcd_ntsc_profile = {
+	"MPEG-1 VideoCD NTSC video and MPEG audio",
+	CXM_FW_STREAM_TYPE_VCD,
+	30,
+	352, 240, 480,
+	{ 10, 12, 21 },
+	12,
+	0,
+	{ 1, 1150000, 0 },
+	{ 1, 15, 3},
+	/*
+	 * Spatial filter = Manual, Temporal filter = Manual
+	 * Median filter = Horizontal / Vertical
+	 * Spatial filter value = 1, Temporal filter value = 4
+	 */
+	{ 0, 3, 1, 4 },
+	44100
+};
+
+static struct cxm_codec_profile vcd_pal_profile = {
+	"MPEG-1 VideoCD PAL video and MPEG audio",
+	CXM_FW_STREAM_TYPE_VCD,
+	25,
+	352, 288, 576,
+	{ 6, 17, 22 },
+	8,
+	0,
+	{ 1, 1150000, 0 },
+	{ 1, 12, 3},
+	/*
+	 * Spatial filter = Manual, Temporal filter = Manual
+	 * Median filter = Horizontal / Vertical
+	 * Spatial filter value = 1, Temporal filter value = 4
+	 */
+	{ 0, 3, 1, 4 },
+	44100
+};
+
+static struct cxm_codec_profile svcd_ntsc_profile = {
+	"MPEG-2 SuperVCD NTSC video and MPEG audio",
+	CXM_FW_STREAM_TYPE_SVCD,
+	30,
+	480, 480, 480,
+	{ 10, 12, 21 },
+	2,
+	0,
+	/* 2.5 Mb/s peak limit to keep bbdmux followed by mplex -f 4 happy */
+	{ 0, 1150000, 2500000 },
+	{ 1, 15, 3},
+	/*
+	 * Spatial filter = Manual, Temporal filter = Manual
+	 * Median filter = Horizontal / Vertical
+	 * Spatial filter value = 1, Temporal filter value = 4
+	 */
+	{ 0, 3, 1, 4 },
+	44100
+};
+
+static struct cxm_codec_profile svcd_pal_profile = {
+	"MPEG-2 SuperVCD PAL video and MPEG audio",
+	CXM_FW_STREAM_TYPE_SVCD,
+	25,
+	480, 576, 576,
+	{ 6, 17, 22 },
+	2,
+	0,
+	/* 2.5 Mb/s peak limit to keep bbdmux followed by mplex -f 4 happy */
+	{ 0, 1150000, 2500000 },
+	{ 1, 12, 3},
+	/*
+	 * Spatial filter = Manual, Temporal filter = Manual
+	 * Median filter = Horizontal / Vertical
+	 * Spatial filter value = 1, Temporal filter value = 4
+	 */
+	{ 0, 3, 1, 4 },
+	44100
+};
+
+static struct cxm_codec_profile dvd_half_d1_ntsc_profile = {
+	"MPEG-2 DVD NTSC video and MPEG audio",
+	CXM_FW_STREAM_TYPE_DVD,
+	30,
+	352, 480, 480,
+	{ 10, 12, 21 },
+	2,
+	0,
+	{ 0, 4000000, 4520000 }, /* 4 hours on 8.54 GB media */
+	{ 1, 15, 3},
+	/*
+	 * Spatial filter = Manual, Temporal filter = Manual
+	 * Median filter = Horizontal / Vertical
+	 * Spatial filter value = 1, Temporal filter value = 4
+	 */
+	{ 0, 3, 1, 4 },
+	48000
+};
+
+static struct cxm_codec_profile dvd_half_d1_pal_profile = {
+	"MPEG-2 DVD PAL video and MPEG audio",
+	CXM_FW_STREAM_TYPE_DVD,
+	25,
+	352, 576, 576,
+	{ 6, 17, 22 },
+	2,
+	0,
+	{ 0, 4000000, 4520000 }, /* 4 hours on 8.54 GB media */
+	{ 1, 12, 3},
+	/*
+	 * Spatial filter = Manual, Temporal filter = Manual
+	 * Median filter = Horizontal / Vertical
+	 * Spatial filter value = 1, Temporal filter value = 4
+	 */
+	{ 0, 3, 1, 4 },
+	48000
+};
+
+static struct cxm_codec_profile dvd_full_d1_ntsc_profile = {
+	"MPEG-2 DVD NTSC video and MPEG audio",
+	CXM_FW_STREAM_TYPE_DVD,
+	30,
+	720, 480, 480,
+	{ 10, 12, 21 },
+	2,
+	0,
+	/* 9.52 Mb/s peak limit to keep bbdmux followed by mplex -f 8 happy */
+	{ 0, 9000000, 9520000 }, /* 1 hour on 4.7 GB media */
+	{ 1, 15, 3},
+	/*
+	 * Spatial filter = Manual, Temporal filter = Manual
+	 * Median filter = Horizontal / Vertical
+	 * Spatial filter value = 1, Temporal filter value = 4
+	 */
+	{ 0, 3, 1, 4 },
+	48000
+};
+
+static struct cxm_codec_profile dvd_full_d1_pal_profile = {
+	"MPEG-2 DVD PAL video and MPEG audio",
+	CXM_FW_STREAM_TYPE_DVD,
+	25,
+	720, 576, 576,
+	{ 6, 17, 22 },
+	2,
+	0,
+	/* 9.52 Mb/s peak limit to keep bbdmux followed by mplex -f 8 happy */
+	{ 0, 9000000, 9520000 }, /* 1 hour on 4.7 GB media */
+	{ 1, 12, 3},
+	/*
+	 * Spatial filter = Manual, Temporal filter = Manual
+	 * Median filter = Horizontal / Vertical
+	 * Spatial filter value = 1, Temporal filter value = 4
+	 */
+	{ 0, 3, 1, 4 },
+	48000
+};
+
+
+static const struct cxm_codec_profile
+*codec_profiles[] = {
+	&vcd_ntsc_profile,
+	&vcd_pal_profile,
+	&svcd_ntsc_profile,
+	&svcd_pal_profile,
+	&dvd_half_d1_ntsc_profile,
+	&dvd_half_d1_pal_profile,
+	&dvd_full_d1_ntsc_profile,
+	&dvd_full_d1_pal_profile
+};
+
+
+static unsigned int
+cxm_queue_firmware_command( struct cxm_softc *sc,
+			    enum cxm_mailbox_name mbx_name, u_int32_t cmd,
+			    u_int32_t *parameters, unsigned int nparameters )
+{
+	unsigned int i;
+	unsigned int mailbox;
+	u_int32_t completed_command;
+	u_int32_t flags;
+
+	if (nparameters > CXM_MBX_MAX_PARAMETERS) {
+		kprintf("%s: too many parameters for mailbox\n", sc->name);
+		return -1;
+	}
+
+	mailbox = 0;
+
+	switch (mbx_name) {
+	case cxm_dec_mailbox:
+		mailbox = sc->dec_mbx
+			  + CXM_MBX_FW_CMD_MAILBOX *sizeof(struct cxm_mailbox);
+		break;
+
+	case cxm_enc_mailbox:
+		mailbox = sc->enc_mbx
+			  + CXM_MBX_FW_CMD_MAILBOX *sizeof(struct cxm_mailbox);
+		break;
+
+	default:
+		return -1;
+	}
+
+	crit_enter();
+	for (i = 0; i < CXM_MBX_FW_CMD_MAILBOXES; i++) {
+		flags = CSR_READ_4(sc,
+				   mailbox
+				   + offsetof(struct cxm_mailbox, flags));
+		if (! (flags & CXM_MBX_FLAG_IN_USE) )
+			break;
+
+		/*
+		 * Mail boxes containing certain completed commands
+		 * for which the results are never needed can be reused.
+		 */
+
+		if ((flags & (CXM_MBX_FLAG_DRV_DONE | CXM_MBX_FLAG_FW_DONE))
+		    == (CXM_MBX_FLAG_DRV_DONE | CXM_MBX_FLAG_FW_DONE)) {
+			completed_command
+			 = CSR_READ_4(sc,
+				      mailbox
+				      + offsetof(struct cxm_mailbox, command));
+
+			/*
+			 * DMA results are always check by reading the
+			 * DMA status register ... never by checking
+			 * the mailbox after the command has completed.
+			 */
+
+			if (completed_command == CXM_FW_CMD_SCHED_DMA_TO_HOST)
+				break;
+		}
+
+		mailbox += sizeof(struct cxm_mailbox);
+	}
+
+	if (i >= CXM_MBX_FW_CMD_MAILBOXES) {
+		crit_exit();
+		return -1;
+	}
+
+	CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, flags),
+		    CXM_MBX_FLAG_IN_USE);
+
+	/*
+	 * PCI writes may be buffered so force the
+	 * write to complete by reading the last
+	 * location written.
+	 */
+
+	(void)CSR_READ_4(sc, mailbox + offsetof(struct cxm_mailbox, flags));
+
+	crit_exit();
+
+	CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, command), cmd);
+	CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, timeout),
+		    CXM_FW_STD_TIMEOUT);
+
+	for (i = 0; i < nparameters; i++)
+		CSR_WRITE_4(sc,
+			    mailbox
+			    + offsetof(struct cxm_mailbox, parameters)
+			    + i * sizeof(u_int32_t),
+			    *(parameters + i));
+
+#ifndef CXM_PVR150
+	for ( ; i < CXM_MBX_MAX_PARAMETERS; i++)
+		CSR_WRITE_4(sc,
+			    mailbox
+			    + offsetof(struct cxm_mailbox, parameters)
+			    + i * sizeof(u_int32_t), 0);
+#endif
+
+	CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, flags),
+		    CXM_MBX_FLAG_IN_USE | CXM_MBX_FLAG_DRV_DONE);
+
+	return mailbox;
+}
+
+
+static int
+cxm_firmware_command( struct cxm_softc *sc,
+		      enum cxm_mailbox_name mbx_name, u_int32_t cmd,
+		      u_int32_t *parameters, unsigned int nparameters )
+{
+	const char *wmesg;
+	unsigned int *bmp;
+	unsigned int i;
+	unsigned int mailbox;
+	u_int32_t flags;
+	u_int32_t result;
+
+	bmp = NULL;
+	wmesg = "";
+
+	switch (mbx_name) {
+	case cxm_dec_mailbox:
+		bmp = &sc->dec_mbx;
+		wmesg = "cxmdfw";
+		break;
+
+	case cxm_enc_mailbox:
+		bmp = &sc->enc_mbx;
+		wmesg = "cxmefw";
+		break;
+
+	default:
+		return -1;
+	}
+
+	mailbox = cxm_queue_firmware_command(sc, mbx_name, cmd,
+					     parameters, nparameters);
+	if (mailbox == -1) {
+		kprintf("%s: no free mailboxes\n", sc->name);
+		return -1;
+	}
+
+	/* Give the firmware a chance to start processing the request */
+	(void)tsleep(bmp, 0, wmesg, hz / 100);
+
+	for (i = 0; i < 100; i++) {
+		flags = CSR_READ_4(sc,
+				   mailbox
+				   + offsetof(struct cxm_mailbox, flags));
+		if ( (flags & CXM_MBX_FLAG_FW_DONE) )
+			break;
+
+		/* Wait for 10ms */
+		(void)tsleep(bmp, 0, wmesg, hz / 100);
+	}
+
+	if (i >= 100) {
+		kprintf("%s: timeout\n", sc->name);
+		return -1;
+	}
+
+	result = CSR_READ_4(sc,
+			    mailbox
+			    + offsetof(struct cxm_mailbox, result));
+
+	for (i = 0; i < nparameters; i++)
+		*(parameters + i)
+		  = CSR_READ_4(sc,
+			       mailbox
+			       + offsetof(struct cxm_mailbox, parameters)
+			       + i * sizeof(u_int32_t));
+
+	CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, flags), 0);
+
+	return result == 0 ? 0 : -1;
+}
+
+
+static int
+cxm_firmware_command_nosleep( struct cxm_softc *sc,
+			      enum cxm_mailbox_name mbx_name, u_int32_t cmd,
+			      u_int32_t *parameters, unsigned int nparameters )
+{
+	unsigned int i;
+	unsigned int mailbox;
+	u_int32_t flags;
+	u_int32_t result;
+
+	for (i = 0; i < 100; i++) {
+		mailbox = cxm_queue_firmware_command(sc, mbx_name, cmd,
+						     parameters, nparameters);
+		if (mailbox != -1)
+			break;
+
+		/* Wait for 10ms */
+		DELAY(10000);
+		}
+
+	if (i >= 100) {
+		kprintf("%s: no free mailboxes\n", sc->name);
+		return -1;
+	}
+
+	/* Give the firmware a chance to start processing the request */
+	DELAY(10000);
+
+	for (i = 0; i < 100; i++) {
+		flags = CSR_READ_4(sc,
+				   mailbox
+				   + offsetof(struct cxm_mailbox, flags));
+		if ( (flags & CXM_MBX_FLAG_FW_DONE) )
+			break;
+
+		/* Wait for 10ms */
+		DELAY(10000);
+	}
+
+	if (i >= 100) {
+		kprintf("%s: timeout\n", sc->name);
+		return -1;
+	}
+
+	result = CSR_READ_4(sc,
+			    mailbox
+			    + offsetof(struct cxm_mailbox, result));
+
+	for (i = 0; i < nparameters; i++)
+		*(parameters + i)
+		  = CSR_READ_4(sc,
+			       mailbox
+			       + offsetof(struct cxm_mailbox, parameters)
+			       + i * sizeof(u_int32_t));
+
+	CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, flags), 0);
+
+	return result == 0 ? 0 : -1;
+}
+
+
+static int
+cxm_stop_firmware( struct cxm_softc *sc )
+{
+
+	if (cxm_firmware_command_nosleep(sc, cxm_enc_mailbox,
+					 CXM_FW_CMD_ENC_HALT_FW, NULL, 0) < 0)
+		return -1;
+
+	if (sc->type == cxm_iTVC15_type
+	    && cxm_firmware_command_nosleep(sc, cxm_dec_mailbox,
+					    CXM_FW_CMD_DEC_HALT_FW,
+					    NULL, 0) < 0)
+		return -1;
+
+	/* Wait for 10ms */
+	DELAY(10000);
+
+	return 0;
+}
+
+
+static void
+cxm_set_irq_mask( struct cxm_softc *sc, u_int32_t mask )
+{
+	crit_enter();
+
+	CSR_WRITE_4(sc, CXM_REG_IRQ_MASK, mask);
+
+	/*
+	 * PCI writes may be buffered so force the
+	 * write to complete by reading the last
+	 * location written.
+	 */
+
+	(void)CSR_READ_4(sc, CXM_REG_IRQ_MASK);
+
+	sc->irq_mask = mask;
+
+	crit_exit();
+}
+
+
+static void
+cxm_set_irq_status( struct cxm_softc *sc, u_int32_t status )
+{
+
+	CSR_WRITE_4(sc, CXM_REG_IRQ_STATUS, status);
+
+	/*
+	 * PCI writes may be buffered so force the
+	 * write to complete by reading the last
+	 * location written.
+	 */
+
+	(void)CSR_READ_4(sc, CXM_REG_IRQ_STATUS);
+}
+
+
+static int
+cxm_stop_hardware( struct cxm_softc *sc )
+{
+#ifndef CXM_PVR150
+	if (sc->cxm_iic) {
+		if (cxm_saa7115_mute(sc) < 0)
+			return -1;
+		if (cxm_msp_mute(sc) < 0)
+			return -1;
+	}
+#endif
+
+	/* Halt the firmware */
+	if (sc->enc_mbx != -1) {
+		if (cxm_stop_firmware(sc) < 0)
+			return -1;
+	}
+
+	/* Mask all interrupts */
+	cxm_set_irq_mask(sc, 0xffffffff);
+
+	/* Stop VDM */
+	CSR_WRITE_4(sc, CXM_REG_VDM, CXM_CMD_VDM_STOP);
+
+	/* Stop AO */
+	CSR_WRITE_4(sc, CXM_REG_AO, CXM_CMD_AO_STOP);
+
+	/* Ping (?) APU */
+	CSR_WRITE_4(sc, CXM_REG_APU, CXM_CMD_APU_PING);
+
+	/* Stop VPU */
+	CSR_WRITE_4(sc, CXM_REG_VPU, sc->type == cxm_iTVC15_type
+					? CXM_CMD_VPU_STOP15
+					: CXM_CMD_VPU_STOP16);
+
+	/* Reset Hw Blocks */
+	CSR_WRITE_4(sc, CXM_REG_HW_BLOCKS, CXM_CMD_HW_BLOCKS_RST);
+
+	/* Stop SPU */
+	CSR_WRITE_4(sc, CXM_REG_SPU, CXM_CMD_SPU_STOP);
+
+	/* Wait for 10ms */
+	DELAY(10000);
+
+	return 0;
+}
+
+
+static int
+cxm_download_firmware( struct cxm_softc *sc )
+{
+	unsigned int i;
+	const u_int32_t *fw;
+
+	/* Download the encoder firmware */
+	fw = (const u_int32_t *)cxm_enc_fw;
+	for (i = 0; i < CXM_FW_SIZE; i += sizeof(*fw))
+		CSR_WRITE_4(sc, CXM_MEM_ENC + i, *fw++);
+
+	/* Download the decoder firmware */
+	if (sc->type == cxm_iTVC15_type) {
+		fw = (const u_int32_t *)cxm_dec_fw;
+		for (i = 0; i < CXM_FW_SIZE; i += sizeof(*fw))
+			CSR_WRITE_4(sc, CXM_MEM_DEC + i, *fw++);
+	}
+
+	return 0;
+}
+
+
+static int
+cxm_init_hardware( struct cxm_softc *sc )
+{
+	unsigned int i;
+	unsigned int mailbox;
+	u_int32_t parameter;
+
+	if (cxm_stop_hardware(sc) < 0)
+		return -1;
+
+	/* Initialize encoder SDRAM pre-charge */
+	CSR_WRITE_4(sc, CXM_REG_ENC_SDRAM_PRECHARGE,
+			CXM_CMD_SDRAM_PRECHARGE_INIT);
+
+	/* Initialize encoder SDRAM refresh to 1us */
+	CSR_WRITE_4(sc, CXM_REG_ENC_SDRAM_REFRESH,
+			CXM_CMD_SDRAM_REFRESH_INIT);
+
+	/* Initialize decoder SDRAM pre-charge */
+	CSR_WRITE_4(sc, CXM_REG_DEC_SDRAM_PRECHARGE,
+			CXM_CMD_SDRAM_PRECHARGE_INIT);
+
+	/* Initialize decoder SDRAM refresh to 1us */
+	CSR_WRITE_4(sc, CXM_REG_DEC_SDRAM_REFRESH,
+			CXM_CMD_SDRAM_REFRESH_INIT);
+
+	/* Wait for 600ms */
+	DELAY(600000);
+
+	if (cxm_download_firmware(sc) < 0)
+		return -1;
+
+	/* Enable SPU */
+	CSR_WRITE_4(sc, CXM_REG_SPU,
+			CSR_READ_4(sc, CXM_REG_SPU) & CXM_MASK_SPU_ENABLE);
+
+	/* Wait for 1 second */
+	DELAY(1000000);
+
+	/* Enable VPU */
+	CSR_WRITE_4(sc, CXM_REG_VPU,
+			CSR_READ_4(sc, CXM_REG_VPU)
+			& (sc->type == cxm_iTVC15_type
+				? CXM_MASK_VPU_ENABLE15
+				: CXM_MASK_VPU_ENABLE16));
+
+	/* Wait for 1 second */
+	DELAY(1000000);
+
+	/* Locate encoder mailbox */
+	mailbox = CXM_MEM_ENC;
+	for (i = 0; i < CXM_MEM_ENC_SIZE; i += 0x100)
+		if (CSR_READ_4(sc, mailbox + i) == 0x12345678
+		    && CSR_READ_4(sc, mailbox + i + 4) == 0x34567812
+		    && CSR_READ_4(sc, mailbox + i + 8) == 0x56781234
+		    && CSR_READ_4(sc, mailbox + i + 12) == 0x78123456)
+			break;
+
+	if (i >= CXM_MEM_ENC_SIZE)
+		return -1;
+
+	sc->enc_mbx = mailbox + i + 16;
+
+	/* Locate decoder mailbox */
+	if (sc->type == cxm_iTVC15_type) {
+		mailbox = CXM_MEM_DEC;
+		for (i = 0; i < CXM_MEM_DEC_SIZE; i += 0x100)
+			if (CSR_READ_4(sc, mailbox + i) == 0x12345678
+			    && CSR_READ_4(sc, mailbox + i + 4) == 0x34567812
+			    && CSR_READ_4(sc, mailbox + i + 8) == 0x56781234
+			    && CSR_READ_4(sc, mailbox + i + 12) == 0x78123456)
+				break;
+
+		if (i >= CXM_MEM_DEC_SIZE)
+			return -1;
+
+		sc->dec_mbx = mailbox + i + 16;
+	}
+
+	/* Get encoder firmware version */
+	parameter = 0;
+	if (cxm_firmware_command_nosleep(sc, cxm_enc_mailbox,
+					 CXM_FW_CMD_ENC_GET_FW_VER,
+					 &parameter, 1) < 0)
+		return -1;
+
+	kprintf("%s: encoder firmware version %#x\n",
+	       sc->name, (unsigned int)parameter);
+
+	/* Get decoder firmware version */
+	if (sc->type == cxm_iTVC15_type) {
+		parameter = 0;
+		if (cxm_firmware_command_nosleep(sc, cxm_dec_mailbox,
+						 CXM_FW_CMD_DEC_GET_FW_VER,
+						 &parameter, 1) < 0)
+			return -1;
+
+		kprintf("%s: decoder firmware version %#x\n",
+		       sc->name, (unsigned int)parameter);
+	}
+
+	return 0;
+}
+
+
+static int
+cxm_configure_encoder( struct cxm_softc *sc )
+{
+	int fps;
+	unsigned int i;
+	u_int32_t parameters[12];
+	const struct cxm_codec_profile *cpp;
+
+#ifndef CXM_PVR150
+	if (sc->source == cxm_fm_source)
+#endif
+		switch (cxm_tuner_selected_channel_set(sc)) {
+		case CHNLSET_NABCST:
+		case CHNLSET_CABLEIRC:
+		case CHNLSET_JPNBCST:
+		case CHNLSET_JPNCABLE:
+			fps = 30;
+			break;
+
+		default:
+			fps = 25;
+			break;
+		}
+#ifndef CXM_PVR150
+	else
+		fps = cxm_saa7115_detected_fps(sc);
+#endif
+
+	if (fps < 0)
+		return -1;
+
+	if (sc->profile->fps != fps) {
+
+		/*
+		 * Pick a profile with the correct fps using the
+		 * chosen stream type and width to decide between
+		 * the VCD, SVCD, or DVD profiles.
+		 */
+
+		for (i = 0; i < NUM_ELEMENTS(codec_profiles); i++)
+			if (codec_profiles[i]->fps == fps
+			    && codec_profiles[i]->stream_type
+			       == sc->profile->stream_type
+			    && codec_profiles[i]->width == sc->profile->width)
+				break;
+
+		if (i >= NUM_ELEMENTS(codec_profiles))
+			return -1;
+
+		sc->profile = codec_profiles[i];
+	}
+
+	cpp = sc->profile;
+
+#ifndef CXM_PVR150
+	if (cxm_saa7115_configure(sc,
+				  cpp->width, cpp->source_height, fps,
+				  cpp->audio_sample_rate) < 0)
+		return -1;
+#endif
+
+	/* assign dma block len */
+	parameters[0] = 1; /* Transfer block size = 1 */
+	parameters[1] = 1; /* Units = 1 (frames) */
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_DMA_BLOCKLEN,
+				 parameters, 2) != 0)
+		return -1;
+
+
+	/* assign program index info */
+	parameters[0] = 0; /* Picture mask = 0 (don't generate index) */
+	parameters[1] = 0; /* Num_req = 0 */
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_PGM_INDEX_INFO,
+				 parameters, 2) != 0)
+		return -1;
+
+	/* assign stream type */
+	parameters[0] = cpp->stream_type;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_STREAM_TYPE,
+				 parameters, 1) != 0)
+		return -1;
+
+	/* assign output port */
+	parameters[0] = 0; /* 0 (Memory) */
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_OUTPUT_PORT,
+				 parameters, 1) != 0)
+		return -1;
+
+	/* assign framerate */
+	parameters[0] = cpp->fps == 30 ? 0 : 1;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_FRAME_RATE,
+				 parameters, 1) != 0)
+		return -1;
+
+	/* assign frame size */
+	parameters[0] = cpp->height;
+	parameters[1] = cpp->width;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_FRAME_SIZE,
+				 parameters, 2) != 0)
+		return -1;
+
+	/* assign aspect ratio */
+	parameters[0] = cpp->aspect;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_ASPECT_RATIO,
+				 parameters, 1) != 0)
+		return -1;
+
+	/* assign bitrates */
+	parameters[0] = cpp->bitrate.mode;
+	parameters[1] = cpp->bitrate.average;
+	parameters[2] = cpp->bitrate.peak / 400;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_BITRATES,
+				 parameters, 3) != 0)
+		return -1;
+
+	/* assign gop closure */
+	parameters[0] = cpp->gop.closure;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_GOP_CLOSURE,
+				 parameters, 1) != 0)
+		return -1;
+
+	/* assign gop properties */
+	parameters[0] = cpp->gop.frames;
+	parameters[1] = cpp->gop.bframes;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_GOP_PROPERTIES,
+				 parameters, 2) != 0)
+		return -1;
+
+	/* assign 3 2 pulldown */
+	parameters[0] = cpp->pulldown;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_3_2_PULLDOWN,
+				 parameters, 1) != 0)
+		return -1;
+
+	/* assign dnr filter mode */
+	parameters[0] = cpp->dnr.mode;
+	parameters[1] = cpp->dnr.type;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_DNR_FILTER_MODE,
+				 parameters, 2) != 0)
+		return -1;
+
+	/* assign dnr filter props */
+	parameters[0] = cpp->dnr.spatial;
+	parameters[1] = cpp->dnr.temporal;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_DNR_FILTER_PROPERTIES,
+				 parameters, 2) != 0)
+		return -1;
+
+	/*
+	 * assign audio properties
+	 */
+
+	for (i = 0; i < NUM_ELEMENTS(codec_audio_formats); i++)
+		if (codec_audio_formats[i].sample_rate
+		    == cpp->audio_sample_rate)
+			break;
+
+	if (i >= NUM_ELEMENTS(codec_audio_formats))
+		return -1;
+
+	parameters[0] = codec_audio_formats[i].format;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_AUDIO_PROPERTIES,
+				 parameters, 1) != 0)
+		return -1;
+
+	/* assign coring levels */
+	parameters[0] = 0; /* luma_h */
+	parameters[1] = 255; /* luma_l */
+	parameters[2] = 0; /* chroma_h */
+	parameters[3] = 255; /* chroma_l */
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_CORING_LEVELS,
+				 parameters, 4) != 0)
+		return -1;
+
+	/* assign spatial filter type */
+	parameters[0] = 3; /* Luminance filter = 3 (2D H/V Separable) */
+	parameters[1] = 1; /* Chrominance filter = 1 (1D Horizontal) */
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_SPATIAL_FILTER_TYPE,
+				 parameters, 2) != 0)
+		return -1;
+
+	/* assign frame drop rate */
+	parameters[0] = 0;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_FRAME_DROP_RATE,
+				 parameters, 1) != 0)
+		return -1;
+
+	/* assign placeholder */
+	parameters[0] = 0; /* type = 0 (Extension / UserData) */
+	parameters[1] = 0; /* period */
+	parameters[2] = 0; /* size_t */
+	parameters[3] = 0; /* arg0 */
+	parameters[4] = 0; /* arg1 */
+	parameters[5] = 0; /* arg2 */
+	parameters[6] = 0; /* arg3 */
+	parameters[7] = 0; /* arg4 */
+	parameters[8] = 0; /* arg5 */
+	parameters[9] = 0; /* arg6 */
+	parameters[10] = 0; /* arg7 */
+	parameters[11] = 0; /* arg8 */
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_PLACEHOLDER,
+				 parameters, 12) != 0)
+		return -1;
+
+	/* assign VBI properties */
+	parameters[0] = 0xbd04; /* mode = 0 (sliced), stream and user data */
+	parameters[1] = 0; /* frames per interrupt (only valid in raw mode) */
+	parameters[2] = 0; /* total raw VBI frames (only valid in raw mode) */
+	parameters[3] = 0x25256262; /* ITU 656 start codes (saa7115 table 24)*/
+	parameters[4] = 0x38387f7f; /* ITU 656 stop codes (saa7115 table 24) */
+	parameters[5] = cpp->vbi.nlines; /* lines per frame */
+	parameters[6] = 1440; /* bytes per line = 720 pixels */
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_VBI_PROPERTIES,
+				 parameters, 7) != 0)
+		return -1;
+
+	/* assign VBI lines */
+	parameters[0] = 0xffffffff; /* all lines */
+	parameters[1] = 0; /* disable VBI features */
+	parameters[2] = 0;
+	parameters[3] = 0;
+	parameters[4] = 0;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_VBI_LINE,
+				 parameters, 5) != 0)
+		return -1;
+
+	/* assign number of lines in fields 1 and 2 */
+	parameters[0] = cpp->source_height / 2 + cpp->vbi.nlines;
+	parameters[1] = cpp->source_height / 2 + cpp->vbi.nlines;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ASSIGN_NUM_VSYNC_LINES,
+				 parameters, 2) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int
+cxm_start_encoder( struct cxm_softc *sc )
+{
+	u_int32_t parameters[4];
+	u_int32_t subtype;
+	u_int32_t type;
+
+
+	if (sc->encoding)
+		return 0;
+
+	if (cxm_configure_encoder(sc) < 0)
+		return -1;
+
+	/* Mute the video input if necessary. */
+	parameters[0] = sc->source == cxm_fm_source ? 1 : 0;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_MUTE_VIDEO_INPUT,
+				 parameters, 1) != 0)
+		return -1;
+
+	/* Clear pending encoder interrupts (which are currently masked) */
+	cxm_set_irq_status(sc, CXM_IRQ_ENC);
+
+	/* Enable event notification */
+	parameters[0] = 0; /* Event = 0 (refresh encoder input) */
+	parameters[1] = 1; /* Notification = 1 (enable) */
+	parameters[2] = 0x10000000; /* Interrupt bit */
+	parameters[3] = -1; /* Mailbox = -1 (no mailbox) */
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ENC_EVENT_NOTIFICATION,
+				 parameters, 4) != 0)
+		return -1;
+
+#ifdef CXM_PVR150
+	cx25840_command(sc->iicbus, VIDIOC_STREAMOFF, NULL);
+#else
+	if (cxm_saa7115_mute(sc) < 0)
+		return -1;
+	if (cxm_msp_mute(sc) < 0)
+		return -1;
+#endif
+
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_INITIALIZE_VIDEO_INPUT,
+				 NULL, 0) < 0)
+		return -1;
+
+#ifdef CXM_PVR150
+	cx25840_command(sc->iicbus, VIDIOC_STREAMON, NULL);
+#else
+	if (cxm_saa7115_unmute(sc) < 0)
+		return -1;
+	if (cxm_msp_unmute(sc) < 0)
+		return -1;
+#endif
+
+	/* Wait for 100ms */
+	(void)tsleep(&sc->encoding, 0, "cxmce", hz / 10);
+
+	type = sc->mpeg ? CXM_FW_CAPTURE_STREAM_TYPE_MPEG
+			: CXM_FW_CAPTURE_STREAM_TYPE_RAW;
+	subtype = ((sc->mpeg || sc->source == cxm_fm_source)
+		   ? CXM_FW_CAPTURE_STREAM_PCM_AUDIO : 0)
+		  | ((sc->mpeg || sc->source != cxm_fm_source)
+		     ? CXM_FW_CAPTURE_STREAM_YUV : 0);
+
+	/* Start the encoder */
+	parameters[0] = type;
+	parameters[1] = subtype;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_BEGIN_CAPTURE, parameters, 2) != 0)
+		return -1;
+
+	sc->enc_pool.offset = 0;
+	sc->enc_pool.read = 0;
+	sc->enc_pool.write = 0;
+
+	sc->encoding_eos = 0;
+
+	sc->encoding = 1;
+
+	/* Enable interrupts */
+	cxm_set_irq_mask(sc, sc->irq_mask & ~CXM_IRQ_ENC);
+
+	return 0;
+}
+
+
+static int
+cxm_stop_encoder( struct cxm_softc *sc )
+{
+	u_int32_t parameters[4];
+	u_int32_t subtype;
+	u_int32_t type;
+
+	if (! sc->encoding )
+		return 0;
+
+	type = sc->mpeg ? CXM_FW_CAPTURE_STREAM_TYPE_MPEG
+			: CXM_FW_CAPTURE_STREAM_TYPE_RAW;
+	subtype = ((sc->mpeg || sc->source == cxm_fm_source)
+		   ? CXM_FW_CAPTURE_STREAM_PCM_AUDIO : 0)
+		  | ((sc->mpeg || sc->source != cxm_fm_source)
+		     ? CXM_FW_CAPTURE_STREAM_YUV : 0);
+
+	/* Stop the encoder */
+	parameters[0] = sc->mpeg ? 0 : 1; /* When = 0 (end of GOP) */
+	parameters[1] = type;
+	parameters[2] = subtype;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_END_CAPTURE, parameters, 3) != 0)
+		return -1;
+
+	/* Wait for up to 1 second */
+	crit_enter();
+	if (! sc->encoding_eos )
+		(void)tsleep(&sc->encoding_eos, 0, "cxmeos", hz);
+	crit_exit();
+
+	if (sc->mpeg && ! sc->encoding_eos )
+		kprintf("%s: missing encoder EOS\n", sc->name);
+
+	/* Disable event notification */
+	parameters[0] = 0; /* Event = 0 (refresh encoder input) */
+	parameters[1] = 0; /* Notification = 0 (disable) */
+	parameters[2] = 0x10000000; /* Interrupt bit */
+	parameters[3] = -1; /* Mailbox = -1 (no mailbox) */
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_ENC_EVENT_NOTIFICATION,
+				 parameters, 4) != 0)
+		return -1;
+
+	/* Disable interrupts */
+	cxm_set_irq_mask(sc, sc->irq_mask | CXM_IRQ_ENC);
+
+	sc->encoding = 0;
+
+	return 0;
+}
+
+
+static int
+cxm_pause_encoder( struct cxm_softc *sc )
+{
+	u_int32_t parameter;
+
+	/* Pause the encoder */
+	parameter = 0;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_PAUSE_ENCODER, &parameter, 1) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int
+cxm_unpause_encoder( struct cxm_softc *sc )
+{
+	u_int32_t parameter;
+
+	/* Unpause the encoder */
+	parameter = 1;
+	if (cxm_firmware_command(sc, cxm_enc_mailbox,
+				 CXM_FW_CMD_PAUSE_ENCODER, &parameter, 1) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static unsigned int
+cxm_encoder_fixup_byte_order( struct cxm_softc *sc,
+			      unsigned int current, size_t offset)
+{
+	unsigned int	strips;
+	unsigned int	i;
+	unsigned int	j;
+	unsigned int	k;
+	unsigned int	macroblocks_per_line;
+	unsigned int	scratch;
+	unsigned int	words_per_line;
+	u_int32_t	*ptr;
+	u_int32_t	*src;
+#ifdef CXM_PVR150
+	u_int32_t	*ptr_end;
+#else
+	size_t		nbytes;
+#endif
+
+	switch (sc->enc_pool.bufs[current].byte_order) {
+	case cxm_device_mpeg_byte_order:
+
+		/*
+		 * Convert each 32 bit word to the proper byte ordering.
+		 */
+
+#ifdef CXM_PVR150
+		ptr = (u_int32_t *)sc->enc_pool.bufs[current].vaddr ;
+		ptr_end = ptr + (sc->enc_pool.bufs[current].size / sizeof(u_int32_t));
+		for(;ptr < ptr_end;ptr++)
+			 *ptr = bswap32(*ptr);
+#else
+		for (nbytes = 0,
+		     ptr = (u_int32_t *)sc->enc_pool.bufs[current].vaddr;
+		     nbytes != sc->enc_pool.bufs[current].size;
+		     nbytes += sizeof(*ptr), ptr++)
+			*ptr = bswap32(*ptr);
+#endif
+		break;
+
+	case cxm_device_yuv12_byte_order:
+
+		/*
+		 * Convert each macro block to planar using
+		 * a scratch buffer (the buffer prior to the
+		 * current buffer is always free since it marks
+		 * the end of the ring buffer).
+		 */
+
+		scratch = (current + (CXM_SG_BUFFERS - 1)) % CXM_SG_BUFFERS;
+
+		if (offset) {
+			current = scratch;
+			break;
+		}
+
+		src = (u_int32_t *)sc->enc_pool.bufs[current].vaddr;
+		words_per_line = sc->profile->width / sizeof(*ptr);
+		macroblocks_per_line
+		  = sc->profile->width / CXM_MACROBLOCK_WIDTH;
+		strips = sc->enc_pool.bufs[current].size
+			   / (macroblocks_per_line * CXM_MACROBLOCK_SIZE);
+
+		for (i = 0; i < strips; i++) {
+			ptr = (u_int32_t *)sc->enc_pool.bufs[scratch].vaddr
+			      + i * macroblocks_per_line * CXM_MACROBLOCK_SIZE
+				/ sizeof(*ptr);
+			for (j = 0; j < macroblocks_per_line; j++) {
+				for (k = 0; k < CXM_MACROBLOCK_HEIGHT; k++) {
+#if CXM_MACROBLOCK_WIDTH != 16
+#  error CXM_MACROBLOCK_WIDTH != 16
+#endif
+					*(ptr + k * words_per_line)
+					  = *src++;
+					*(ptr + k * words_per_line + 1)
+					  = *src++;
+					*(ptr + k * words_per_line + 2)
+					  = *src++;
+					*(ptr + k * words_per_line + 3)
+					  = *src++;
+				}
+				ptr += CXM_MACROBLOCK_WIDTH / sizeof(*ptr);
+			}
+		}
+
+		sc->enc_pool.bufs[scratch].size
+		  = sc->enc_pool.bufs[current].size;
+
+		current = scratch;
+		break;
+
+	default:
+		break;
+	}
+
+	sc->enc_pool.bufs[current].byte_order = cxm_host_byte_order;
+
+	return current;
+}
+
+
+static void
+cxm_encoder_dma_discard( struct cxm_softc *sc )
+{
+	u_int32_t parameters[3];
+
+	/* Discard the DMA request */
+	parameters[0] = 0;
+	parameters[1] = 0;
+	parameters[2] = 0;
+	if (cxm_queue_firmware_command(sc, cxm_enc_mailbox,
+				       CXM_FW_CMD_SCHED_DMA_TO_HOST,
+				       parameters, 3) == -1) {
+		kprintf("%s: failed to discard encoder dma request\n",
+		       sc->name);
+		return;
+	}
+
+#ifdef CXM_PVR150
+	sc->encoding_dma = 0;
+#else
+	sc->encoding_dma = -1;
+#endif
+}
+
+
+static void
+cxm_encoder_dma_done( struct cxm_softc *sc )
+{
+	int buffers_pending;
+	u_int32_t status;
+
+	if (! sc->encoding_dma) {
+		kprintf("%s: encoder dma not already in progress\n",
+		       sc->name);
+		return;
+	}
+
+	buffers_pending = sc->encoding_dma;
+	sc->encoding_dma = 0;
+
+	if (buffers_pending < 0)
+		return;
+
+	status = CSR_READ_4(sc, CXM_REG_DMA_STATUS) & 0x0000000f;
+
+	if ((status
+	     & (CXM_DMA_ERROR_LIST | CXM_DMA_ERROR_WRITE | CXM_DMA_SUCCESS))
+	    != CXM_DMA_SUCCESS) {
+		kprintf("%s: encoder dma status %#x\n",
+		       sc->name, (unsigned int)status);
+		return;
+	}
+
+	/* Update the books */
+	crit_enter();
+	sc->enc_pool.write = (sc->enc_pool.write + buffers_pending)
+				   % CXM_SG_BUFFERS;
+	crit_exit();
+
+	/* signal anyone requesting notification */
+	if (sc->enc_proc)
+		ksignal (sc->enc_proc, sc->enc_signal);
+
+	/* wakeup anyone waiting for data */
+	wakeup(&sc->enc_pool.read);
+
+	/* wakeup anyone polling for data */
+	selwakeup(&sc->enc_sel);
+}
+
+
+static void
+cxm_encoder_dma_request( struct cxm_softc *sc )
+{
+	enum cxm_byte_order byte_order;
+	int buffers_free;
+	int buffers_pending;
+	unsigned int current;
+	unsigned int i;
+	unsigned int mailbox;
+	unsigned int macroblocks_per_line;
+	unsigned int nrequests;
+	unsigned int strips;
+	u_int32_t parameters[CXM_MBX_MAX_PARAMETERS];
+	u_int32_t type;
+	size_t max_sg_segment;
+	struct {
+		size_t offset;
+		size_t size;
+	} requests[2];
+
+	if (sc->encoding_dma) {
+		kprintf("%s: encoder dma already in progress\n",
+		       sc->name);
+		cxm_encoder_dma_discard(sc);
+		return;
+	}
+
+	mailbox = sc->enc_mbx
+		  + CXM_MBX_FW_DMA_MAILBOX * sizeof(struct cxm_mailbox);
+
+#ifdef CXM_PVR150
+		parameters[0]
+#else
+	for (i = 0; i < CXM_MBX_MAX_PARAMETERS; i++)
+		parameters[i]
+#endif
+		  = CSR_READ_4(sc,
+			       mailbox
+			       + offsetof(struct cxm_mailbox, parameters)
+#ifndef CXM_PVR150
+			       + i * sizeof(u_int32_t)
+#endif
+			      );
+
+	byte_order = cxm_device_mpeg_byte_order;
+	max_sg_segment = CXM_SG_SEGMENT;
+	nrequests = 0;
+	type = parameters[0];
+
+	switch (type) {
+	case 0: /* MPEG */
+#ifdef CXM_PVR150
+		for (i = 1; i < 3/* CXM_MBX_MAX_PARAMETERS */; i++)
+			parameters[i] = CSR_READ_4(sc, mailbox +
+			    offsetof(struct cxm_mailbox, parameters) +
+			    i * sizeof(u_int32_t));
+#endif
+		requests[nrequests].offset = parameters[1];
+		requests[nrequests++].size = parameters[2];
+		break;
+
+	case 1: /* YUV */
+#ifdef CXM_PVR150
+		for (i = 1; i < 5/* CXM_MBX_MAX_PARAMETERS */; i++)
+			parameters[i] = CSR_READ_4(sc, mailbox +
+			    offsetof(struct cxm_mailbox, parameters) +
+			    i * sizeof(u_int32_t));
+#endif
+		byte_order = cxm_device_yuv12_byte_order;
+
+		/*
+		 * Simplify macroblock unpacking by ensuring
+		 * that strips don't span buffers.
+		 */
+
+#if CXM_MACROBLOCK_SIZE % 256
+#  error CXM_MACROBLOCK_SIZE not a multiple of 256
+#endif
+
+		macroblocks_per_line = sc->profile->width
+				       / CXM_MACROBLOCK_WIDTH;
+		strips = CXM_SG_SEGMENT
+			 / (macroblocks_per_line * CXM_MACROBLOCK_SIZE);
+		max_sg_segment = strips
+				 * macroblocks_per_line * CXM_MACROBLOCK_SIZE;
+
+		requests[nrequests].offset = parameters[1]; /* Y */
+		requests[nrequests++].size = parameters[2];
+		requests[nrequests].offset = parameters[3]; /* UV */
+		requests[nrequests++].size = parameters[4];
+		break;
+
+	case 2: /* PCM (audio) */
+	case 3: /* VBI */
+	default:
+		kprintf("%s: encoder dma type %#x unsupported\n",
+		       sc->name, (unsigned int)type);
+		cxm_encoder_dma_discard(sc);
+		return;
+	}
+
+	/*
+	 * Determine the number of buffers free at this * instant *
+	 * taking into consideration that the ring buffer wraps.
+	 */
+	crit_enter();
+	buffers_free = sc->enc_pool.read - sc->enc_pool.write;
+	if (buffers_free <= 0)
+		buffers_free += CXM_SG_BUFFERS;
+	crit_exit();
+
+	/*
+	 * Build the scatter / gather list taking in
+	 * consideration that the ring buffer wraps,
+	 * at least one free buffer must always be
+	 * present to mark the end of the ring buffer,
+	 * and each transfer must be a multiple of 256.
+	 */
+
+	buffers_pending = 0;
+	current = sc->enc_pool.write;
+
+	for (i = 0; i < nrequests; i++) {
+		if (! requests[i].size ) {
+			kprintf("%s: encoder dma size is zero\n", sc->name);
+			cxm_encoder_dma_discard(sc);
+			return;
+		}
+
+		while (requests[i].size) {
+			sc->enc_pool.bufs[current].size
+			  = requests[i].size > max_sg_segment
+			    ? max_sg_segment : requests[i].size;
+			sc->enc_pool.bufs[current].byte_order = byte_order;
+
+			sc->enc_sg.vaddr[buffers_pending].src
+			  = requests[i].offset;
+			sc->enc_sg.vaddr[buffers_pending].dst
+			  = sc->enc_pool.bufs[current].baddr;
+			sc->enc_sg.vaddr[buffers_pending].size
+			  = (sc->enc_pool.bufs[current].size + 0x000000ff)
+			    & 0xffffff00;
+
+			requests[i].offset += sc->enc_pool.bufs[current].size;
+			requests[i].size -= sc->enc_pool.bufs[current].size;
+			buffers_pending++;
+			current = (current + 1) % CXM_SG_BUFFERS;
+
+			if (buffers_pending >= buffers_free) {
+				kprintf(
+			      "%s: encoder dma not enough buffer space free\n",
+				       sc->name);
+				cxm_encoder_dma_discard(sc);
+				return;
+			}
+		}
+	}
+
+	/* Mark the last transfer in the list */
+	sc->enc_sg.vaddr[buffers_pending - 1].size |= 0x80000000;
+
+	/* Schedule the DMA */
+	parameters[0] = sc->enc_sg.baddr;
+	parameters[1] = buffers_pending * sizeof(sc->enc_sg.vaddr[0]);
+	parameters[2] = type;
+	if (cxm_queue_firmware_command(sc, cxm_enc_mailbox,
+				       CXM_FW_CMD_SCHED_DMA_TO_HOST,
+				       parameters, 3) == -1) {
+		kprintf("%s: failed to schedule encoder dma request\n",
+		       sc->name);
+		return;
+	}
+
+	/*
+	 * Record the number of pending buffers for the
+	 * benefit of cxm_encoder_dma_done.  Doing this
+	 * after queuing the command doesn't introduce
+	 * a race condition since we're already in the
+	 * interrupt handler.
+	 */
+
+	sc->encoding_dma = buffers_pending;
+}
+
+
+static int
+cxm_encoder_wait_for_lock( struct cxm_softc *sc )
+{
+#ifndef CXM_PVR150
+	int muted;
+#endif
+	int locked;
+	int result;
+
+	locked = 1;
+
+	/*
+	 * Wait for the tuner to lock.
+	 */
+	if (sc->source == cxm_fm_source || sc->source == cxm_tuner_source) {
+		result = cxm_tuner_wait_for_lock(sc);
+		if (result <= 0)
+			return result;
+		}
+
+#ifndef CXM_PVR150
+	/*
+	 * Wait for the video decoder to lock.
+	 */
+	if (sc->source != cxm_fm_source) {
+		result = cxm_saa7115_wait_for_lock(sc);
+		if (result < 0)
+			return result;
+		else if (result == 0)
+			locked = 0;
+		}
+
+	/*
+	 * Wait for the audio decoder to lock.
+	 */
+	if (sc->source == cxm_tuner_source) {
+		muted = cxm_msp_is_muted(sc);
+
+		result = cxm_msp_autodetect_standard(sc);
+		if (result < 0)
+			return result;
+		else if (result == 0)
+			locked = 0;
+
+		if (muted == 0 && cxm_msp_unmute(sc) < 0)
+			return -1;
+	}
+#endif
+
+	return locked;
+}
+
+
+static void
+cxm_mapmem(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+	bus_addr_t *busaddrp;
+
+	/*
+	 * Only the first bus space address is needed
+	 * since it's known that the memory is physically
+	 * contiguous due to bus_dmamem_alloc.
+	 */
+
+	busaddrp = (bus_addr_t *)arg;
+	*busaddrp = segs->ds_addr;
+}
+
+
+/*
+ * the boot time probe routine.
+ */
+static int
+cxm_probe( device_t dev )
+{
+	struct cxm_dev		*t;
+
+	t = cxm_devs;
+
+	while(t->name != NULL) {
+		if ((pci_get_vendor(dev) == t->vid) &&
+		    (pci_get_device(dev) == t->did)) {
+			device_set_desc(dev, t->name);
+			return 0;
+		}
+		t++;
+	}
+
+	return ENXIO;
+}
+
+
+/*
+ * the attach routine.
+ */
+static int
+cxm_attach( device_t dev )
+{
+	int		error;
+	int		rid;
+	int		unit;
+	unsigned int	i;
+	u_int32_t	command;
+	struct cxm_softc *sc;
+
+	/* Get the device data */
+	sc = device_get_softc(dev);
+	unit = device_get_unit(dev);
+
+	sc->type = cxm_iTVC15_type;
+
+	switch(pci_get_device(dev)) {
+	case PCI_PRODUCT_ICOMPRESSION_ITVC16:
+		sc->type = cxm_iTVC16_type;
+		break;
+
+	default:
+		break;
+	}
+
+	/* build the device name */
+	ksnprintf(sc->name, sizeof(sc->name), "cxm%d",unit);
+
+	/*
+	 * Enable bus mastering and memory mapped I/O.
+	 */
+	pci_enable_busmaster(dev);
+	pci_enable_io(dev, SYS_RES_MEMORY);
+	command = pci_read_config(dev, PCIR_COMMAND, 4);
+
+	if (! (command & PCIM_CMD_MEMEN) ) {
+		device_printf(dev, "failed to enable memory mappings\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	/*
+	 * Map control/status registers.
+	 */
+	rid = CXM_RID;
+	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
+					0, ~0, 1, RF_ACTIVE);
+
+	if (! sc->mem_res ) {
+		device_printf(dev, "could not map memory\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	sc->btag = rman_get_bustag(sc->mem_res);
+	sc->bhandle = rman_get_bushandle(sc->mem_res);
+
+	/*
+	 * Attach the I2C bus.
+	 */
+	sc->cxm_iic = device_add_child(dev, "cxm_iic", unit);
+
+	if (! sc->cxm_iic ) {
+		device_printf(dev, "could not add cxm_iic\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	error = device_probe_and_attach(sc->cxm_iic);
+
+	if (error) {
+		device_printf(dev, "could not attach cxm_iic\n");
+		goto fail;
+	}
+
+	/*
+	 * Initialize the tuner.
+	 */
+	if (cxm_tuner_init(sc) < 0) {
+		device_printf(dev, "could not initialize tuner\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+#ifdef CXM_PVR150
+	cx25840_initialize(sc->iicbus,1);
+	tda9887_initialize(sc->iicbus);
+	cxm_wm8775_init(sc->iicbus);
+
+	{
+		int jan = 0;
+
+		/* set the standard */
+		if (cxm_eeprom_get_std(sc)) {
+			device_printf(dev,"Eeprom NTSC\n");
+			jan = V4L2_STD_NTSC_M;
+		} else {
+			device_printf(dev,"Eeprom PAL\n");
+			jan = V4L2_STD_PAL;
+		}
+
+		tda9887_command(sc->iicbus, VIDIOC_S_STD , &jan);
+		cx25840_command(sc->iicbus, VIDIOC_S_STD , &jan);
+	}
+
+	DELAY(100000);
+
+	cx25840_command(sc->iicbus, VIDIOC_LOG_STATUS,NULL);
+#else
+	/*
+	 * Initialize the SAA7115.
+	 */
+	if (cxm_saa7115_init(sc) < 0) {
+		device_printf(dev, "could not initialize video decoder\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	/*
+	 * Initialize the MSP3400.
+	 */
+	if (cxm_msp_init(sc) < 0) {
+		device_printf(dev, "could not initialize audio decoder\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	/*
+	 * Initialize the IR Remote.
+	 */
+	if (cxm_ir_init(sc) < 0) {
+		device_printf(dev, "could not initialize IR remote\n");
+		error = ENXIO;
+		goto fail;
+	}
+#endif
+
+	sc->dec_mbx = -1;
+	sc->enc_mbx = -1;
+
+	/*
+	 * Disable the Conexant device.
+	 *
+	 * This is done * after * attaching the I2C bus so
+	 * cxm_stop_hardware can mute the video and audio
+	 * decoders.
+	 */
+	cxm_stop_hardware(sc);
+
+	/*
+	 * Allocate our interrupt.
+	 */
+	rid = 0;
+	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
+				0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
+
+	if (sc->irq_res == NULL) {
+		device_printf(dev, "could not map interrupt\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	error = bus_setup_intr(dev, sc->irq_res, 0,
+			       cxm_intr, sc, &sc->ih_cookie, NULL);
+	if (error) {
+		device_printf(dev, "could not setup irq\n");
+		goto fail;
+
+	}
+
+	/*
+	 * Allocate a DMA tag for the parent bus.
+	 */
+	error = bus_dma_tag_create(NULL, 1, 0,
+				   BUS_SPACE_MAXADDR_32BIT,
+				   BUS_SPACE_MAXADDR, NULL, NULL,
+				   BUS_SPACE_MAXSIZE_32BIT, 1,
+				   BUS_SPACE_MAXSIZE_32BIT, 0,
+				   &sc->parent_dmat);
+	if (error) {
+		device_printf(dev, "could not create parent bus DMA tag\n");
+		goto fail;
+	}
+
+	/*
+	 * Allocate a DMA tag for the encoder buffers.
+	 */
+	error = bus_dma_tag_create(sc->parent_dmat, 256, 0,
+				   BUS_SPACE_MAXADDR_32BIT,
+				   BUS_SPACE_MAXADDR, NULL, NULL,
+				   CXM_SG_SEGMENT, 1,
+				   BUS_SPACE_MAXSIZE_32BIT, 0,
+				   &sc->enc_pool.dmat);
+	if (error) {
+		device_printf(dev,
+			      "could not create encoder buffer DMA tag\n");
+		goto fail;
+	}
+
+	for (i = 0; i < CXM_SG_BUFFERS; i++) {
+
+		/*
+		 * Allocate the encoder buffer.
+		 */
+		error = bus_dmamem_alloc(sc->enc_pool.dmat,
+					 (void **)&sc->enc_pool.bufs[i].vaddr,
+					 BUS_DMA_NOWAIT,
+					 &sc->enc_pool.bufs[i].dmamap);
+		if (error) {
+			device_printf(dev,
+				      "could not allocate encoder buffer\n");
+			goto fail;
+		}
+
+		/*
+		 * Map the encoder buffer.
+		 */
+		error = bus_dmamap_load(sc->enc_pool.dmat,
+					sc->enc_pool.bufs[i].dmamap,
+					sc->enc_pool.bufs[i].vaddr,
+					CXM_SG_SEGMENT,
+					cxm_mapmem,
+					&sc->enc_pool.bufs[i].baddr, 0);
+		if (error) {
+			device_printf(dev, "could not map encoder buffer\n");
+			goto fail;
+		}
+	}
+
+	/*
+	 * Allocate a DMA tag for the scatter / gather list.
+	 */
+	error = bus_dma_tag_create(sc->parent_dmat, 1, 0,
+				   BUS_SPACE_MAXADDR_32BIT,
+				   BUS_SPACE_MAXADDR, NULL, NULL,
+				   CXM_SG_BUFFERS
+				   * sizeof(struct cxm_sg_entry), 1,
+				   BUS_SPACE_MAXSIZE_32BIT, 0,
+				   &sc->enc_sg.dmat);
+	if (error) {
+		device_printf(dev,
+			      "could not create scatter / gather DMA tag\n");
+		goto fail;
+	}
+
+	/*
+	 * Allocate the scatter / gather list.
+	 */
+	error = bus_dmamem_alloc(sc->enc_sg.dmat, (void **)&sc->enc_sg.vaddr,
+				 BUS_DMA_NOWAIT, &sc->enc_sg.dmamap);
+	if (error) {
+		device_printf(dev,
+			      "could not allocate scatter / gather list\n");
+		goto fail;
+	}
+
+	/*
+	 * Map the scatter / gather list.
+	 */
+	error = bus_dmamap_load(sc->enc_sg.dmat, sc->enc_sg.dmamap,
+				sc->enc_sg.vaddr,
+				CXM_SG_BUFFERS * sizeof(struct cxm_sg_entry),
+				cxm_mapmem, &sc->enc_sg.baddr, 0);
+	if (error) {
+		device_printf(dev, "could not map scatter / gather list\n");
+		goto fail;
+	}
+
+	/*
+	 * Initialize the hardware.
+	 */
+	if (cxm_init_hardware(sc) < 0) {
+		device_printf(dev, "could not initialize hardware\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+#ifdef CXM_PVR150
+	if (cxm_eeprom_get_std(sc))
+		sc->profile = &dvd_full_d1_pal_profile;
+	else
+#endif
+		sc->profile = &dvd_full_d1_ntsc_profile;
+
+	sc->source = cxm_tuner_source;
+
+
+	/* make the device entries */
+	dev_ops_add(&cxm_ops, -1, unit);
+	sc->cxm_dev_t = make_dev(&cxm_ops, unit,
+				0, 0, 0444, "cxm%d",  unit);
+
+	return 0;
+
+fail:
+	if (sc->enc_sg.baddr)
+		bus_dmamap_unload(sc->enc_sg.dmat, sc->enc_sg.dmamap);
+	if (sc->enc_sg.vaddr)
+		bus_dmamem_free(sc->enc_sg.dmat, sc->enc_sg.vaddr,
+				sc->enc_sg.dmamap);
+	if (sc->enc_sg.dmat)
+		bus_dma_tag_destroy(sc->enc_sg.dmat);
+
+	for (i = 0; i < CXM_SG_BUFFERS; i++) {
+		if (sc->enc_pool.bufs[i].baddr)
+			bus_dmamap_unload(sc->enc_pool.dmat,
+					  sc->enc_pool.bufs[i].dmamap);
+		if (sc->enc_pool.bufs[i].vaddr)
+			bus_dmamem_free(sc->enc_pool.dmat,
+					sc->enc_pool.bufs[i].vaddr,
+					sc->enc_pool.bufs[i].dmamap);
+	}
+
+	if (sc->enc_pool.dmat)
+		bus_dma_tag_destroy(sc->enc_pool.dmat);
+
+	if (sc->parent_dmat)
+		bus_dma_tag_destroy(sc->parent_dmat);
+
+	/*
+	 * Detach the I2C bus.
+	 *
+	 * This is done * after * deallocating the scatter / gather
+	 * list and buffers so the kernel has a better chance of
+	 * gracefully handling a memory shortage.
+	 *
+	 * Detach the children before recursively deleting
+	 * in case a child has a pointer to a grandchild
+	 * which is used by the child's detach routine.
+	 */
+	bus_generic_detach(dev);
+	if (sc->cxm_iic)
+		device_delete_child(dev, sc->cxm_iic);
+
+	if (sc->ih_cookie)
+		bus_teardown_intr(dev, sc->irq_res, sc->ih_cookie);
+	if (sc->irq_res)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	if (sc->mem_res)
+		bus_release_resource(dev, SYS_RES_MEMORY, CXM_RID, sc->mem_res);
+
+	return error;
+}
+
+/*
+ * the detach routine.
+ */
+static int
+cxm_detach( device_t dev )
+{
+	unsigned int i;
+	struct cxm_softc *sc;
+	device_t child;
+
+	/* Get the device data */
+	sc = device_get_softc(dev);
+
+	/* Disable the Conexant device. */
+	cxm_stop_hardware(sc);
+
+	/* Unregister the /dev/cxmN device. */
+	dev_ops_remove(&cxm_ops, 0, device_get_unit(dev));
+
+	/*
+	 * Deallocate scatter / gather list and buffers.
+	 */
+	bus_dmamap_unload(sc->enc_sg.dmat, sc->enc_sg.dmamap);
+	bus_dmamem_free(sc->enc_sg.dmat, sc->enc_sg.vaddr, sc->enc_sg.dmamap);
+
+	bus_dma_tag_destroy(sc->enc_sg.dmat);
+
+	for (i = 0; i < CXM_SG_BUFFERS; i++) {
+		bus_dmamap_unload(sc->enc_pool.dmat,
+				  sc->enc_pool.bufs[i].dmamap);
+		bus_dmamem_free(sc->enc_pool.dmat, sc->enc_pool.bufs[i].vaddr,
+				sc->enc_pool.bufs[i].dmamap);
+	}
+
+	bus_dma_tag_destroy(sc->enc_pool.dmat);
+
+	bus_dma_tag_destroy(sc->parent_dmat);
+
+	/*
+	 * Detach the I2C bus.
+	 *
+	 * This is done * after * deallocating the scatter / gather
+	 * list and buffers so the kernel has a better chance of
+	 * gracefully handling a memory shortage.
+	 *
+	 * Detach the children before recursively deleting
+	 * in case a child has a pointer to a grandchild
+	 * which is used by the child's detach routine.
+	 *
+	 * Remember the child before detaching so we can
+	 * delete it (bus_generic_detach indirectly zeroes
+	 * sc->child_dev).
+	 */
+	child = sc->cxm_iic;
+	bus_generic_detach(dev);
+	if (child)
+		device_delete_child(dev, child);
+
+	/* Deallocate resources. */
+	bus_teardown_intr(dev, sc->irq_res, sc->ih_cookie);
+	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+	bus_release_resource(dev, SYS_RES_MEMORY, CXM_RID, sc->mem_res);
+
+	return 0;
+}
+
+/*
+ * the shutdown routine.
+ */
+static int
+cxm_shutdown( device_t dev )
+{
+	struct cxm_softc *sc = device_get_softc(dev);
+
+	/* Disable the Conexant device. */
+	cxm_stop_hardware(sc);
+
+	return 0;
+}
+
+/*
+ * the interrupt routine.
+ */
+static void
+cxm_intr( void *arg )
+{
+	u_int32_t status;
+	struct cxm_softc *sc;
+
+	/* Get the device data */
+	sc = (struct cxm_softc *)arg;
+
+	status = CSR_READ_4(sc, CXM_REG_IRQ_STATUS);
+
+	status &= ~sc->irq_mask;
+
+	if (! status )
+		return;
+
+	/* Process DMA done before handling a new DMA request or EOS */
+	if (status & CXM_IRQ_ENC_DMA_DONE)
+		cxm_encoder_dma_done(sc);
+
+	if (status & CXM_IRQ_ENC_DMA_REQUEST)
+		cxm_encoder_dma_request(sc);
+
+	if (status & CXM_IRQ_ENC_EOS) {
+		sc->encoding_eos = 1;
+		wakeup(&sc->encoding_eos);
+	}
+
+	cxm_set_irq_status(sc, status);
+}
+
+
+/*
+ * the child detached routine.
+ */
+static void
+cxm_child_detached( device_t dev, device_t child )
+{
+	struct cxm_softc *sc;
+
+	/* Get the device data */
+	sc = device_get_softc(dev);
+
+	if (child == sc->cxm_iic)
+		sc->cxm_iic = NULL;
+}
+
+
+static int
+cxm_read_ivar( device_t dev, device_t child, int index, uintptr_t* val)
+{
+	struct cxm_softc *sc;
+
+	/* Get the device data */
+	sc = device_get_softc(dev);
+
+	switch (index) {
+	case CXM_IVAR_BHANDLE:
+		*(bus_space_handle_t **)val = &sc->bhandle;
+		break;
+
+	case CXM_IVAR_BTAG:
+		*(bus_space_tag_t **)val = &sc->btag;
+		break;
+
+	case CXM_IVAR_IICBUS:
+		*(device_t **)val = &sc->iicbus;
+		break;
+
+	default:
+		return ENOENT;
+	}
+
+	return 0;
+}
+
+
+static int
+cxm_write_ivar( device_t dev, device_t child, int index, uintptr_t val)
+{
+	struct cxm_softc *sc;
+
+	/* Get the device data */
+	sc = device_get_softc(dev);
+
+	switch (index) {
+	case CXM_IVAR_BHANDLE:
+		return EINVAL;
+
+	case CXM_IVAR_BTAG:
+		return EINVAL;
+
+	case CXM_IVAR_IICBUS:
+		if (sc->iicbus)
+			return EINVAL;
+		sc->iicbus = val ? *(device_t *)val : NULL;
+		break;
+
+	default:
+		return ENOENT;
+	}
+
+	return 0;
+}
+
+
+/*---------------------------------------------------------
+**
+**	Conexant iTVC15 / iTVC16 character device driver routines
+**
+**---------------------------------------------------------
+*/
+
+#define UNIT(x)		((x) & 0x0f)
+#define FUNCTION(x)	(x >> 4)
+
+/*
+ *
+ */
+int
+cxm_open( struct dev_open_args *ap )
+{
+	cdev_t		dev = ap->a_head.a_dev;
+	int		unit;
+	struct cxm_softc *sc;
+
+	unit = UNIT( minor(dev) );
+
+	/* Get the device data */
+	sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
+	if (sc == NULL) {
+		/* the device is no longer valid/functioning */
+		return ENXIO;
+	}
+
+#ifdef CXM_PVR150
+	sc->is_opened++;
+#else
+	if (sc->is_opened)
+		return EBUSY;
+
+	sc->is_opened = 1;
+#endif
+
+	sc->mpeg = 1;
+
+	/* Record that the device is now busy */
+	device_busy(devclass_get_device(cxm_devclass, unit));
+
+	return 0;
+}
+
+
+/*
+ *
+ */
+int
+cxm_close( struct dev_close_args *ap )
+{
+	cdev_t		dev = ap->a_head.a_dev;
+	int		unit;
+	struct cxm_softc *sc;
+
+	unit = UNIT( minor(dev) );
+
+	/* Get the device data */
+	sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
+	if (sc == NULL) {
+		/* the device is no longer valid/functioning */
+		return ENXIO;
+	}
+
+	if (cxm_stop_encoder(sc) < 0)
+		return ENXIO;
+
+	sc->enc_pool.offset = 0;
+	sc->enc_pool.read = 0;
+	sc->enc_pool.write = 0;
+
+	sc->enc_proc = NULL;
+	sc->enc_signal = 0;
+
+	device_unbusy(devclass_get_device(cxm_devclass, unit));
+
+	sc->is_opened = 0;
+
+	return 0;
+}
+
+
+/*
+ *
+ */
+int
+cxm_read( struct dev_read_args *ap )
+{
+	cdev_t		dev = ap->a_head.a_dev;
+	int		buffers_available;
+	int		buffers_read;
+	int		error;
+	int		unit;
+	unsigned int	current;
+#ifdef CXM_PVR150
+	unsigned int	encread;
+#else
+	unsigned int	i;
+#endif
+	size_t		nbytes;
+	size_t		offset;
+	struct cxm_softc *sc;
+
+	unit = UNIT( minor(dev) );
+
+	/* Get the device data */
+	sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
+	if (sc == NULL) {
+		/* the device is no longer valid/functioning */
+		return ENXIO;
+	}
+
+	/* Only trigger the encoder if the ring buffer is empty */
+	if (! sc->encoding && sc->enc_pool.read == sc->enc_pool.write) {
+		if (cxm_start_encoder(sc) < 0)
+			return ENXIO;
+		if (ap->a_ioflag & IO_NDELAY)
+			return EWOULDBLOCK;
+	}
+
+#ifdef CXM_PVR150
+	buffers_read = 0;
+	do {
+		crit_enter();
+		encread = sc->enc_pool.read = (sc->enc_pool.read + buffers_read) % CXM_SG_BUFFERS;
+		buffers_read = 0;
+		buffers_available = sc->enc_pool.write - sc->enc_pool.read;
+		offset = sc->enc_pool.offset;
+		crit_exit();
+		if (buffers_available < 0)
+			buffers_available += CXM_SG_BUFFERS;
+
+		if(!ap->a_uio->uio_resid)
+			break;
+
+		if(buffers_available == 0) {
+			error = tsleep(&sc->enc_pool.read, PCATCH, "cmxrd", 0);
+
+			if(error)
+				return error;
+			else
+				continue;
+		} else {
+			current = cxm_encoder_fixup_byte_order (sc, encread, offset);
+
+			nbytes = sc->enc_pool.bufs[current].size - offset;
+
+			/* Don't transfer more than requested */
+			if (nbytes > ap->a_uio->uio_resid)
+				nbytes = ap->a_uio->uio_resid;
+
+			error = uiomove(sc->enc_pool.bufs[current].vaddr + offset,
+				 nbytes, ap->a_uio);
+
+			if (error)
+				return error;
+
+			offset += nbytes;
+
+			/* Handle a partial read of a buffer */
+			if (! ap->a_uio->uio_resid && offset != sc->enc_pool.bufs[current].size) {
+				sc->enc_pool.offset = offset;
+				/* no need to update the books! */
+				break;
+			}
+
+			sc->enc_pool.offset = offset = 0;
+			buffers_read = 1;
+		}
+	} while(1);
+#else
+	buffers_available = 0;
+
+	crit_enter();
+	while (sc->enc_pool.read == sc->enc_pool.write) {
+		error = tsleep(&sc->enc_pool.read, PCATCH, "cmxrd", 0);
+		if (error) {
+			crit_exit();
+			return error;
+		}
+	}
+
+	/*
+	 * Determine the number of buffers available at this * instant *
+	 * taking in consideration that the ring buffer wraps.
+	 */
+	buffers_available = sc->enc_pool.write - sc->enc_pool.read;
+	if (buffers_available < 0)
+		buffers_available += CXM_SG_BUFFERS;
+	crit_exit();
+
+	offset = sc->enc_pool.offset;
+
+	for (buffers_read = 0, i = sc->enc_pool.read;
+	     buffers_read != buffers_available && ap->a_uio->uio_resid;
+	     buffers_read++, i = (i + 1) % CXM_SG_BUFFERS) {
+
+		current = cxm_encoder_fixup_byte_order (sc, i, offset);
+
+		nbytes = sc->enc_pool.bufs[current].size - offset;
+
+		/* Don't transfer more than requested */
+		if (nbytes > ap->a_uio->uio_resid)
+			nbytes = ap->a_uio->uio_resid;
+
+		error = uiomove(sc->enc_pool.bufs[current].vaddr + offset,
+				nbytes, ap->a_uio);
+		if (error)
+			return error;
+
+		offset += nbytes;
+
+		/* Handle a partial read of a buffer */
+		if (! ap->a_uio->uio_resid && offset != sc->enc_pool.bufs[i].size)
+			break;
+
+		offset = 0;
+	}
+
+	sc->enc_pool.offset = offset;
+
+	/* Update the books */
+	crit_enter();
+	sc->enc_pool.read = (sc->enc_pool.read + buffers_read)
+			    % CXM_SG_BUFFERS;
+	crit_exit();
+#endif
+
+	return 0;
+}
+
+
+/*
+ *
+ */
+int
+cxm_ioctl( struct dev_ioctl_args *ap )
+{
+	cdev_t		dev = ap->a_head.a_dev;
+	int		brightness;
+	int		chroma_saturation;
+	int		contrast;
+	int		fps;
+	int		hue;
+	int		result;
+	int		status;
+	int		unit;
+	unsigned int	i;
+	unsigned int	sig;
+	unsigned long	freq;
+	struct cxm_softc *sc;
+	enum cxm_source	source;
+	struct bktr_capture_area *cap;
+	struct bktr_remote *remote;
+
+	unit = UNIT( minor(dev) );
+
+	/* Get the device data */
+	sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
+	if (sc == NULL) {
+		/* the device is no longer valid/functioning */
+		return ENXIO;
+	}
+
+	switch (ap->a_cmd) {
+	case BT848_GAUDIO:
+		switch (cxm_msp_selected_source(sc)) {
+		case cxm_tuner_source:
+			*(int *) ap->a_data = AUDIO_TUNER;
+			break;
+
+		case cxm_line_in_source_composite:
+		case cxm_line_in_source_svideo:
+			*(int *) ap->a_data = AUDIO_EXTERN;
+			break;
+
+		case cxm_fm_source:
+			*(int *) ap->a_data = AUDIO_INTERN;
+			break;
+
+		default:
+			return ENXIO;
+		}
+
+		if (cxm_msp_is_muted(sc) == 1)
+			*(int *) ap->a_data |= AUDIO_MUTE;
+		break;
+
+	case BT848_SAUDIO:
+		source = cxm_unknown_source;
+
+		switch (*(int *) ap->a_data) {
+		case AUDIO_TUNER:
+			source = cxm_tuner_source;
+			break;
+
+		case AUDIO_EXTERN:
+			source = cxm_line_in_source_composite;
+			break;
+
+		case AUDIO_INTERN:
+			source = cxm_fm_source;
+			break;
+
+		case AUDIO_MUTE:
+			if (cxm_msp_mute(sc) < 0)
+				return ENXIO;
+			return 0;
+
+		case AUDIO_UNMUTE:
+			if (cxm_msp_unmute(sc) < 0)
+				return ENXIO;
+			return 0;
+
+		default:
+			return EINVAL;
+		}
+
+		if (sc->encoding) {
+
+			/*
+			 * Switching between audio + video and audio only
+			 * subtypes isn't supported while encoding.
+			 */
+
+			if (source != sc->source
+			    && (source == cxm_fm_source
+				|| sc->source == cxm_fm_source))
+				return EBUSY;
+		}
+
+		if (cxm_pause_encoder(sc) < 0)
+			return ENXIO;
+
+		if (cxm_msp_select_source(sc, source) < 0)
+			return ENXIO;
+
+		if (source == cxm_fm_source)
+			sc->source = source;
+
+		result = cxm_encoder_wait_for_lock(sc);
+		if (result < 0)
+			return ENXIO;
+		else if (result == 0)
+			return EINVAL;
+
+		if (cxm_unpause_encoder(sc) < 0)
+			return ENXIO;
+		break;
+
+	case BT848_GBRIG:
+		brightness = cxm_saa7115_get_brightness(sc);
+
+		if (brightness < 0)
+			return ENXIO;
+
+		/*
+		 * Brooktree brightness:
+		 * 0x80 = -50.0%, 0x00 = +0.0%, 0x7f = +49.6%
+		 */
+		*(int *)ap->a_data = (int)(unsigned char)brightness - 128;
+		break;
+
+	case BT848_SBRIG:
+
+		/*
+		 * Brooktree brightness:
+		 * 0x80 = -50.0%, 0x00 = +0.0%, 0x7f = +49.6%
+		 */
+		brightness = *(int *)ap->a_data + 128;
+
+		if (cxm_saa7115_set_brightness(sc, brightness) < 0)
+			return ENXIO;
+		break;
+
+	case METEORGBRIG:
+		brightness = cxm_saa7115_get_brightness(sc);
+
+		if (brightness < 0)
+			return ENXIO;
+
+		*(unsigned char *)ap->a_data = (unsigned char)brightness;
+		break;
+
+	case METEORSBRIG:
+		brightness = *(unsigned char *)ap->a_data;
+
+		if (cxm_saa7115_set_brightness(sc, brightness) < 0)
+			return ENXIO;
+		break;
+
+	case BT848_GCSAT:
+		chroma_saturation = cxm_saa7115_get_chroma_saturation(sc);
+
+		if (chroma_saturation < 0)
+			return ENXIO;
+
+		/*
+		 * Brooktree chroma saturation:
+		 * 0x000 = 0%, 0x0fe = 100%, 0x1ff = 201.18%
+		 */
+		*(int *)ap->a_data = ((signed char)chroma_saturation > 0)
+				? (chroma_saturation * 4 - 2) : 0;
+		break;
+
+	case BT848_SCSAT:
+
+		/*
+		 * Brooktree chroma saturation:
+		 * 0x000 = 0%, 0x0fe = 100%, 0x1ff = 201.18%
+		 */
+		chroma_saturation = (*(int *)ap->a_data & 0x1ff) < 510
+				      ? ((*(int *)ap->a_data & 0x1ff) + 2) / 4 : 127;
+
+		if (cxm_saa7115_set_chroma_saturation(sc, chroma_saturation)
+		    < 0)
+			return ENXIO;
+
+		break;
+
+	case METEORGCSAT:
+		chroma_saturation = cxm_saa7115_get_chroma_saturation(sc);
+
+		if (chroma_saturation < 0)
+			return ENXIO;
+
+		*(unsigned char *)ap->a_data = (unsigned char)chroma_saturation;
+		break;
+
+	case METEORSCSAT:
+		chroma_saturation = *(unsigned char *)ap->a_data;
+
+		if (cxm_saa7115_set_chroma_saturation(sc, chroma_saturation)
+		    < 0)
+			return ENXIO;
+		break;
+
+	case METEORGCONT:
+		contrast = cxm_saa7115_get_contrast(sc);
+
+		if (contrast < 0)
+			return ENXIO;
+
+		*(unsigned char *)ap->a_data = (unsigned char)contrast;
+		break;
+
+	case METEORSCONT:
+		contrast = *(unsigned char *)ap->a_data;
+
+		if (cxm_saa7115_set_contrast(sc, contrast) < 0)
+			return ENXIO;
+		break;
+
+	case BT848_GHUE:
+		hue = cxm_saa7115_get_hue(sc);
+
+		if (hue < 0)
+			return ENXIO;
+
+		*(int *)ap->a_data = (signed char)hue;
+		break;
+
+	case BT848_SHUE:
+		hue = *(int *)ap->a_data;
+
+		if (cxm_saa7115_set_hue(sc, hue) < 0)
+			return ENXIO;
+		break;
+
+	case METEORGHUE:
+		hue = cxm_saa7115_get_hue(sc);
+
+		if (hue < 0)
+			return ENXIO;
+
+		*(signed char *)ap->a_data = (signed char)hue;
+		break;
+
+	case METEORSHUE:
+		hue = *(signed char *)ap->a_data;
+
+		if (cxm_saa7115_set_hue(sc, hue) < 0)
+			return ENXIO;
+		break;
+
+	case METEORCAPTUR:
+		switch (*(int *) ap->a_data) {
+		case METEOR_CAP_CONTINOUS:
+			if (cxm_start_encoder(sc) < 0)
+				return ENXIO;
+			break;
+
+		case METEOR_CAP_STOP_CONT:
+			if (cxm_stop_encoder(sc) < 0)
+				return ENXIO;
+			break;
+
+		default:
+			return EINVAL;
+		}
+		break;
+
+	case BT848_GCAPAREA:
+		cap = (struct bktr_capture_area *)ap->a_data;
+		memset (cap, 0, sizeof (*cap));
+		cap->x_offset = 0;
+		cap->y_offset = 0;
+		cap->x_size = sc->profile->width;
+		cap->y_size = sc->profile->height;
+		break;
+
+	case BT848_SCAPAREA:
+		if (sc->encoding)
+			return EBUSY;
+
+		cap = (struct bktr_capture_area *)ap->a_data;
+		if (cap->x_offset || cap->y_offset
+		    || (cap->x_size % CXM_MACROBLOCK_WIDTH)
+		    || (cap->y_size % CXM_MACROBLOCK_HEIGHT))
+			return EINVAL;
+
+		/*
+		 * Setting the width and height has the side effect of
+		 * chosing between the VCD, SVCD, and DVD profiles.
+		 */
+
+		for (i = 0; i < NUM_ELEMENTS(codec_profiles); i++)
+			if (codec_profiles[i]->width == cap->x_size
+			    && codec_profiles[i]->height == cap->y_size)
+				break;
+
+		if (i >= NUM_ELEMENTS(codec_profiles))
+			return EINVAL;
+
+		sc->profile = codec_profiles[i];
+		break;
+
+	case BT848GFMT:
+		switch (cxm_saa7115_detected_format(sc)) {
+		case cxm_ntsc_60hz_source_format:
+			*(unsigned long *)ap->a_data = BT848_IFORM_F_NTSCM;
+			break;
+
+		case cxm_pal_50hz_source_format:
+			*(unsigned long *)ap->a_data = BT848_IFORM_F_PALBDGHI;
+			break;
+
+		case cxm_secam_50hz_source_format:
+			*(unsigned long *)ap->a_data = BT848_IFORM_F_SECAM;
+			break;
+
+		case cxm_pal_60hz_source_format:
+			*(unsigned long *)ap->a_data = BT848_IFORM_F_PALM;
+			break;
+
+		case cxm_bw_50hz_source_format:
+		case cxm_bw_60hz_source_format:
+		case cxm_ntsc_50hz_source_format:
+			*(unsigned long *)ap->a_data = BT848_IFORM_F_AUTO;
+			break;
+
+		default:
+			return ENXIO;
+		}
+		break;
+
+	case METEORGFMT:
+		switch (cxm_saa7115_detected_format(sc)) {
+		case cxm_ntsc_60hz_source_format:
+			*(unsigned long *)ap->a_data = METEOR_FMT_NTSC;
+			break;
+
+		case cxm_pal_50hz_source_format:
+			*(unsigned long *)ap->a_data = METEOR_FMT_PAL;
+			break;
+
+		case cxm_secam_50hz_source_format:
+			*(unsigned long *)ap->a_data = METEOR_FMT_SECAM;
+			break;
+
+		case cxm_bw_50hz_source_format:
+		case cxm_bw_60hz_source_format:
+		case cxm_ntsc_50hz_source_format:
+		case cxm_pal_60hz_source_format:
+			*(unsigned long *)ap->a_data = METEOR_FMT_AUTOMODE;
+			break;
+
+		default:
+			return ENXIO;
+		}
+		break;
+
+	case METEORGFPS:
+		fps = cxm_saa7115_detected_fps(sc);
+
+		if (fps < 0)
+			return ENXIO;
+
+		*(unsigned short *)ap->a_data = fps;
+		break;
+
+	case METEORGINPUT:
+		switch (sc->source) {
+		case cxm_tuner_source:
+			*(unsigned long *)ap->a_data = METEOR_INPUT_DEV1;
+			break;
+
+		case cxm_line_in_source_composite:
+			*(unsigned long *)ap->a_data = METEOR_INPUT_DEV2;
+			break;
+
+		case cxm_line_in_source_svideo:
+			*(unsigned long *)ap->a_data = METEOR_INPUT_DEV_SVIDEO;
+			break;
+
+		default:
+			return ENXIO;
+		}
+		break;
+
+	case METEORSINPUT:
+		source = cxm_unknown_source;
+
+		switch (*(unsigned long *)ap->a_data & 0xf000) {
+		case METEOR_INPUT_DEV1:
+#ifdef CXM_PVR150
+		{
+			int arg;
+#endif
+			source = cxm_tuner_source;
+#ifdef CXM_PVR150
+			arg = AUDIO_TUNER;
+			cx25840_command(sc->iicbus, AUDC_SET_INPUT, &arg);
+			arg=CX25840_TUNER;
+			cx25840_command(sc->iicbus, VIDIOC_S_INPUT, &arg);
+		}
+#endif
+			break;
+
+		case METEOR_INPUT_DEV2:
+#ifdef CXM_PVR150
+		{
+			int arg;
+#endif
+			source = cxm_line_in_source_composite;
+#ifdef CXM_PVR150
+			arg = AUDIO_EXTERN_1;
+			cx25840_command(sc->iicbus, AUDC_SET_INPUT, &arg);
+			arg=CX25840_COMPOSITE0;
+			cx25840_command(sc->iicbus, VIDIOC_S_INPUT, &arg);
+		}
+#endif
+			break;
+
+		case METEOR_INPUT_DEV_SVIDEO:
+#ifdef CXM_PVR150
+		{
+			int arg;
+#endif
+			source = cxm_line_in_source_svideo;
+#ifdef CXM_PVR150
+			arg = AUDIO_EXTERN_1;
+			cx25840_command(sc->iicbus, AUDC_SET_INPUT, &arg);
+			arg=CX25840_SVIDEO0;
+			cx25840_command(sc->iicbus, VIDIOC_S_INPUT, &arg);
+		}
+#endif
+			break;
+
+		default:
+			 return EINVAL;
+		}
+
+		if (sc->encoding) {
+
+			/*
+			 * Switching between audio + video and audio only
+			 * subtypes isn't supported while encoding.
+			 */
+
+			if (source != sc->source
+			    && (source == cxm_fm_source
+				|| sc->source == cxm_fm_source))
+				return EBUSY;
+		}
+
+		if (cxm_pause_encoder(sc) < 0)
+			return ENXIO;
+
+#ifndef CXM_PVR150
+		if (cxm_saa7115_select_source(sc, source) < 0)
+			return ENXIO;
+		if (cxm_msp_select_source(sc, source) < 0)
+			return ENXIO;
+#endif
+		sc->source = source;
+
+		result = cxm_encoder_wait_for_lock(sc);
+		if (result < 0)
+			return ENXIO;
+		else if (result == 0)
+			return EINVAL;
+
+		if (cxm_unpause_encoder(sc) < 0)
+			return ENXIO;
+		break;
+
+	case METEORGSIGNAL:
+		*(unsigned int *)ap->a_data = sc->enc_signal;
+		break;
+
+	case METEORSSIGNAL:
+		sig = *(unsigned int *)ap->a_data;
+
+		if (! _SIG_VALID(sig) )
+			return EINVAL;
+
+		/*
+		 * Historically, applications used METEOR_SIG_MODE_MASK
+		 * to reset signal delivery.
+		 */
+		if (sig == METEOR_SIG_MODE_MASK)
+			sig = 0;
+
+		crit_enter();
+		sc->enc_proc = sig ? curproc : NULL;
+		sc->enc_signal = sig;
+		crit_exit();
+		break;
+
+	case RADIO_GETFREQ:
+		/* Convert from kHz to MHz * 100 */
+		freq = sc->tuner_freq / 10;
+
+		*(unsigned int *)ap->a_data = freq;
+		break;
+
+	case RADIO_SETFREQ:
+		if (sc->source == cxm_fm_source)
+			if (cxm_pause_encoder(sc) < 0)
+				return ENXIO;
+
+		/* Convert from MHz * 100 to kHz */
+		freq = *(unsigned int *)ap->a_data * 10;
+
+		if (cxm_tuner_select_frequency(sc, cxm_tuner_fm_freq_type,
+					       freq) < 0)
+			return ENXIO;
+
+		/*
+		 * Explicitly wait for the tuner lock so we
+		 * can indicate if there's a station present.
+		 */
+		if (cxm_tuner_wait_for_lock(sc) < 0)
+			return EINVAL;
+
+		result = cxm_encoder_wait_for_lock(sc);
+		if (result < 0)
+			return ENXIO;
+		else if (result == 0)
+			return EINVAL;
+
+		if (sc->source == cxm_fm_source)
+			if (cxm_unpause_encoder(sc) < 0)
+				return ENXIO;
+		break;
+
+	case TVTUNER_GETAFC:
+		*(int *)ap->a_data = sc->tuner_afc;
+		break;
+
+	case TVTUNER_SETAFC:
+		sc->tuner_afc = (*(int *)ap->a_data != 0);
+		break;
+
+	case TVTUNER_GETTYPE:
+		*(unsigned int *)ap->a_data = cxm_tuner_selected_channel_set(sc);
+		break;
+
+	case TVTUNER_SETTYPE:
+		if (cxm_tuner_select_channel_set(sc, *(unsigned int *)ap->a_data) < 0)
+			return EINVAL;
+		break;
+
+	case TVTUNER_SETCHNL:
+#ifndef CXM_PVR150
+		if (sc->source == cxm_tuner_source)
+			if (cxm_pause_encoder(sc) < 0)
+				return ENXIO;
+#endif
+
+		if (cxm_tuner_select_channel(sc, *(unsigned int *)ap->a_data) < 0)
+			return ENXIO;
+
+		if (sc->tuner_afc)
+			if (cxm_tuner_apply_afc(sc) < 0)
+				return EINVAL;
+
+#ifdef CXM_PVR150
+		/*
+		 * Explicitly wait for the tuner lock so we
+		 * can indicate if there's a station present.
+		 */
+		if (cxm_tuner_wait_for_lock(sc) < 0)
+			return EINVAL;
+
+		result = cxm_encoder_wait_for_lock(sc);
+		if (result < 0)
+			return ENXIO;
+		else if (result == 0)
+			return EINVAL;
+
+		if (sc->source == cxm_tuner_source)
+			if (cxm_unpause_encoder(sc) < 0)
+				return ENXIO;
+#endif
+		break;
+
+	case TVTUNER_GETFREQ:
+		/* Convert from kHz to MHz * 16 */
+		freq = (sc->tuner_freq * 16) / 1000;
+
+		*(unsigned int *)ap->a_data = freq;
+		break;
+
+	case TVTUNER_SETFREQ:
+		if (sc->source == cxm_tuner_source)
+			if (cxm_pause_encoder(sc) < 0)
+				return ENXIO;
+
+		/* Convert from MHz * 16 to kHz */
+		freq = (*(unsigned int *)ap->a_data * 1000) / 16;
+
+		if (cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type,
+					       freq) < 0)
+			return ENXIO;
+
+		/*
+		 * Explicitly wait for the tuner lock so we
+		 * can indicate if there's a station present.
+		 */
+		if (cxm_tuner_wait_for_lock(sc) < 0)
+			return EINVAL;
+
+		result = cxm_encoder_wait_for_lock(sc);
+		if (result < 0)
+			return ENXIO;
+		else if (result == 0)
+			return EINVAL;
+
+		if (sc->source == cxm_tuner_source)
+			if (cxm_unpause_encoder(sc) < 0)
+				return ENXIO;
+
+#ifdef CXM_PVR150
+		cx25840_command(sc->iicbus, VIDIOC_LOG_STATUS, NULL);
+#endif
+		break;
+
+	case TVTUNER_GETSTATUS:
+		status = cxm_tuner_status(sc);
+		if (status < 0)
+			return ENXIO;
+		*(unsigned long *)ap->a_data = status & 0xff;
+		break;
+
+	case REMOTE_GETKEY:
+		remote = (struct bktr_remote *)ap->a_data;
+		if (cxm_ir_key(sc, (char *)remote, sizeof(*remote)) < 0)
+			return ENXIO;
+		break;
+
+	default:
+		return ENOTTY;
+	}
+
+	return 0;
+}
+
+
+int cxm_poll( struct dev_poll_args *ap)
+{
+	cdev_t		dev = ap->a_head.a_dev;
+	int		revents;
+	int		unit;
+	struct cxm_softc *sc;
+
+	unit = UNIT( minor(dev) );
+
+	/* Get the device data */
+	sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
+	if (sc == NULL) {
+		/* the device is no longer valid/functioning */
+		return POLLHUP;
+	}
+
+	revents = 0;
+
+	crit_enter();
+	if (ap->a_events & (POLLIN | POLLRDNORM)) {
+		if (sc->enc_pool.read == sc->enc_pool.write)
+			selrecord(curthread, &sc->enc_sel);
+		else
+			revents = ap->a_events & (POLLIN | POLLRDNORM);
+	}
+	crit_exit();
+
+	return revents;
+}
diff --git a/sys/dev/video/cxm/cxm.h b/sys/dev/video/cxm/cxm.h
new file mode 100644
index 0000000..7ba2217
--- /dev/null
+++ b/sys/dev/video/cxm/cxm.h
@@ -0,0 +1,697 @@
+/*
+ * Copyright (c) 2003, 2004, 2005
+ *	John Wehle <john@feith.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by John Wehle.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CXM_H
+#define _CXM_H
+
+/*
+ * Header file for the Conexant MPEG-2 Codec driver.
+ */
+
+#include <sys/selinfo.h>
+
+#include <bus/pci/pcidevs.h>
+
+#include "opt_cxm.h"
+
+#define bswap32(X) ntohl(X)
+
+#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(*array))
+
+/*
+ * For simplicity several large buffers allocate during
+ * driver attachment which normally occurs early on
+ * (when large areas of memory are available) are used
+ * to move data to / from the card.  It's not unusual
+ * for the memory allocation to fail due to fragmentation
+ * if the driver is loaded after the system has been
+ * running for a while.  One solution is to allocate
+ * several PAGE_SIZE buffers instead, however it doesn't
+ * seem worth the trouble.
+ */
+enum cxm_byte_order { cxm_unknown_byte_order,
+		      cxm_device_mpeg_byte_order, cxm_device_yuv12_byte_order,
+		      cxm_host_byte_order };
+
+struct cxm_buffer {
+	char		*vaddr;
+	bus_addr_t	baddr;
+	bus_dmamap_t	dmamap;
+	size_t		size;
+	enum cxm_byte_order byte_order;
+};
+
+#ifdef CXM_PVR150
+#define CXM_SG_BUFFERS 16
+#else
+#define CXM_SG_BUFFERS 50
+#endif
+
+struct cxm_buffer_pool {
+	bus_dma_tag_t		dmat;
+	size_t			offset;
+	unsigned int		read;
+	volatile unsigned int	write;
+	struct cxm_buffer	bufs[CXM_SG_BUFFERS];
+};
+
+/*
+ * Audio format encoding
+ *
+ * 7 6 5 4 3 2 1 0
+ *
+ *             0 0  44.1 kHz
+ *             0 1  48 kHz
+ *             1 0  32 kHz
+ *
+ *         0 1  Layer 1
+ *         1 0  Layer 2
+ *         1 1  Layer 3
+ *
+ *          L1 / L2
+ * 0 0 0 0  Free fmt
+ * 0 0 0 1  32k / 32k
+ * 0 0 1 0  64k / 48k
+ * 0 0 1 1  96k / 56k
+ * 0 1 0 0  128k / 64k
+ * 0 1 0 1  160k / 80k
+ * 0 1 1 0  192k / 96k
+ * 0 1 1 1  224k / 112k
+ * 1 0 0 0  256k / 128k
+ * 1 0 0 1  288k / 160k
+ * 1 0 1 0  320k / 192k
+ * 1 0 1 1  352k / 224k
+ * 1 1 0 0  384k / 256k
+ * 1 1 0 1  416k / 320k
+ * 1 1 1 0  448k / 384k
+ */
+struct cxm_codec_audio_format {
+	unsigned int	sample_rate;
+	u_int32_t	format;
+};
+
+struct cxm_codec_profile {
+	const char	*name;
+	u_int32_t	stream_type;
+	u_int32_t	fps;
+	u_int32_t	width;
+	u_int32_t	height;
+	u_int32_t	source_height;
+	struct {
+		u_int32_t	start;
+		u_int32_t	nlines;
+		u_int32_t	cc;
+	} vbi;
+	u_int32_t	aspect;
+	u_int32_t	pulldown;
+	struct {
+		u_int32_t	mode;
+		u_int32_t	average;
+		u_int32_t	peak;
+	} bitrate;
+	struct {
+		u_int32_t	closure;
+		u_int32_t	frames;
+		u_int32_t	bframes;
+	} gop;
+	struct {
+		u_int32_t	mode;
+		u_int32_t	type;
+		u_int32_t	spatial;
+		u_int32_t	temporal;
+	} dnr;
+
+	unsigned int	audio_sample_rate;
+};
+
+struct cxm_dev {
+	u_int16_t	vid;
+	u_int16_t	did;
+	char		*name;
+};
+
+#define CXM_MBX_FW_CMD_MAILBOX   0
+#define CXM_MBX_FW_CMD_MAILBOXES 6
+
+#define CXM_MBX_FW_DMA_MAILBOX   9
+
+#define CXM_MBX_MAX_PARAMETERS   16
+
+/* Mailbox flags bit definitions */
+#define CXM_MBX_FLAG_DRV_DONE 0x00000002
+#define CXM_MBX_FLAG_FW_DONE  0x00000004
+#define CXM_MBX_FLAG_IN_USE   0x00000001
+
+struct cxm_mailbox {
+	u_int32_t	flags;
+	u_int32_t	command;
+	u_int32_t	result;
+	u_int32_t	timeout;
+	u_int32_t	parameters[CXM_MBX_MAX_PARAMETERS];
+} __attribute__ ((packed));
+
+enum cxm_mailbox_name { cxm_unknown_mailbox,
+			cxm_dec_mailbox, cxm_enc_mailbox };
+
+/*
+ * Scatter / gather is supported with the restriction
+ * that the size of each piece must be a multiple of
+ * 256 and less than 64k.
+ */
+#ifdef CXM_PVR150
+#define CXM_SG_SEGMENT  32768
+#else
+#define CXM_SG_SEGMENT  (0xff00 & ~(PAGE_SIZE - 1))
+#endif
+
+struct cxm_sg_entry {
+	u_int32_t	src;
+	u_int32_t	dst;
+	u_int32_t	size;
+} __attribute__ ((packed));
+
+struct cxm_sg_list {
+	bus_dma_tag_t	dmat;
+	struct cxm_sg_entry *vaddr;
+	bus_addr_t	baddr;
+	bus_dmamap_t	dmamap;
+
+};
+
+enum cxm_source { cxm_unknown_source, cxm_fm_source, cxm_tuner_source,
+		  cxm_line_in_source_composite, cxm_line_in_source_svideo };
+
+enum cxm_source_format { cxm_unknown_source_format,
+			 cxm_bw_50hz_source_format,
+			 cxm_bw_60hz_source_format,
+			 cxm_ntsc_50hz_source_format,
+			 cxm_ntsc_60hz_source_format,
+			 cxm_pal_50hz_source_format,
+			 cxm_pal_60hz_source_format,
+			 cxm_secam_50hz_source_format };
+
+enum cxm_type { cxm_unknown_type, cxm_iTVC15_type, cxm_iTVC16_type };
+
+/*
+ * Conexant iTVC15 / iTVC16 info structure, one per card installed.
+ */
+struct cxm_softc {
+	enum cxm_type	type;
+	char		name[10];	/* Device name and unit number */
+
+	struct resource	*mem_res;	/* Resource descriptor for registers */
+	bus_space_tag_t	btag;		/* Bus space access functions */
+	bus_space_handle_t bhandle;	/* Bus space access functions */
+
+	struct resource *irq_res;	/* Resource descriptor for interrupt */
+	void            *ih_cookie;	/* Newbus interrupt handler cookie */
+
+	u_int32_t	irq_mask;
+
+	bus_dma_tag_t	parent_dmat;
+
+	struct cxm_buffer_pool	enc_pool;
+	struct cxm_sg_list enc_sg;
+
+	struct selinfo	enc_sel;
+
+	struct proc	*enc_proc;
+	int		enc_signal;
+
+	unsigned int	dec_mbx;
+	unsigned int	enc_mbx;
+
+#ifdef CXM_PVR150
+	unsigned int	buffers_requested;
+#endif
+
+	device_t	cxm_iic;
+	device_t	iicbus;
+
+	const struct cxm_tuner *tuner;
+	const struct cxm_tuner_channels *tuner_channels;
+	int		tuner_afc;
+	unsigned long	tuner_freq;
+
+	char		msp_name[10];
+
+	const struct cxm_codec_profile *profile;
+
+	enum cxm_source	source;
+
+	dev_t		cxm_dev;	/* bus attachment */
+	cdev_t		cxm_dev_t;	/* control device */
+	int		is_opened;
+	int		mpeg;
+
+	int		encoding;
+	int		encoding_dma;
+	int		encoding_eos;
+#ifdef CXM_PVR150
+	int 		tuner_signal;
+#endif
+};
+
+/*
+ * Conexant iTVC15 / iTVC16 I2C info structure, one per card installed.
+ */
+struct cxm_iic_softc {
+	char		name[10];	/* Device name and unit number */
+	bus_space_tag_t	btag;		/* Bus space access functions */
+	bus_space_handle_t bhandle;	/* Bus space access functions */
+
+	device_t	iicbb;
+
+};
+
+/*
+ * List of IVARS available to the I2C device driver
+ */
+#define CXM_IVAR_BHANDLE 0
+#define CXM_IVAR_BTAG    1
+#define CXM_IVAR_IICBUS  2
+
+/*
+ * Bus resource id
+ */
+#define CXM_RID PCIR_MAPS
+
+/*
+ * Access macros
+ */
+#define CSR_WRITE_4(sc, reg, val)       \
+	bus_space_write_4((sc)->btag, (sc)->bhandle, (reg), (val))
+#define CSR_WRITE_2(sc, reg, val)       \
+	bus_space_write_2((sc)->btag, (sc)->bhandle, (reg), val))
+#define CSR_WRITE_1(sc, reg, val)       \
+	bus_space_write_1((sc)->btag, (sc)->bhandle, (reg), val))
+#define CSR_READ_4(sc, reg)             \
+	bus_space_read_4((sc)->btag, (sc)->bhandle, (reg))
+#define CSR_READ_2(sc, reg)             \
+	bus_space_read_2((sc)->btag, (sc)->bhandle, (reg))
+#define CSR_READ_1(sc, reg)             \
+	bus_space_read_1((sc)->btag, (sc)->bhandle, (reg))
+
+/*
+ * Decoder / encoder firmware
+ */
+extern const char cxm_dec_fw[];
+extern const char cxm_enc_fw[];
+
+#define CXM_FW_SIZE (256 * 1024)
+
+/*
+ * Decoder / encoder memory offsets
+ */
+#define CXM_MEM_DEC 0x01000000
+#define CXM_MEM_ENC 0x00000000
+
+#define CXM_MEM_DEC_SIZE 0x01000000
+#define CXM_MEM_ENC_SIZE 0x01000000
+
+/*
+ * Register offsets
+ */
+#define CXM_REG_AO                  0x2002d00
+#define CXM_REG_APU                 0x200a064
+#define CXM_REG_DEC_SDRAM_PRECHARGE 0x20008fc
+#define CXM_REG_DEC_SDRAM_REFRESH   0x20008f8
+#define CXM_REG_DMA_STATUS          0x2000004
+#define CXM_REG_ENC_SDRAM_PRECHARGE 0x20007fc
+#define CXM_REG_ENC_SDRAM_REFRESH   0x20007f8
+#define CXM_REG_HW_BLOCKS           0x2009054
+#define CXM_REG_I2C_GETSCL          0x2007008
+#define CXM_REG_I2C_GETSDA          0x200700c
+#define CXM_REG_I2C_SETSCL          0x2007000
+#define CXM_REG_I2C_SETSDA          0x2007004
+#define CXM_REG_IRQ_MASK            0x2000048
+#define CXM_REG_IRQ_STATUS          0x2000040
+#define CXM_REG_SPU                 0x2009050
+#define CXM_REG_VDM                 0x2002800
+#define CXM_REG_VPU                 0x2009058
+
+/*
+ * Register values
+ */
+#define CXM_CMD_AO_STOP              0x00000005
+#define CXM_CMD_APU_PING             0x00000000
+#define CXM_CMD_HW_BLOCKS_RST        0xffffffff
+#define CXM_CMD_SDRAM_PRECHARGE_INIT 0x0000001a
+#define CXM_CMD_SDRAM_REFRESH_INIT   0x80000640
+#define CXM_CMD_SPU_STOP             0x00000001
+#define CXM_CMD_VDM_STOP             0x00000000
+#define CXM_CMD_VPU_STOP15           0xfffffffe
+#define CXM_CMD_VPU_STOP16           0xffffffee
+
+#define CXM_DMA_ERROR_LIST           0x00000008
+#define CXM_DMA_ERROR_READ           0x00000002
+#define CXM_DMA_ERROR_WRITE          0x00000004
+#define CXM_DMA_SUCCESS              0x00000001
+
+#define CXM_IRQ_DEC_DMA_DONE         (1 << 20)
+#define CXM_IRQ_DEC_DMA_REQUEST      (1 << 22)
+#define CXM_IRQ_DEC_VSYNC            (1 << 10)
+#define CXM_IRQ_ENC_DMA_DONE         (1 << 27)
+#define CXM_IRQ_ENC_DMA_REQUEST      (1 << 31)
+#define CXM_IRQ_ENC_EOS              (1 << 30)
+#define CXM_IRQ_ENC_EVENT            (1 << 28)
+
+#define CXM_IRQ_ENC (CXM_IRQ_ENC_DMA_REQUEST | CXM_IRQ_ENC_DMA_DONE \
+		     | CXM_IRQ_ENC_EOS | CXM_IRQ_ENC_EVENT)
+
+/*
+ * Register masks
+ */
+#define CXM_MASK_SPU_ENABLE          0xfffffffe
+#define CXM_MASK_VPU_ENABLE15        0xfffffff6
+#define CXM_MASK_VPU_ENABLE16        0xfffffffb
+
+/*
+ * Firmware commands
+ */
+#define CXM_FW_CMD_ASSIGN_3_2_PULLDOWN          0x000000b1
+#define CXM_FW_CMD_ASSIGN_ASPECT_RATIO          0x00000099
+#define CXM_FW_CMD_ASSIGN_AUDIO_PROPERTIES      0x000000bd
+#define CXM_FW_CMD_ASSIGN_BITRATES              0x00000095
+#define CXM_FW_CMD_ASSIGN_CORING_LEVELS         0x0000009f
+#define CXM_FW_CMD_ASSIGN_DMA_BLOCKLEN          0x000000c9
+#define CXM_FW_CMD_ASSIGN_DNR_FILTER_MODE       0x0000009b
+#define CXM_FW_CMD_ASSIGN_DNR_FILTER_PROPERTIES 0x0000009d
+#define CXM_FW_CMD_ASSIGN_FRAME_DROP_RATE       0x000000d0
+#define CXM_FW_CMD_ASSIGN_FRAME_RATE            0x0000008f
+#define CXM_FW_CMD_ASSIGN_FRAME_SIZE            0x00000091
+#define CXM_FW_CMD_ASSIGN_GOP_CLOSURE           0x000000c5
+#define CXM_FW_CMD_ASSIGN_GOP_PROPERTIES        0x00000097
+#define CXM_FW_CMD_ASSIGN_NUM_VSYNC_LINES       0x000000d6
+#define CXM_FW_CMD_ASSIGN_OUTPUT_PORT           0x000000bb
+#define CXM_FW_CMD_ASSIGN_PGM_INDEX_INFO        0x000000c7
+#define CXM_FW_CMD_ASSIGN_PLACEHOLDER           0x000000d8
+#define CXM_FW_CMD_ASSIGN_SPATIAL_FILTER_TYPE   0x000000a1
+#define CXM_FW_CMD_ASSIGN_STREAM_TYPE           0x000000b9
+#define CXM_FW_CMD_ASSIGN_VBI_LINE              0x000000b7
+#define CXM_FW_CMD_ASSIGN_VBI_PROPERTIES        0x000000c8
+#define CXM_FW_CMD_BEGIN_CAPTURE                0x00000081
+#define CXM_FW_CMD_DEC_EVENT_NOTIFICATION       0x00000017
+#define CXM_FW_CMD_DEC_GET_FW_VER               0x00000011
+#define CXM_FW_CMD_DEC_HALT_FW                  0x0000000e
+#define CXM_FW_CMD_ENC_EVENT_NOTIFICATION       0x000000d5
+#define CXM_FW_CMD_ENC_GET_FW_VER               0x000000c4
+#define CXM_FW_CMD_ENC_HALT_FW                  0x000000c3
+#define CXM_FW_CMD_END_CAPTURE                  0x00000082
+#define CXM_FW_CMD_INITIALIZE_VIDEO_INPUT       0x000000cd
+#define CXM_FW_CMD_MUTE_VIDEO_INPUT             0x000000d9
+#define CXM_FW_CMD_PAUSE_ENCODER                0x000000d2
+#define CXM_FW_CMD_SCHED_DMA_TO_HOST            0x000000cc
+
+#define CXM_FW_STD_TIMEOUT                          0x00010000
+
+#define CXM_FW_CAPTURE_STREAM_TYPE_MPEG             0x00000000
+#define CXM_FW_CAPTURE_STREAM_TYPE_RAW              0x00000001
+#define CXM_FW_CAPTURE_STREAM_TYPE_RAW_PASSTHROUGH  0x00000002
+#define CXM_FW_CAPTURE_STREAM_TYPE_VBI              0x00000003
+
+#define CXM_FW_CAPTURE_STREAM_YUV                   0x00000001
+#define CXM_FW_CAPTURE_STREAM_PCM_AUDIO             0x00000002
+#define CXM_FW_CAPTURE_STREAM_VBI                   0x00000004
+
+#ifdef CXM_PVR150
+#define CXM_FW_STREAM_TYPE_DVD                      0x0000000e
+#else
+#define CXM_FW_STREAM_TYPE_DVD                      0x0000000a
+#endif
+#define CXM_FW_STREAM_TYPE_MPEG1                    0x00000002
+#define CXM_FW_STREAM_TYPE_MPEG2_PROGRAM            0x00000000
+#define CXM_FW_STREAM_TYPE_SVCD                     0x0000000c
+#define CXM_FW_STREAM_TYPE_VCD                      0x0000000b
+
+#define CXM_MACROBLOCK_HEIGHT 16
+#define CXM_MACROBLOCK_WIDTH  16
+#define CXM_MACROBLOCK_SIZE   (CXM_MACROBLOCK_HEIGHT * CXM_MACROBLOCK_WIDTH)
+
+/*
+ * I2C addresses
+ */
+#define CXM_I2C_EEPROM   0xa0
+#define CXM_I2C_IR       0x30
+#define CXM_I2C_MSP3400  0x80
+#define CXM_I2C_SAA7115  0x42
+#define CXM_I2C_TUNER    0xc2
+#define CXM_I2C_TUNER_IF 0x86
+#define CXM_I2C_WM8775   0x36
+
+/* The maximum wait time of the MSP
+   is 1 ms during normal operations. */
+#define CXM_I2C_TIMEOUT  1000
+
+/*
+ * EEPROM
+ */
+int cxm_eeprom_init( struct cxm_softc *sc );
+int cxm_eeprom_tuner_type( struct cxm_softc *sc );
+#ifdef CXM_PVR150
+int cxm_eeprom_get_std( struct cxm_softc *sc );
+#endif
+
+/*
+ * Infrared remote
+ */
+int cxm_ir_init( struct cxm_softc *sc );
+int cxm_ir_key( struct cxm_softc *sc, char *buf, int len );
+
+/*
+ * msp34xxx Audio decoder
+ */
+#define CXM_MSP3400C_DEM 0x10
+#define CXM_MSP3400C_DFP 0x12
+
+struct cxm_msp_setting {
+	unsigned char	dev;
+	unsigned int	addr;
+	char		value[2];
+};
+
+struct cxm_msp_command {
+	unsigned int nsettings;
+	struct cxm_msp_setting settings[5];
+};
+
+int cxm_msp_init( struct cxm_softc *sc );
+int cxm_msp_mute( struct cxm_softc *sc );
+int cxm_msp_unmute( struct cxm_softc *sc );
+int cxm_msp_is_muted( struct cxm_softc *sc );
+int cxm_msp_select_source( struct cxm_softc *sc, enum cxm_source source );
+enum cxm_source cxm_msp_selected_source( struct cxm_softc *sc );
+int cxm_msp_autodetect_standard( struct cxm_softc *sc );
+int cxm_msp_is_locked( struct cxm_softc *sc );
+int cxm_msp_wait_for_lock( struct cxm_softc *sc );
+
+/*
+ * wm8775 Audio ADC
+ */
+struct cxm_wm8775_setting {
+	unsigned char	addr;
+	char		value[2];
+};
+
+struct cxm_wm8775_command {
+	unsigned int nsettings;
+	struct cxm_wm8775_setting settings[13];
+};
+
+int cxm_wm8775_init(struct cxm_softc *sc);
+
+/*
+ * Tuner
+ */
+#define CXM_TUNER_PHILIPS_FI1216_MK2   0
+#define CXM_TUNER_PHILIPS_FM1216       1
+#define CXM_TUNER_PHILIPS_FQ1216ME     2
+#define CXM_TUNER_PHILIPS_FQ1216ME_MK3 3
+#define CXM_TUNER_PHILIPS_FM1216ME_MK3 4
+#define CXM_TUNER_PHILIPS_FI1236_MK2   5
+#define CXM_TUNER_PHILIPS_FM1236       6
+#define CXM_TUNER_PHILIPS_FI1246_MK2   7
+#define CXM_TUNER_PHILIPS_FM1246       8
+#define CXM_TUNER_TEMIC_4006_FH5       9
+#define CXM_TUNER_TEMIC_4009_FR5      10
+#define CXM_TUNER_TEMIC_4036_FY5      11
+#define CXM_TUNER_TEMIC_4039_FR5      12
+#define CXM_TUNER_TEMIC_4066_FY5      13
+#define CXM_TUNER_LG_TPI8PSB11D       14
+#define CXM_TUNER_LG_TPI8PSB01N       15
+#define CXM_TUNER_LG_TAPC_H701F       16
+#define CXM_TUNER_LG_TAPC_H001F       17
+#define CXM_TUNER_LG_TAPE_H001F       18
+#define CXM_TUNER_MICROTUNE_4049_FM5  19
+#define CXM_TUNER_TCL_2002N_6A        20
+#ifdef CXM_PVR150
+#define CXM_TUNER_SAMSUNG             21
+#define CXM_TUNER_TYPES               22
+#else
+#define CXM_TUNER_TYPES               21
+#endif
+
+#define CXM_TUNER_AFC_MASK           0x07
+
+#define CXM_TUNER_AFC_FREQ_MINUS_125 0x00
+#define CXM_TUNER_AFC_FREQ_MINUS_62  0x01
+#define CXM_TUNER_AFC_FREQ_CENTERED  0x02
+#define CXM_TUNER_AFC_FREQ_PLUS_62   0x03
+#define CXM_TUNER_AFC_FREQ_PLUS_125  0x04
+
+#define CXM_TUNER_PHASE_LOCKED       0x40
+
+#define CXM_TUNER_FM_SYSTEM          0x01
+#define CXM_TUNER_TV_SYSTEM_BG       0x02
+#define CXM_TUNER_TV_SYSTEM_DK       0x04
+#define CXM_TUNER_TV_SYSTEM_I        0x08
+#define CXM_TUNER_TV_SYSTEM_MN       0x10
+#define CXM_TUNER_TV_SYSTEM_L        0x20
+#define CXM_TUNER_TV_SYSTEM_L_PRIME  0x40
+
+struct cxm_tuner_band_code {
+	unsigned long	freq;
+	unsigned char	codes[2];
+};
+
+struct cxm_tuner_channel_assignment {
+	unsigned int	channel;
+	unsigned long	freq;
+	unsigned long	step;
+};
+
+struct cxm_tuner_channels {
+	const char	*name;
+	unsigned int	chnlset;
+	unsigned int	system;
+	unsigned int	min_channel;
+	unsigned int	max_channel;
+	unsigned long	if_freq;
+	struct cxm_tuner_channel_assignment assignments[17];
+};
+
+struct cxm_tuner_system_code {
+	unsigned int	system;
+	unsigned char	codes[4];
+};
+
+enum cxm_tuner_system_code_style { cxm_unknown_system_code_style,
+				   cxm_none_system_code_style,
+				   cxm_port_system_code_style,
+				   cxm_if_system_code_style,
+				   cxm_if_system_with_aux_code_style };
+
+struct cxm_tuner_system {
+	unsigned int				supported;
+	enum cxm_tuner_system_code_style	code_style;
+	struct cxm_tuner_system_code		codes[6];
+};
+
+struct cxm_tuner {
+	const char	*name;
+	struct cxm_tuner_system	systems;
+	unsigned long	min_freq;
+	unsigned long	max_freq;
+	struct cxm_tuner_band_code band_codes[3];
+	unsigned long fm_min_freq;
+	unsigned long fm_max_freq;
+	struct cxm_tuner_band_code fm_band_code;
+	const struct cxm_tuner_channels *default_channels;
+};
+
+enum cxm_tuner_freq_type { cxm_tuner_unknown_freq_type, cxm_tuner_fm_freq_type,
+			   cxm_tuner_tv_freq_type };
+
+extern const struct cxm_tuner cxm_tuners[];
+
+int cxm_tuner_init( struct cxm_softc *sc );
+int cxm_tuner_select_channel_set( struct cxm_softc *sc,
+				  unsigned int channel_set );
+unsigned int cxm_tuner_selected_channel_set( struct cxm_softc *sc );
+int cxm_tuner_select_frequency( struct cxm_softc *sc,
+				enum cxm_tuner_freq_type type,
+				unsigned long freq );
+int cxm_tuner_select_channel( struct cxm_softc *sc, unsigned int channel );
+int cxm_tuner_apply_afc( struct cxm_softc *sc );
+int cxm_tuner_is_locked( struct cxm_softc *sc );
+int cxm_tuner_wait_for_lock( struct cxm_softc *sc );
+int cxm_tuner_status( struct cxm_softc *sc );
+
+/*
+ * Video decoder
+ */
+struct cxm_saa7115_setting {
+	unsigned char	addr;
+	unsigned int	nvalues;
+	char		values[32];
+};
+
+struct cxm_saa7115_command {
+	unsigned int nsettings;
+	struct cxm_saa7115_setting settings[20];
+};
+
+struct cxm_saa7115_audio_clock {
+	unsigned int sample_rate;
+	unsigned int fps;
+	const struct cxm_saa7115_command *clock;
+};
+
+struct cxm_saa7115_scaling {
+	unsigned int width;
+	unsigned int height;
+	unsigned int fps;
+	const struct cxm_saa7115_command *scaling;
+};
+
+int cxm_saa7115_init( struct cxm_softc *sc );
+int cxm_saa7115_mute( struct cxm_softc *sc );
+int cxm_saa7115_unmute( struct cxm_softc *sc );
+int cxm_saa7115_select_source( struct cxm_softc *sc, enum cxm_source source );
+int cxm_saa7115_configure( struct cxm_softc *sc,
+			   unsigned int width, unsigned int height,
+			   unsigned int fps, unsigned int audio_sample_rate);
+enum cxm_source_format cxm_saa7115_detected_format( struct cxm_softc *sc );
+int cxm_saa7115_detected_fps( struct cxm_softc *sc );
+int cxm_saa7115_get_brightness( struct cxm_softc *sc );
+int cxm_saa7115_set_brightness( struct cxm_softc *sc,
+				unsigned char brightness );
+int cxm_saa7115_get_chroma_saturation( struct cxm_softc *sc );
+int cxm_saa7115_set_chroma_saturation( struct cxm_softc *sc,
+				       unsigned char chroma_saturation);
+int cxm_saa7115_get_contrast( struct cxm_softc *sc );
+int cxm_saa7115_set_contrast( struct cxm_softc *sc, unsigned char contrast );
+int cxm_saa7115_get_hue( struct cxm_softc *sc );
+int cxm_saa7115_set_hue( struct cxm_softc *sc, unsigned char hue );
+int cxm_saa7115_is_locked( struct cxm_softc *sc );
+int cxm_saa7115_wait_for_lock( struct cxm_softc *sc );
+
+#endif	/* !_CXM_H */
diff --git a/sys/dev/video/cxm/cxm/Makefile b/sys/dev/video/cxm/cxm/Makefile
new file mode 100644
index 0000000..2613bc5
--- /dev/null
+++ b/sys/dev/video/cxm/cxm/Makefile
@@ -0,0 +1,13 @@
+.PATH:	${.CURDIR}/..
+.PATH:	${.CURDIR}/../../../../contrib/dev/cxm
+
+KMOD=	cxm
+SRCS=	cxm.c cxm.h cxm_cx2584x_fw.c cxm_dec_fw.c cxm_enc_fw.c cxm_eeprom.c \
+	cxm_ir.c cxm_msp34xxx.c cxm_saa7115.c cxm_tuner.c cxm_wm8775.c \
+	opt_cxm.h bus_if.h device_if.h iicbb_if.h pci_if.h \
+	fbsd-compat.c fbsd-compat.h \
+	cx25840-audio.c cx25840-core.c cx25840-firmware.c cx25840-vbi.c \
+	tda9887.c tda9887.h
+KMODDEPS= cxm_iic
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/video/cxm/cxm_cx2584x_fw.c b/sys/dev/video/cxm/cxm_cx2584x_fw.c
new file mode 100644
index 0000000..33f2dcc
--- /dev/null
+++ b/sys/dev/video/cxm/cxm_cx2584x_fw.c
@@ -0,0 +1,5 @@
+#include <sys/types.h>
+
+u_int8_t cxm_cx2584x_fw[] __attribute__ ((aligned(4))) = {
+	0x4e, 0x4f, 0x46, 0x57, 0x00 /* NOFW */
+};
diff --git a/sys/dev/video/cxm/cxm_dec_fw.c b/sys/dev/video/cxm/cxm_dec_fw.c
new file mode 100644
index 0000000..f04eca0
--- /dev/null
+++ b/sys/dev/video/cxm/cxm_dec_fw.c
@@ -0,0 +1,5 @@
+#include <sys/types.h>
+
+const u_int8_t cxm_dec_fw[] __attribute__ ((aligned(4))) = {
+	0x4e, 0x4f, 0x46, 0x57, 0x00 /* NOFW */
+};
diff --git a/sys/dev/video/cxm/cxm_eeprom.c b/sys/dev/video/cxm/cxm_eeprom.c
new file mode 100644
index 0000000..ae60b07
--- /dev/null
+++ b/sys/dev/video/cxm/cxm_eeprom.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2003, 2004, 2005
+ *	John Wehle <john@feith.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by John Wehle.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * EEPROM routines for the Conexant MPEG-2 Codec driver.
+ *
+ * Ideally these routines should be implemented as a separate
+ * driver which has a generic EEPROM interface so that it's
+ * not necessary for each multimedia driver to re-invent the
+ * wheel.
+ */
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <sys/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+
+#include <machine/clock.h>
+
+#include <dev/video/cxm/cxm.h>
+
+#include <bus/iicbus/iiconf.h>
+#include <bus/iicbus/iicbus.h>
+
+#include "iicbb_if.h"
+
+
+static int
+cxm_eeprom_read( device_t iicbus, int i2c_addr,
+		 char *buf, int len, unsigned int offset )
+{
+	char msg[1];
+	int received;
+	int sent;
+
+	msg[0] = (unsigned char)offset;
+
+	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
+	    || sent != sizeof(msg))
+		goto fail;
+
+	if (iicbus_repeated_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
+		goto fail;
+
+	if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+        return received;
+
+fail:
+	iicbus_stop(iicbus);
+	return -1;
+}
+
+
+int
+cxm_eeprom_init( struct cxm_softc *sc )
+{
+	unsigned char eeprom[1];
+
+	if (cxm_eeprom_read(sc->iicbus, CXM_I2C_EEPROM,
+			    eeprom, sizeof(eeprom), 0) != sizeof(eeprom))
+		return -1;
+
+	return 0;
+}
+
+
+#ifdef CXM_PVR150
+static unsigned int sup_std[4];
+
+int
+cxm_eeprom_get_std( struct cxm_softc *sc )
+{
+	int dev_num;
+
+	dev_num = device_get_unit(sc->iicbus);
+
+	return sup_std[dev_num] == 8; /* returns true if ntsc */
+}
+#endif
+
+
+int
+cxm_eeprom_tuner_type( struct cxm_softc *sc )
+{
+	unsigned char eeprom[256];
+	unsigned int i;
+	unsigned int len;
+	unsigned int subsystem_vendor_id;
+	unsigned int tuner_code;
+	int tuner_type;
+#ifdef CXM_PVR150
+	int dev_num;
+	int beenhere;
+
+	beenhere = 0;
+	dev_num = device_get_unit(sc->iicbus);
+#endif
+
+	if (cxm_eeprom_read(sc->iicbus, CXM_I2C_EEPROM,
+			    eeprom, sizeof(eeprom), 0) != sizeof(eeprom))
+		return -1;
+
+	subsystem_vendor_id = (unsigned int)eeprom[254] << 8 | eeprom[255];
+	tuner_type = -1;
+
+	switch (subsystem_vendor_id) {
+	case PCI_VENDOR_HAUPPAUGE:
+
+		/*
+		 * The Hauppauge eeprom format is tagged.
+		 */
+
+		if (eeprom[0] != 0x84) {
+			kprintf("%s: unknown Hauppauge eeprom format %#x\n",
+			       sc->name, (unsigned int)eeprom[0]);
+			break;
+		}
+
+		tuner_code = -1;
+
+		for (i = 0; i < sizeof(eeprom); i += len) {
+			len = 0;
+			if (eeprom[i] == 0x84) {
+				len = (unsigned int)eeprom[i + 2] << 8
+				      | eeprom[i + 1];
+				i += 3;
+			}
+			else if ((eeprom[i] & 0xf0) == 0x70) {
+				if (eeprom[i] & 0x08)
+					break;
+				len = eeprom[i] & 0x07;
+				i++;
+			}
+			else {
+				kprintf(
+				  "%s: unknown Hauppauge eeprom packet %#x\n",
+				       sc->name, (unsigned int)eeprom[i]);
+				return -1;
+			}
+
+			if (i >= sizeof(eeprom)
+			    || (i + len) > sizeof(eeprom)) {
+				kprintf("%s: corrupt Hauppauge eeprom packet\n",
+				       sc->name);
+				return -1;
+			}
+
+			switch (eeprom[i]) {
+			case 0x00:
+				tuner_code = eeprom[i + 6];
+#ifdef CXM_PVR150
+				sup_std[dev_num] = eeprom[i + 5];
+#endif
+				break;
+
+			case 0x0a:
+#ifdef CXM_PVR150
+				if (beenhere==0) {
+					tuner_code = eeprom[i + 2]; /* + 2 */
+					sup_std[dev_num] = eeprom[i + 1];
+					beenhere = 1;
+				} else {
+					kprintf("%s: second(radio) tuner %d\n",
+					    sc->name, (int)eeprom[i + 2]);
+				}
+#else
+				tuner_code = eeprom[i + 2];
+#endif
+				break;
+
+			default:
+				break;
+			}
+		}
+
+		switch (tuner_code) {
+		case 0x03: /* Philips FI1216 */
+		case 0x08: /* Philips FI1216 MK2 */
+			tuner_type = CXM_TUNER_PHILIPS_FI1216_MK2;
+			break;
+
+		case 0x22: /* Philips FQ1216ME */
+			tuner_type = CXM_TUNER_PHILIPS_FQ1216ME;
+			break;
+
+#ifdef CXM_PVR150
+		case 0x69:
+		case 0x5b:
+#endif
+		case 0x37: /* Philips FQ1216ME MK3 */
+			tuner_type = CXM_TUNER_PHILIPS_FQ1216ME_MK3;
+			break;
+
+		case 0x1d: /* Temic 4006FH5 */
+			tuner_type = CXM_TUNER_TEMIC_4006_FH5;
+			break;
+
+		case 0x30: /* LG Innotek TPI8PSB11D */
+			tuner_type = CXM_TUNER_LG_TPI8PSB11D;
+			break;
+
+		case 0x34: /* Microtune 4049FM5 */
+			tuner_type = CXM_TUNER_MICROTUNE_4049_FM5;
+			break;
+
+		case 0x05: /* Philips FI1236 */
+		case 0x0a: /* Philips FI1236 MK2 */
+			tuner_type = CXM_TUNER_PHILIPS_FI1236_MK2;
+			break;
+
+		case 0x1a: /* Temic 4036FY5 */
+			tuner_type = CXM_TUNER_TEMIC_4036_FY5;
+			break;
+
+		case 0x52: /* LG Innotek TAPC-H701F */
+			tuner_type = CXM_TUNER_LG_TAPC_H701F;
+			break;
+
+#ifdef CXM_PVR150
+		case 0x70: /* TCL M2523-5N-E */
+#endif
+		case 0x55: /* TCL 2002N-6A */
+			tuner_type = CXM_TUNER_TCL_2002N_6A;
+			break;
+
+		case 0x06: /* Philips FI1246 */
+		case 0x0b: /* Philips FI1246 MK2 */
+			tuner_type = CXM_TUNER_PHILIPS_FI1246_MK2;
+			break;
+
+		case 0x23: /* Temic 4066FY5 */
+			tuner_type = CXM_TUNER_TEMIC_4066_FY5;
+			break;
+
+		case 0x10: /* Philips FR1216 MK2 */
+		case 0x15: /* Philips FM1216 */
+			tuner_type = CXM_TUNER_PHILIPS_FM1216;
+			break;
+
+		case 0x39: /* Philips FM1216ME MK3 */
+			tuner_type = CXM_TUNER_PHILIPS_FM1216ME_MK3;
+			break;
+
+		case 0x2a: /* Temic 4009FR5 */
+			tuner_type = CXM_TUNER_TEMIC_4009_FR5;
+			break;
+
+		case 0x2f: /* LG Innotek TPI8PSB01N */
+			tuner_type = CXM_TUNER_LG_TPI8PSB01N;
+			break;
+
+		case 0x12: /* Philips FR1236 MK2 */
+		case 0x17: /* Philips FM1236 */
+			tuner_type = CXM_TUNER_PHILIPS_FM1236;
+			break;
+
+		case 0x21: /* Temic 4039FR5 */
+			tuner_type = CXM_TUNER_TEMIC_4039_FR5;
+			break;
+
+		case 0x44: /* LG Innotek TAPE-H001F */
+			tuner_type = CXM_TUNER_LG_TAPE_H001F;
+			break;
+
+		case 0x13: /* Philips FR1246 MK2 */
+		case 0x18: /* Philips FM1246 */
+			tuner_type = CXM_TUNER_PHILIPS_FM1246;
+			break;
+
+#ifdef CXM_PVR150
+		case 0x57:
+			tuner_type = CXM_TUNER_SAMSUNG;
+			break;
+#endif
+
+		default:
+			kprintf("%s: unknown tuner code %#x\n",
+			       sc->name, tuner_code);
+			break;
+		}
+		break;
+
+	default:
+		kprintf("%s: unknown subsystem vendor id %#x\n",
+		       sc->name, subsystem_vendor_id);
+		break;
+	}
+
+	return tuner_type;
+}
diff --git a/sys/dev/video/cxm/cxm_enc_fw.c b/sys/dev/video/cxm/cxm_enc_fw.c
new file mode 100644
index 0000000..6f9e050
--- /dev/null
+++ b/sys/dev/video/cxm/cxm_enc_fw.c
@@ -0,0 +1,5 @@
+#include <sys/types.h>
+
+const u_int8_t cxm_enc_fw[] __attribute__ ((aligned(4))) = {
+	0x4e, 0x4f, 0x46, 0x57, 0x00 /* NOFW */
+};
diff --git a/sys/dev/video/cxm/cxm_i2c.c b/sys/dev/video/cxm/cxm_i2c.c
new file mode 100644
index 0000000..85176a8
--- /dev/null
+++ b/sys/dev/video/cxm/cxm_i2c.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 2003, 2004, 2005
+ *	John Wehle <john@feith.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by John Wehle.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * I2c routines for the Conexant MPEG-2 Codec driver.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <sys/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <machine/clock.h>
+
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+
+#include <dev/video/cxm/cxm.h>
+
+#include <bus/iicbus/iiconf.h>
+
+#include "iicbb_if.h"
+
+
+static int	cxm_iic_probe( device_t dev );
+static int	cxm_iic_attach( device_t dev );
+static int	cxm_iic_detach( device_t dev );
+static void	cxm_iic_child_detached( device_t dev, device_t child );
+
+static int	cxm_iic_callback( device_t, int, caddr_t * );
+static int	cxm_iic_reset( device_t, u_char, u_char, u_char * );
+static int	cxm_iic_getscl( device_t );
+static int	cxm_iic_getsda( device_t );
+static void	cxm_iic_setscl( device_t, int );
+static void	cxm_iic_setsda( device_t, int );
+static void	cxm_iic_setlines( device_t, int, int );
+
+static device_method_t cxm_iic_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,         cxm_iic_probe),
+	DEVMETHOD(device_attach,        cxm_iic_attach),
+	DEVMETHOD(device_detach,        cxm_iic_detach),
+
+	/* bus interface */
+	DEVMETHOD(bus_child_detached,   cxm_iic_child_detached),
+	DEVMETHOD(bus_print_child,      bus_generic_print_child),
+	DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
+
+	/* iicbb interface */
+	DEVMETHOD(iicbb_callback,       cxm_iic_callback),
+	DEVMETHOD(iicbb_reset,          cxm_iic_reset),
+	DEVMETHOD(iicbb_getdataline,    cxm_iic_getsda),
+	DEVMETHOD(iicbb_setlines,       cxm_iic_setlines),
+
+	{ 0, 0 }
+};
+
+static driver_t cxm_iic_driver = {
+	"cxm_iic",
+	cxm_iic_methods,
+	sizeof(struct cxm_iic_softc),
+};
+
+static devclass_t cxm_iic_devclass;
+
+MODULE_VERSION(cxm_iic, 1);
+DRIVER_MODULE(cxm_iic, cxm, cxm_iic_driver, cxm_iic_devclass, 0, 0);
+
+
+/*
+ * the boot time probe routine.
+ *
+ * The cxm_iic device is only probed after it has
+ * been established that the cxm device is present
+ * which means that the cxm_iic device * must *
+ * be present since it's built into the cxm hardware.
+ */
+static int
+cxm_iic_probe( device_t dev )
+{
+	device_set_desc(dev, "Conexant iTVC15 / iTVC16 I2C controller");
+
+	return 0;
+}
+
+
+/*
+ * the attach routine.
+ */
+static int
+cxm_iic_attach( device_t dev )
+{
+	device_t *kids;
+	device_t iicbus;
+	int error;
+	int numkids;
+	int i;
+	int unit;
+	bus_space_handle_t *bhandlep;
+	bus_space_tag_t *btagp;
+	struct cxm_iic_softc *sc;
+	device_t child;
+
+	/* Get the device data */
+	sc = device_get_softc(dev);
+	unit = device_get_unit(dev);
+
+	/* build the device name */
+	ksnprintf(sc->name, sizeof(sc->name), "cxm_iic%d",unit);
+
+	/* retrieve the cxm btag and bhandle */
+	if (BUS_READ_IVAR(device_get_parent(dev), dev,
+			  CXM_IVAR_BTAG, (uintptr_t *)&btagp)
+	    || BUS_READ_IVAR(device_get_parent(dev), dev,
+			     CXM_IVAR_BHANDLE, (uintptr_t *)&bhandlep)) {
+		device_printf(dev,
+			      "could not retrieve bus space information\n");
+		return ENXIO;
+	}
+
+	sc->btag = *btagp;
+	sc->bhandle = *bhandlep;
+
+	/* add bit-banging generic code onto cxm_iic interface */
+	sc->iicbb = device_add_child(dev, "iicbb", -1);
+
+	if (! sc->iicbb) {
+		device_printf(dev, "could not add iicbb\n");
+		return ENXIO;
+	}
+
+	/* probed and attached the bit-banging code */
+	error = device_probe_and_attach(sc->iicbb);
+
+	if (error) {
+		device_printf(dev, "could not attach iicbb\n");
+		goto fail;
+	}
+
+	/* locate iicbus which was attached by the bit-banging code */
+	iicbus = NULL;
+	device_get_children(sc->iicbb, &kids, &numkids);
+	for (i = 0; i < numkids; i++)
+		if (strcmp(device_get_name(kids[i]), "iicbus") == 0) {
+			iicbus = kids[i];
+			break;
+		}
+	kfree(kids, M_TEMP);
+
+	if (! iicbus) {
+		device_printf(dev, "could not find iicbus\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	if (BUS_WRITE_IVAR(device_get_parent(dev), dev,
+			   CXM_IVAR_IICBUS, (uintptr_t)&iicbus)) {
+		device_printf(dev, "could not store iicbus information\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	/*
+	 * Detach the children before recursively deleting
+	 * in case a child has a pointer to a grandchild
+	 * which is used by the child's detach routine.
+	 *
+	 * Remember the child before detaching so we can
+	 * delete it (bus_generic_detach indirectly zeroes
+	 * sc->child_dev).
+	 */
+	child = sc->iicbb;
+	bus_generic_detach(dev);
+	if (child)
+		device_delete_child(dev, child);
+
+	return error;
+}
+
+
+/*
+ * the detach routine.
+ */
+static int
+cxm_iic_detach( device_t dev )
+{
+	struct cxm_iic_softc *sc;
+	device_t child;
+
+	/* Get the device data */
+	sc = device_get_softc(dev);
+
+	(void)BUS_WRITE_IVAR(device_get_parent(dev), dev, CXM_IVAR_IICBUS, 0);
+
+	/*
+	 * Detach the children before recursively deleting
+	 * in case a child has a pointer to a grandchild
+	 * which is used by the child's detach routine.
+	 *
+	 * Remember the child before detaching so we can
+	 * delete it (bus_generic_detach indirectly zeroes
+	 * sc->child_dev).
+	 */
+	child = sc->iicbb;
+	bus_generic_detach(dev);
+	if (child)
+		device_delete_child(dev, child);
+
+	return 0;
+}
+
+
+/*
+ * the child detached routine.
+ */
+static void
+cxm_iic_child_detached( device_t dev, device_t child )
+{
+	struct cxm_iic_softc *sc;
+
+	/* Get the device data */
+	sc = device_get_softc(dev);
+
+	if (child == sc->iicbb)
+		sc->iicbb = NULL;
+}
+
+
+static int
+cxm_iic_callback( device_t dev, int index, caddr_t *data )
+{
+	return 0;
+}
+
+
+static int
+cxm_iic_reset( device_t dev, u_char speed, u_char addr, u_char * oldaddr )
+{
+	struct cxm_iic_softc *sc;
+
+	/* Get the device data */
+	sc = (struct cxm_iic_softc *)device_get_softc(dev);
+
+	/* Set scl to 1 */
+	CSR_WRITE_4(sc, CXM_REG_I2C_SETSCL, ~(int)1);
+
+	/* Set sda to 1 */
+	CSR_WRITE_4(sc, CXM_REG_I2C_SETSDA, ~(int)1);
+
+	/*
+	 * PCI writes may be buffered so force the
+	 * write to complete by reading the last
+	 * location written.
+	 */
+
+	(void)CSR_READ_4(sc, CXM_REG_I2C_SETSDA);
+
+	/* Wait for 10 usec */
+	DELAY(10);
+
+	return IIC_ENOADDR;
+}
+
+
+static int
+cxm_iic_getscl( device_t dev )
+{
+	struct cxm_iic_softc *sc;
+
+	/* Get the device data */
+	sc = (struct cxm_iic_softc *)device_get_softc(dev);
+
+	/* Get sda */
+	return CSR_READ_1(sc, CXM_REG_I2C_GETSCL);
+}
+
+
+static int
+cxm_iic_getsda( device_t dev )
+{
+	struct cxm_iic_softc *sc;
+
+	/* Get the device data */
+	sc = (struct cxm_iic_softc *)device_get_softc(dev);
+
+	/* Get sda */
+	return CSR_READ_1(sc, CXM_REG_I2C_GETSDA);
+}
+
+
+static void
+cxm_iic_setscl( device_t dev, int val )
+{
+	struct cxm_iic_softc *sc;
+
+	/* Get the device data */
+	sc = (struct cxm_iic_softc *)device_get_softc(dev);
+
+	/* Set scl to the requested value */
+	CSR_WRITE_4(sc, CXM_REG_I2C_SETSCL, ~(int)(val ? 1 : 0));
+
+	/*
+	 * PCI writes may be buffered so force the
+	 * write to complete by reading the last
+	 * location written.
+	 */
+
+	(void)CSR_READ_4(sc, CXM_REG_I2C_SETSCL);
+}
+
+
+static void
+cxm_iic_setsda( device_t dev, int val )
+{
+	struct cxm_iic_softc *sc;
+
+	/* Get the device data */
+	sc = (struct cxm_iic_softc *)device_get_softc(dev);
+
+	/* Set sda to the requested value */
+	CSR_WRITE_4(sc, CXM_REG_I2C_SETSDA, ~(int)(val ? 1 : 0));
+
+	/*
+	 * PCI writes may be buffered so force the
+	 * write to complete by reading the last
+	 * location written.
+	 */
+
+	(void)CSR_READ_4(sc, CXM_REG_I2C_SETSDA);
+}
+
+
+static void
+cxm_iic_setlines( device_t dev, int ctrl, int data )
+{
+
+	cxm_iic_setscl(dev, ctrl);
+	cxm_iic_setsda(dev, data);
+
+#ifdef CXM_PVR150
+	/* Wait for 5 usec */
+	DELAY(5);
+#else
+	/* Wait for 10 usec */
+	DELAY(10);
+#endif
+}
diff --git a/sys/dev/video/cxm/cxm_iic/Makefile b/sys/dev/video/cxm/cxm_iic/Makefile
new file mode 100644
index 0000000..6dca4f8
--- /dev/null
+++ b/sys/dev/video/cxm/cxm_iic/Makefile
@@ -0,0 +1,6 @@
+.PATH:	${.CURDIR}/..
+KMOD	= cxm_iic
+SRCS	= cxm_i2c.c cxm.h \
+	  opt_cxm.h bus_if.h device_if.h iicbb_if.h pci_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/video/cxm/cxm_ir.c b/sys/dev/video/cxm/cxm_ir.c
new file mode 100644
index 0000000..fe96c9b
--- /dev/null
+++ b/sys/dev/video/cxm/cxm_ir.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2003, 2004
+ *	John Wehle <john@feith.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by John Wehle.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Infrared remote routines for the Conexant MPEG-2 Codec driver.
+ *
+ * Ideally these routines should be implemented as a separate
+ * driver which has a generic infrared remote interface so that
+ * it's not necessary for each multimedia driver to re-invent
+ * the wheel.
+ */
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <sys/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <machine/clock.h>
+
+#include <dev/video/cxm/cxm.h>
+
+#include <bus/iicbus/iiconf.h>
+#include <bus/iicbus/iicbus.h>
+
+#include "iicbb_if.h"
+
+
+static int
+cxm_ir_read( device_t iicbus, int i2c_addr, char *buf, int len )
+{
+	int received;
+
+	if (iicbus_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+        return received;
+
+fail:
+	iicbus_stop(iicbus);
+	return -1;
+}
+
+
+int
+cxm_ir_init( struct cxm_softc *sc )
+{
+	unsigned char key[1];
+
+	if (cxm_ir_read(sc->iicbus, CXM_I2C_IR,
+			    key, sizeof(key)) != sizeof(key))
+		return -1;
+
+	kprintf("%s: IR Remote\n", sc->name);
+
+	return 0;
+}
+
+
+int
+cxm_ir_key( struct cxm_softc *sc, char *buf, int len )
+{
+	int result;
+
+	result = cxm_ir_read(sc->iicbus, CXM_I2C_IR, buf, len);
+
+	if (result >= 0)
+		return result;
+
+	/*
+	 * If the IR receiver didn't respond,
+	 * then wait 50 ms and try again.
+	 */
+
+	tsleep(&sc->iicbus, 0, "IR", hz / 20 );
+
+	return cxm_ir_read(sc->iicbus, CXM_I2C_IR, buf, len);
+}
diff --git a/sys/dev/video/cxm/cxm_msp34xxx.c b/sys/dev/video/cxm/cxm_msp34xxx.c
new file mode 100644
index 0000000..687ec16
--- /dev/null
+++ b/sys/dev/video/cxm/cxm_msp34xxx.c
@@ -0,0 +1,627 @@
+/*
+ * Copyright (c) 2003, 2004, 2005
+ *	John Wehle <john@feith.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by John Wehle.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Audio decoder routines for the Conexant MPEG-2 Codec driver.
+ *
+ * Ideally these routines should be implemented as a separate
+ * driver which has a generic audio decoder interface so that
+ * it's not necessary for each multimedia driver to re-invent
+ * the wheel.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <sys/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <machine/clock.h>
+
+#include <dev/video/cxm/cxm.h>
+
+#include <bus/iicbus/iiconf.h>
+#include <bus/iicbus/iicbus.h>
+
+#include "iicbb_if.h"
+
+
+static const struct cxm_msp_command
+msp34x5G_init = {
+	5,
+	{
+		/* Enable Automatic Sound Select */
+		{ CXM_MSP3400C_DEM, 0x0030, { 0x20, 0x03 } },
+		/* SCART Prescale = 0 dB */
+		{ CXM_MSP3400C_DFP, 0x000d, { 0x19, 0x00 } },
+		/* FM / AM Prescale = 100 Khz and FM Matrix = Sound A Mono  */
+		{ CXM_MSP3400C_DFP, 0x000e, { 0x24, 0x03 } },
+		/* NICAM Prescale = 9 dB  */
+		{ CXM_MSP3400C_DFP, 0x0010, { 0x5a, 0x00 } },
+		/* Enable Automatic Standard Select */
+		{ CXM_MSP3400C_DEM, 0x0020, { 0x00, 0x01 } },
+	}
+};
+
+static const struct cxm_msp_command
+msp34x5G_select_tuner = {
+	3,
+	{
+		/* Loudspeaker Source = demodulator (St or A), Matrix = St */
+		{ CXM_MSP3400C_DFP, 0x0008, { 0x03, 0x20 } },
+		/* SCART1_L/R Source = demodulator (St or A), Matrix = St */
+		{ CXM_MSP3400C_DFP, 0x000a, { 0x03, 0x20 } },
+		/* DSP In = mute, SC1_OUT_L/R Source = SCART1_L/R */
+		{ CXM_MSP3400C_DFP, 0x0013, { 0x0f, 0x20 } }
+	}
+};
+
+static const struct cxm_msp_command
+msp34x5D_init = {
+	4,
+	{
+		/* Enable Automatic NICAM-FM/AM Switching */
+		{ CXM_MSP3400C_DEM, 0x0021, { 0x00, 0x01 } },
+		/* SCART Prescale = 0 dB */
+		{ CXM_MSP3400C_DFP, 0x000d, { 0x19, 0x00 } },
+		/* NICAM Prescale = 9 dB  */
+		{ CXM_MSP3400C_DFP, 0x0010, { 0x5a, 0x00 } },
+		/* Enable Automatic Standard Select */
+		{ CXM_MSP3400C_DEM, 0x0020, { 0x00, 0x01 } },
+	}
+};
+
+static const struct cxm_msp_command
+msp34x5D_select_tuner = {
+	5,
+	{
+		/* Loudspeaker Source = demodulator (NICAM), Matrix = St */
+		{ CXM_MSP3400C_DFP, 0x0008, { 0x01, 0x20 } },
+		/* SCART1_L/R Source = demodulator (NICAM), Matrix = St */
+		{ CXM_MSP3400C_DFP, 0x000a, { 0x01, 0x20 } },
+		/* FM / AM Prescale = 100 Khz and FM Matrix = No Matrix  */
+		{ CXM_MSP3400C_DFP, 0x000e, { 0x24, 0x00 } },
+		/* FM Deemphasis = 50 us */
+		{ CXM_MSP3400C_DFP, 0x000f, { 0x00, 0x00 } },
+		/* DSP In = mute, SC1_OUT_L/R Source = SCART1_L/R */
+		{ CXM_MSP3400C_DFP, 0x0013, { 0x0f, 0x20 } }
+	}
+};
+
+static const struct cxm_msp_command
+msp34xxx_mute = {
+	2,
+	{
+		/* Loudspeaker volume = mute */
+		{ CXM_MSP3400C_DFP, 0x0000, { 0x00, 0x00 } },
+		/* SC1_OUT_L/R volume = mute */
+		{ CXM_MSP3400C_DFP, 0x0007, { 0x00, 0x01 } }
+	}
+};
+
+static const struct cxm_msp_command
+msp34xxx_unmute = {
+	2,
+	{
+		/* Loudspeaker volume = 0 db */
+		{ CXM_MSP3400C_DFP, 0x0000, { 0x73, 0x00 } },
+		/* SC1_OUT_L/R volume = 0 db */
+		{ CXM_MSP3400C_DFP, 0x0007, { 0x73, 0x01 } }
+	}
+};
+
+static const struct cxm_msp_command
+msp34xxx_select_fm = {
+	3,
+	{
+		/* Loudspeaker Source = SCART, Matrix = STEREO */
+		{ CXM_MSP3400C_DFP, 0x0008, { 0x02, 0x20 } },
+		/* SCART1_L/R Source = SCART, Matrix = STEREO */
+		{ CXM_MSP3400C_DFP, 0x000a, { 0x02, 0x20 } },
+		/* DSP In = SC2_IN_L/R, SC1_OUT_L/R Source = SCART1_L/R */
+		{ CXM_MSP3400C_DFP, 0x0013, { 0x0e, 0x00 } }
+	}
+};
+
+static const struct cxm_msp_command
+msp34xxx_select_line_in = {
+	3,
+	{
+		/* Loudspeaker Source = SCART, Matrix = STEREO */
+		{ CXM_MSP3400C_DFP, 0x0008, { 0x02, 0x20 } },
+		/* SCART1_L/R Source = SCART, Matrix = STEREO */
+		{ CXM_MSP3400C_DFP, 0x000a, { 0x02, 0x20 } },
+		/* DSP In = SC1_IN_L/R, SC1_OUT_L/R Source = SCART1_L/R */
+		{ CXM_MSP3400C_DFP, 0x0013, { 0x0c, 0x00 } }
+	}
+};
+
+
+/* Reset the MSP or DPL chip */
+static int
+cxm_msp_dpl_reset( device_t iicbus, int i2c_addr )
+{
+	unsigned char msg[3];
+	int sent;
+
+	/* put into reset mode */
+	msg[0] = 0x00;
+	msg[1] = 0x80;
+	msg[2] = 0x00;
+
+	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
+	    || sent != sizeof(msg))
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+	/* put back to operational mode */
+	msg[0] = 0x00;
+	msg[1] = 0x00;
+	msg[2] = 0x00;
+
+	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
+	    || sent != sizeof(msg))
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+	return 0;
+
+fail:
+	iicbus_stop(iicbus);
+	return -1;
+}
+
+
+/* Read from the MSP or DPL registers */
+static int
+cxm_msp_dpl_read( device_t iicbus, int i2c_addr,
+		  unsigned char dev, unsigned int addr,
+		  char *buf, int len )
+{
+	unsigned char msg[3];
+	int received;
+	int sent;
+
+	msg[0] = (unsigned char)(dev + 1);
+	msg[1] = (unsigned char)(addr >> 8);
+	msg[2] = (unsigned char)addr;
+
+	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
+	    || sent != sizeof(msg))
+		goto fail;
+
+	if (iicbus_repeated_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
+		goto fail;
+
+	if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+	return received;
+
+fail:
+	iicbus_stop(iicbus);
+	return -1;
+}
+
+
+/* Write to the MSP or DPL registers */
+static int
+cxm_msp_dpl_write( device_t iicbus, int i2c_addr,
+		   unsigned char dev, unsigned int addr,
+		   const char *buf, int len )
+{
+	unsigned char msg[3];
+	int sent;
+
+	msg[0] = (unsigned char)dev;
+	msg[1] = (unsigned char)(addr >> 8);
+	msg[2] = (unsigned char)addr;
+
+	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
+	    || sent != sizeof(msg))
+		goto fail;
+
+	if (iicbus_write(iicbus, buf, len, &sent, CXM_I2C_TIMEOUT) != 0)
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+	return sent;
+
+fail:
+	iicbus_stop(iicbus);
+	return -1;
+}
+
+
+int
+cxm_msp_init( struct cxm_softc *sc )
+{
+	unsigned char rev1[2];
+	unsigned char rev2[2];
+	unsigned int i;
+	unsigned int nsettings;
+	const struct cxm_msp_setting *settings;
+
+	if (cxm_msp_dpl_reset (sc->iicbus, CXM_I2C_MSP3400) < 0)
+		return -1;
+
+	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
+			     0x001e, rev1, sizeof(rev1)) != sizeof(rev1))
+		return -1;
+
+	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
+			     0x001f, rev2, sizeof(rev2)) != sizeof(rev2))
+		return -1;
+
+	ksnprintf(sc->msp_name, sizeof(sc->msp_name), "%c4%02d%c-%c%d",
+		 ((rev1[1] >> 4) & 0x0f) + '3', rev2[0],
+		 (rev1[1] & 0x0f) + '@', rev1[0] + '@', rev2[1] & 0x1f);
+
+	/*
+	 * MSP 34x5D, 34x5G, and MSP 44x8G are the
+	 * only audio decoders currently supported.
+	 */
+
+	if (strncmp(&sc->msp_name[0], "34", 2) == 0
+	    && strncmp(&sc->msp_name[3], "5D", 2) == 0)
+	  ;
+	else if (strncmp(&sc->msp_name[0], "34", 2) == 0
+		 && strncmp(&sc->msp_name[3], "5G", 2) == 0)
+	  ;
+	else if (strncmp(&sc->msp_name[0], "44", 2) == 0
+		 && strncmp(&sc->msp_name[3], "8G", 2) == 0)
+	  ;
+	else {
+		kprintf("%s: unknown audio decoder MSP%s\n",
+		       sc->name, sc->msp_name);
+		return -1;
+	}
+
+	nsettings = msp34x5G_init.nsettings;
+	settings = msp34x5G_init.settings;
+	if (sc->msp_name[4] == 'D') {
+		nsettings = msp34x5D_init.nsettings;
+		settings = msp34x5D_init.settings;
+	  }
+
+	for (i = 0; i < nsettings; i++)
+		if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
+				      settings[i].dev, settings[i].addr,
+				      settings[i].value,
+				      sizeof(settings[i].value))
+		    != sizeof(settings[i].value))
+			return -1;
+
+	if (cxm_msp_select_source(sc, cxm_tuner_source) < 0)
+		return -1;
+
+	kprintf("%s: MSP%s audio decoder\n", sc->name, sc->msp_name);
+
+	return 0;
+}
+
+
+int
+cxm_msp_mute( struct cxm_softc *sc )
+{
+	unsigned int i;
+	unsigned int nsettings;
+	const struct cxm_msp_setting *settings;
+
+	nsettings = msp34xxx_mute.nsettings;
+	settings = msp34xxx_mute.settings;
+
+	for (i = 0; i < nsettings; i++)
+		if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
+				      settings[i].dev, settings[i].addr,
+				      settings[i].value,
+				      sizeof(settings[i].value))
+		    != sizeof(settings[i].value))
+			return -1;
+
+	return 0;
+}
+
+
+int
+cxm_msp_unmute( struct cxm_softc *sc )
+{
+	unsigned int i;
+	unsigned int nsettings;
+	const struct cxm_msp_setting *settings;
+
+	nsettings = msp34xxx_unmute.nsettings;
+	settings = msp34xxx_unmute.settings;
+
+	for (i = 0; i < nsettings; i++)
+		if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
+				      settings[i].dev, settings[i].addr,
+				      settings[i].value,
+				      sizeof(settings[i].value))
+		    != sizeof(settings[i].value))
+			return -1;
+
+	return 0;
+}
+
+
+int
+cxm_msp_is_muted( struct cxm_softc *sc )
+{
+	unsigned char volume[2];
+
+	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
+			     0x0000, volume, sizeof(volume)) != sizeof(volume))
+		return -1;
+
+	return volume[0] == 0x00 || volume[0] == 0xff ? 1 : 0;
+}
+
+
+int
+cxm_msp_select_source( struct cxm_softc *sc, enum cxm_source source )
+{
+	unsigned int i;
+	unsigned int nsettings;
+	const struct cxm_msp_setting *settings;
+
+	switch (source) {
+	case cxm_fm_source:
+		nsettings = msp34xxx_select_fm.nsettings;
+		settings = msp34xxx_select_fm.settings;
+		break;
+
+	case cxm_line_in_source_composite:
+	case cxm_line_in_source_svideo:
+		nsettings = msp34xxx_select_line_in.nsettings;
+		settings = msp34xxx_select_line_in.settings;
+		break;
+
+	case cxm_tuner_source:
+		nsettings = msp34x5G_select_tuner.nsettings;
+		settings = msp34x5G_select_tuner.settings;
+		if (sc->msp_name[4] == 'D') {
+			nsettings = msp34x5D_select_tuner.nsettings;
+			settings = msp34x5D_select_tuner.settings;
+		  }
+		break;
+
+	default:
+		return -1;
+	}
+
+	for (i = 0; i < nsettings; i++)
+		if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
+				      settings[i].dev, settings[i].addr,
+				      settings[i].value,
+				      sizeof(settings[i].value))
+		    != sizeof(settings[i].value))
+			return -1;
+
+	return 0;
+}
+
+
+enum cxm_source
+cxm_msp_selected_source( struct cxm_softc *sc )
+{
+	unsigned char dsp[2];
+	unsigned char source[2];
+
+	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
+			     0x0008, source, sizeof(source)) != sizeof(source))
+		return cxm_unknown_source;
+
+	switch (source[0]) {
+	case 0: /* FM / AM mono signal */
+	case 1: /* Stereo or A / B */
+	case 3: /* Stereo or A */
+	case 4: /* Stereo or B */
+		return cxm_tuner_source;
+
+	case 2: /* SCART */
+		break;
+
+	default:
+		return cxm_unknown_source;
+	}
+
+	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
+			     0x0013, dsp, sizeof(dsp)) != sizeof(dsp))
+		return cxm_unknown_source;
+
+	if (dsp[1] & 0x20)
+		return cxm_unknown_source;
+
+	switch (dsp[0] & 0x03) {
+	case 0:
+		return cxm_line_in_source_composite;
+
+	case 2:
+		return cxm_fm_source;
+
+	default:
+		 return cxm_unknown_source;
+	}
+}
+
+
+int
+cxm_msp_autodetect_standard( struct cxm_softc *sc )
+{
+	unsigned int i;
+	int locked;
+	unsigned int nsettings;
+	const struct cxm_msp_setting *settings;
+
+	switch (cxm_msp_selected_source(sc)) {
+	case cxm_tuner_source:
+		break;
+
+	case cxm_fm_source:
+	case cxm_line_in_source_composite:
+	case cxm_line_in_source_svideo:
+		return 1;
+
+	default:
+		return -1;
+	}
+
+	/*
+	 * Section 3.3.2.2 of the data sheet states:
+	 *
+	 *   A general refresh of the STANDARD SELECT
+	 *   register is not allowed.
+	 */
+
+	if (cxm_msp_dpl_reset (sc->iicbus, CXM_I2C_MSP3400) < 0)
+		return -1;
+
+	nsettings = msp34x5G_init.nsettings;
+	settings = msp34x5G_init.settings;
+	if (sc->msp_name[4] == 'D') {
+		nsettings = msp34x5D_init.nsettings;
+		settings = msp34x5D_init.settings;
+	  }
+
+	for (i = 0; i < nsettings; i++)
+		if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
+				      settings[i].dev, settings[i].addr,
+				      settings[i].value,
+				      sizeof(settings[i].value))
+		    != sizeof(settings[i].value))
+			return -1;
+
+	locked = cxm_msp_wait_for_lock(sc);
+
+	if (cxm_msp_select_source(sc, cxm_tuner_source) < 0)
+		return -1;
+
+	return locked;
+}
+
+
+int
+cxm_msp_is_locked( struct cxm_softc *sc )
+{
+	unsigned char source[2];
+	unsigned char standard[2];
+
+	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
+			     0x0008, source, sizeof(source)) != sizeof(source))
+		return -1;
+
+	switch (source[0]) {
+	case 0: /* FM / AM mono signal */
+	case 1: /* Stereo or A / B */
+	case 3: /* Stereo or A */
+	case 4: /* Stereo or B */
+		break;
+
+	default:
+		return 1;
+	}
+
+	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DEM,
+			     0x007e, standard, sizeof(standard))
+	    != sizeof(standard))
+		return -1;
+
+	if (standard[0] >= 8 || (standard[0] == 0 && standard[1] == 0))
+		return 0;
+
+	return 1;
+}
+
+
+int
+cxm_msp_wait_for_lock( struct cxm_softc *sc )
+{
+	unsigned int i;
+
+	/*
+	 * Section 3.3.2.1 of the data sheet states:
+	 *
+	 *   Within 0.5 s the detection and setup of the actual
+	 *   TV sound standard is performed.  The detected result
+	 *   can be read out of the STANDARD RESULT register by
+	 *   the control processor.
+	 */
+
+	for (i = 0; i < 10; i++) {
+
+		/*
+		 * The input may have just changed (prior to
+		 * cxm_msp_wait_for_lock) so start with the
+		 * delay to give the audio decoder a chance
+		 * to update its status.
+		 */
+
+		tsleep(&sc->iicbus, 0, "audio", hz / 20 );
+
+		switch (cxm_msp_is_locked(sc)) {
+		case 1:
+			return 1;
+
+		case 0:
+			break;
+
+		default:
+			return -1;
+		}
+	}
+
+	kprintf("%s: audio decoder failed to lock\n", sc->name);
+
+	return 0;
+}
diff --git a/sys/dev/video/cxm/cxm_saa7115.c b/sys/dev/video/cxm/cxm_saa7115.c
new file mode 100644
index 0000000..51033e9
--- /dev/null
+++ b/sys/dev/video/cxm/cxm_saa7115.c
@@ -0,0 +1,1196 @@
+/*
+ * Copyright (c) 2003, 2004, 2005
+ *	John Wehle <john@feith.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by John Wehle.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Video decoder routines for the Conexant MPEG-2 Codec driver.
+ *
+ * Ideally these routines should be implemented as a separate
+ * driver which has a generic video decoder interface so that
+ * it's not necessary for each multimedia driver to re-invent
+ * the wheel.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <sys/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <machine/clock.h>
+
+#include <dev/video/cxm/cxm.h>
+
+#include <bus/iicbus/iiconf.h>
+#include <bus/iicbus/iicbus.h>
+
+#include "iicbb_if.h"
+
+
+static const struct cxm_saa7115_command
+saa7115_init = {
+	19,
+	{
+		/* Full auto mode for CVBS */
+		{ 0x01, 1, { 0x08 } },
+		{ 0x03, 18, { 0x20, 0x90, 0x90, 0xeb, 0xe0, 0xb0, 0x40, 0x80,
+			      0x44, 0x40, 0x00, 0x03, 0x2a, 0x06, 0x00, 0x9d,
+			      0x80, 0x01 } },
+		{ 0x17, 7, { 0x99, 0x40, 0x80, 0x77, 0x42, 0xa9, 0x01 } },
+
+		/*
+		 * VBI data slicer
+		 *
+		 * NTSC raw VBI data on lines 10 through 21
+		 * PAL raw VBI data on lines 6 through 22
+		 *
+		 * Actually lines 21 and 22 are set by the
+		 * NTSC and PAL specific configurations.
+		 */
+		{ 0x40, 20, { 0x40, 0x00, 0x00, 0x00, 0x00, 0xdd, 0xdd, 0xdd,
+			      0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			      0xdd, 0xdd, 0xdd, 0xdd } },
+		{ 0x56, 4, { 0x00, 0x00, 0x00, 0x47 } },
+		{ 0x5c, 3, { 0x00, 0x1f, 0x35 } },
+
+		/* I-port and X-port configuration */
+		{ 0x80, 2, { 0x00, 0x01 } },
+		{ 0x83, 5, { 0x00, 0x20, 0x21, 0xc5, 0x01 } },
+
+		/* Scaler input configuration and output format settings */
+		{ 0xc0, 4, { 0x00, 0x08, 0x00, 0x80 } },
+
+		/* VBI scaler configuration */
+		{ 0x90, 4, { 0x80, 0x48, 0x00, 0x84 } },
+		{ 0xa0, 3, { 0x01, 0x00, 0x00 } },
+		{ 0xa4, 3, { 0x80, 0x40, 0x40 } },
+		{ 0xa8, 3, { 0x00, 0x02, 0x00 } },
+		{ 0xac, 3, { 0x00, 0x01, 0x00 } },
+		{ 0xb0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
+		{ 0xb8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+
+		/* Audio Master Clock to Audio Serial Clock ratio */
+		{ 0x38, 3, { 0x03, 0x10, 0x00 } },
+
+		/* PLL2 target clock 27 MHz (using a 32.11 MHz crystal) */
+		{ 0xf1, 4, { 0x05, 0xd0, 0x35, 0x00 } },
+
+		/* Pulse generator */
+		{ 0xf6, 10, { 0x61, 0x0e, 0x60, 0x0e, 0x60, 0x0e, 0x00,
+			      0x00, 0x00, 0x88 } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_mute = {
+	1,
+	{
+		/* Disable I-port */
+		{ 0x87, 1, { 0x00 } },
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_unmute = {
+	1,
+	{
+		/* Enable I-port */
+		{ 0x87, 1, { 0x01 } },
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_select_fm = {
+	1,
+	{
+		/* Enable audio clock */
+		{ 0x88, 1, { 0x33 } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_select_line_in_composite = {
+	3,
+	{
+		/* Amp plus anti-alias filter, CVBS from AI11 */
+		{ 0x02, 1, { 0xc0 } },
+		/* Adaptive luminance comb filter */
+		{ 0x09, 1, { 0x40 } },
+
+		/* Enable AD1, audio clock, scaler, decoder */
+		{ 0x88, 1, { 0x70 } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_select_line_in_svideo = {
+	3,
+	{
+		/* Amp plus anti-alias filter, Y / C from AI11 / AI21 */
+		{ 0x02, 1, { 0xc8 } },
+		/* Bypass chrominance trap / comb filter */
+		{ 0x09, 1, { 0x80 } },
+
+		/* Enable AD1 & 2, audio clock, scaler, decoder */
+		{ 0x88, 1, { 0xf0 } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_select_tuner = {
+	3,
+	{
+		/* Amp plus anti-alias filter, CVBS (auto gain) from AI23 */
+		{ 0x02, 1, { 0xc4 } },
+		/* Adaptive luminance comb filter */
+		{ 0x09, 1, { 0x40 } },
+
+		/* Enable AD2, audio clock, scaler, decoder */
+		{ 0x88, 1, { 0xb0 } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_audio_clock_44100_ntsc = {
+	2,
+	{
+		/* Audio clock 44.1 kHz NTSC (using a 32.11 MHz crystal) */
+		{ 0x30, 3, { 0xbc, 0xdf, 0x02 } },
+		{ 0x34, 3, { 0xf2, 0x00, 0x2d } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_audio_clock_44100_pal = {
+	2,
+	{
+		/* Audio clock 44.1 kHz PAL (using a 32.11 MHz crystal) */
+		{ 0x30, 3, { 0x00, 0x72, 0x03 } },
+		{ 0x34, 3, { 0xf2, 0x00, 0x2d } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_audio_clock_48000_ntsc = {
+	2,
+	{
+		/* Audio clock 48 kHz NTSC (using a 32.11 MHz crystal) */
+		{ 0x30, 3, { 0xcd, 0x20, 0x03 } },
+		{ 0x34, 3, { 0xce, 0xfb, 0x30 } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_audio_clock_48000_pal = {
+	2,
+	{
+		/* Audio clock 48 kHz PAL (using a 32.11 MHz crystal) */
+		{ 0x30, 3, { 0x00, 0xc0, 0x03 } },
+		{ 0x34, 3, { 0xce, 0xfb, 0x30 } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_scaler_vcd_ntsc_double_lines = {
+	13,
+	{
+		/*
+		 * Input window = 720 x 240, output window = 352 x 240 with
+		 * YS extended by 2 as per section 17.4 of the data sheet
+		 * and YO accounting for scaler processing triggering at
+		 * line 5 and active video starting at line 23 (see section
+		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
+		 * NTSC active video should actually start at line 22, however
+		 * not all channels / programs do.
+		 */
+		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x12, 0x00, 0xf2, 0x00,
+			      0x60, 0x01, 0xf0, 0x00 } },
+
+		/* Prefiltering and prescaling */
+		{ 0xd0, 3, { 0x02, 0x02, 0xaa } },
+
+		/* Brightness, contrast, and saturation */
+		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
+
+		/* Horizontal phase scaling */
+		{ 0xd8, 3, { 0x18, 0x04, 0x00 } },
+		{ 0xdc, 3, { 0x0c, 0x02, 0x00 } },
+
+		/* Vertical scaling */
+		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
+
+		/* Vertical phase offsets */
+		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+
+		/*
+		 * VBI input window = 720 x 12, output window = 1440 x 12.
+		 */
+		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x05, 0x00, 0x0c, 0x00,
+			      0xa0, 0x05, 0x0c, 0x00 } },
+
+		/* Inverted VGATE start at line 23, stop after line 263 */
+		{ 0x15, 2, { 0x02, 0x12 } },
+
+		/* VBI data slicer 525 lines, line 21 is closed caption */
+		{ 0x54, 2, { 0x4d, 0x00 } },
+		{ 0x5a, 2, { 0x06, 0x83 } },
+
+		/* PLL2 525 lines, 27 Mhz target clock */
+		{ 0xf0, 1, { 0xad } },
+
+		/* Pulse generator 525 lines, 27 Mhz target clock */
+		{ 0xf5, 1, { 0xad } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_scaler_vcd_pal_double_lines = {
+	13,
+	{
+		/*
+		 * Input window = 720 x 288, output window = 352 x 288 with
+		 * YS extended by 2 as per section 17.4 of the data sheet
+		 * and YO accounting for scaler processing triggering at
+		 * line 2 and active video starting at line 25 (see section
+		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
+		 * PAL active video should actually start at line 24, however
+		 * not all channels / programs do.
+		 */
+		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x17, 0x00, 0x22, 0x01,
+			      0x60, 0x01, 0x20, 0x01 } },
+
+		/* Prefiltering and prescaling */
+		{ 0xd0, 3, { 0x02, 0x02, 0xaa } },
+
+		/* Brightness, contrast, and saturation */
+		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
+
+		/* Horizontal phase scaling */
+		{ 0xd8, 3, { 0x18, 0x04, 0x00 } },
+		{ 0xdc, 3, { 0x0c, 0x02, 0x00 } },
+
+		/* Vertical scaling */
+		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
+
+		/* Vertical phase offsets */
+		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+
+		/*
+		 * VBI input window = 720 x 17, output window = 1440 x 17.
+		 */
+		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x04, 0x00, 0x11, 0x00,
+			      0xa0, 0x05, 0x11, 0x00 } },
+
+		/* Inverted VGATE start at line 25, stop after line 313 */
+		{ 0x15, 2, { 0x37, 0x17 } },
+
+		/* VBI data slicer 625 lines, line 22 is closed caption */
+		{ 0x54, 2, { 0xdd, 0x4d } },
+		{ 0x5a, 2, { 0x03, 0x03 } },
+
+		/* PLL2 625 lines, 27 Mhz target clock */
+		{ 0xf0, 1, { 0xb0 } },
+
+		/* Pulse generator 625 lines, 27 Mhz target clock */
+		{ 0xf5, 1, { 0xb0 } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_scaler_svcd_ntsc = {
+	13,
+	{
+		/*
+		 * Input window = 720 x 240, output window = 480 x 240 with
+		 * YS extended by 2 as per section 17.4 of the data sheet
+		 * and YO accounting for scaler processing triggering at
+		 * line 5 and active video starting at line 23 (see section
+		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
+		 * NTSC active video should actually start at line 22, however
+		 * not all channels / programs do.
+		 */
+		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x12, 0x00, 0xf2, 0x00,
+			      0xe0, 0x01, 0xf0, 0x00 } },
+
+		/* Prefiltering and prescaling */
+		{ 0xd0, 3, { 0x01, 0x00, 0x00 } },
+
+		/* Brightness, contrast, and saturation */
+		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
+
+		/* Horizontal phase scaling */
+		{ 0xd8, 3, { 0x00, 0x06, 0x00 } },
+		{ 0xdc, 3, { 0x00, 0x03, 0x00 } },
+
+		/* Vertical scaling */
+		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
+
+		/* Vertical phase offsets */
+		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+
+		/*
+		 * VBI input window = 720 x 12, output window = 1440 x 12.
+		 */
+		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x05, 0x00, 0x0c, 0x00,
+			      0xa0, 0x05, 0x0c, 0x00 } },
+
+		/* Inverted VGATE start at line 23, stop after line 263 */
+		{ 0x15, 2, { 0x02, 0x12 } },
+
+		/* VBI data slicer 525 lines, line 21 is closed caption */
+		{ 0x54, 2, { 0x4d, 0x00 } },
+		{ 0x5a, 2, { 0x06, 0x83 } },
+
+		/* PLL2 525 lines, 27 Mhz target clock */
+		{ 0xf0, 1, { 0xad } },
+
+		/* Pulse generator 525 lines, 27 Mhz target clock */
+		{ 0xf5, 1, { 0xad } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_scaler_svcd_pal = {
+	13,
+	{
+		/*
+		 * Input window = 720 x 288, output window = 480 x 288 with
+		 * YS extended by 2 as per section 17.4 of the data sheet
+		 * and YO accounting for scaler processing triggering at
+		 * line 2 and active video starting at line 25 (see section
+		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
+		 * PAL active video should actually start at line 24, however
+		 * not all channels / programs do.
+		 */
+		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x17, 0x00, 0x22, 0x01,
+			      0xe0, 0x01, 0x20, 0x01 } },
+
+		/* Prefiltering and prescaling */
+		{ 0xd0, 3, { 0x01, 0x00, 0x00 } },
+
+		/* Brightness, contrast, and saturation */
+		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
+
+		/* Horizontal phase scaling */
+		{ 0xd8, 3, { 0x00, 0x06, 0x00 } },
+		{ 0xdc, 3, { 0x00, 0x03, 0x00 } },
+
+		/* Vertical scaling */
+		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
+
+		/* Vertical phase offsets */
+		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+
+		/*
+		 * VBI input window = 720 x 17, output window = 1440 x 17.
+		 */
+		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x04, 0x00, 0x11, 0x00,
+			      0xa0, 0x05, 0x11, 0x00 } },
+
+		/* Inverted VGATE start at line 25, stop after line 313 */
+		{ 0x15, 2, { 0x37, 0x17 } },
+
+		/* VBI data slicer 625 lines, line 22 is closed caption */
+		{ 0x54, 2, { 0xdd, 0x4d } },
+		{ 0x5a, 2, { 0x03, 0x03 } },
+
+		/* PLL2 625 lines, 27 Mhz target clock */
+		{ 0xf0, 1, { 0xb0 } },
+
+		/* Pulse generator 625 lines, 27 Mhz target clock */
+		{ 0xf5, 1, { 0xb0 } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_scaler_dvd_ntsc = {
+	13,
+	{
+		/*
+		 * Input window = 720 x 240, output window = 720 x 240 with
+		 * YS extended by 2 as per section 17.4 of the data sheet
+		 * and YO accounting for scaler processing triggering at
+		 * line 5 and active video starting at line 23 (see section
+		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
+		 * NTSC active video should actually start at line 22, however
+		 * not all channels / programs do.
+		 */
+		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x12, 0x00, 0xf2, 0x00,
+			      0xd0, 0x02, 0xf0, 0x00 } },
+
+		/* Prefiltering and prescaling */
+		{ 0xd0, 3, { 0x01, 0x00, 0x00 } },
+
+		/* Brightness, contrast, and saturation */
+		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
+
+		/* Horizontal phase scaling */
+		{ 0xd8, 3, { 0x00, 0x04, 0x00 } },
+		{ 0xdc, 3, { 0x00, 0x02, 0x00 } },
+
+		/* Vertical scaling */
+		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
+
+		/* Vertical phase offsets */
+		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+
+		/*
+		 * VBI input window = 720 x 12, output window = 1440 x 12.
+		 */
+		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x05, 0x00, 0x0c, 0x00,
+			      0xa0, 0x05, 0x0c, 0x00 } },
+
+		/* Inverted VGATE start at line 23, stop after line 263 */
+		{ 0x15, 2, { 0x02, 0x12 } },
+
+		/* VBI data slicer 525 lines, line 21 is closed caption */
+		{ 0x54, 2, { 0x4d, 0x00 } },
+		{ 0x5a, 2, { 0x06, 0x83 } },
+
+		/* PLL2 525 lines, 27 Mhz target clock */
+		{ 0xf0, 1, { 0xad } },
+
+		/* Pulse generator 525 lines, 27 Mhz target clock */
+		{ 0xf5, 1, { 0xad } }
+	}
+};
+
+static const struct cxm_saa7115_command
+saa7115_scaler_dvd_pal = {
+	13,
+	{
+		/*
+		 * Input window = 720 x 288, output window = 720 x 288 with
+		 * YS extended by 2 as per section 17.4 of the data sheet
+		 * and YO accounting for scaler processing triggering at
+		 * line 2 and active video starting at line 25 (see section
+		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
+		 * PAL active video should actually start at line 24, however
+		 * not all channels / programs do.
+		 */
+		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x17, 0x00, 0x22, 0x01,
+			      0xd0, 0x02, 0x20, 0x01 } },
+
+		/* Prefiltering and prescaling */
+		{ 0xd0, 3, { 0x01, 0x00, 0x00 } },
+
+		/* Brightness, contrast, and saturation */
+		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
+
+		/* Horizontal phase scaling */
+		{ 0xd8, 3, { 0x00, 0x04, 0x00 } },
+		{ 0xdc, 3, { 0x00, 0x02, 0x00 } },
+
+		/* Vertical scaling */
+		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
+
+		/* Vertical phase offsets */
+		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+
+		/*
+		 * VBI input window = 720 x 17, output window = 1440 x 17.
+		 */
+		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x04, 0x00, 0x11, 0x00,
+			      0xa0, 0x05, 0x11, 0x00 } },
+
+		/* Inverted VGATE start at line 25, stop after line 313 */
+		{ 0x15, 2, { 0x37, 0x17 } },
+
+		/* VBI data slicer 625 lines, line 22 is closed caption */
+		{ 0x54, 2, { 0xdd, 0x4d } },
+		{ 0x5a, 2, { 0x03, 0x03 } },
+
+		/* PLL2 625 lines, 27 Mhz target clock */
+		{ 0xf0, 1, { 0xb0 } },
+
+		/* Pulse generator 625 lines, 27 Mhz target clock */
+		{ 0xf5, 1, { 0xb0 } }
+	}
+};
+
+
+static const struct cxm_saa7115_audio_clock
+saa7115_audio_clock[] = {
+	{ 44100, 30, &saa7115_audio_clock_44100_ntsc },
+	{ 44100, 25, &saa7115_audio_clock_44100_pal },
+	{ 48000, 30, &saa7115_audio_clock_48000_ntsc },
+	{ 48000, 25, &saa7115_audio_clock_48000_pal }
+};
+
+static const struct cxm_saa7115_scaling
+saa7115_scalings[] = {
+	{ 352, 480, 30, &saa7115_scaler_vcd_ntsc_double_lines },
+	{ 352, 576, 25, &saa7115_scaler_vcd_pal_double_lines },
+	{ 480, 480, 30, &saa7115_scaler_svcd_ntsc },
+	{ 480, 576, 25, &saa7115_scaler_svcd_pal },
+	{ 720, 480, 30, &saa7115_scaler_dvd_ntsc },
+	{ 720, 576, 25, &saa7115_scaler_dvd_pal }
+};
+
+
+/* Reset the SAA7115 chip */
+static int
+cxm_saa7115_reset( device_t iicbus, int i2c_addr )
+{
+	unsigned char msg[2];
+	int sent;
+
+	/* put into reset mode */
+	msg[0] = 0x88;
+	msg[1] = 0x0b;
+
+	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
+	    || sent != sizeof(msg))
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+	/* put back to operational mode */
+	msg[0] = 0x88;
+	msg[1] = 0x2b;
+
+	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
+	    || sent != sizeof(msg))
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+	return 0;
+
+fail:
+	iicbus_stop(iicbus);
+	return -1;
+}
+
+
+/* Read from the SAA7115 registers */
+static int
+cxm_saa7115_read( device_t iicbus, int i2c_addr,
+		  unsigned char addr, char *buf, int len )
+{
+	unsigned char msg[1];
+	int received;
+	int sent;
+
+	msg[0] = addr;
+
+	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
+	    || sent != sizeof(msg))
+		goto fail;
+
+	if (iicbus_repeated_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
+		goto fail;
+
+	if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+	return received;
+
+fail:
+	iicbus_stop(iicbus);
+	return -1;
+}
+
+
+/* Write to the SAA7115 registers */
+static int
+cxm_saa7115_write( device_t iicbus, int i2c_addr,
+		   unsigned char addr, const char *buf, int len )
+{
+	unsigned char msg[1];
+	int sent;
+
+	msg[0] = addr;
+
+	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
+	    || sent != sizeof(msg))
+		goto fail;
+
+	if (iicbus_write(iicbus, buf, len, &sent, CXM_I2C_TIMEOUT) != 0)
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+	return sent;
+
+fail:
+	iicbus_stop(iicbus);
+	return -1;
+}
+
+
+int
+cxm_saa7115_init( struct cxm_softc *sc )
+{
+	char name[5];
+	unsigned char id[1];
+	unsigned char rev;
+	unsigned int i;
+	unsigned int nsettings;
+	const struct cxm_saa7115_setting *settings;
+
+	if (cxm_saa7115_reset (sc->iicbus, CXM_I2C_SAA7115) < 0)
+		return -1;
+
+	name[4] = '\0';
+	for (i = 0; i < 4; i++) {
+		id[0] = 2 + i;
+
+		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x00,
+				      id, sizeof(id)) != sizeof(id))
+			return -1;
+
+		if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x00,
+				     id, sizeof(id)) != sizeof(id))
+			return -1;
+
+		name[i] = '0' + (id[0] & 0x0f);
+		rev = id[0] >> 4;
+	}
+
+	/*
+	 * SAA 7115 is the only video decoder currently supported.
+	 */
+
+	nsettings = 0;
+	settings = NULL;
+
+	if (strcmp(name, "7115") == 0) {
+		nsettings = saa7115_init.nsettings;
+		settings = saa7115_init.settings;
+	  }
+	else {
+		kprintf("%s: unknown video decoder SAA%s\n",
+		       sc->name, name);
+		return -1;
+	}
+
+	for (i = 0; i < nsettings; i++)
+		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
+				      settings[i].addr,
+				      settings[i].values, settings[i].nvalues)
+		    != settings[i].nvalues)
+			return -1;
+
+	if (cxm_saa7115_select_source(sc, cxm_tuner_source) < 0)
+		return -1;
+
+	kprintf("%s: SAA%s rev %u video decoder\n",
+	       sc->name, name, (unsigned int)rev);
+
+	return 0;
+}
+
+
+int
+cxm_saa7115_mute( struct cxm_softc *sc )
+{
+	unsigned int i;
+	unsigned int nsettings;
+	const struct cxm_saa7115_setting *settings;
+
+	nsettings = saa7115_mute.nsettings;
+	settings = saa7115_mute.settings;
+
+	for (i = 0; i < nsettings; i++)
+		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
+				      settings[i].addr,
+				      settings[i].values, settings[i].nvalues)
+		    != settings[i].nvalues)
+			return -1;
+
+	return 0;
+}
+
+
+int
+cxm_saa7115_unmute( struct cxm_softc *sc )
+{
+	unsigned int i;
+	unsigned int nsettings;
+	const struct cxm_saa7115_setting *settings;
+
+	nsettings = saa7115_unmute.nsettings;
+	settings = saa7115_unmute.settings;
+
+	for (i = 0; i < nsettings; i++)
+		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
+				      settings[i].addr,
+				      settings[i].values, settings[i].nvalues)
+		    != settings[i].nvalues)
+			return -1;
+
+	return 0;
+}
+
+
+int
+cxm_saa7115_select_source( struct cxm_softc *sc, enum cxm_source source )
+{
+	unsigned int i;
+	unsigned int nsettings;
+	const struct cxm_saa7115_setting *settings;
+
+	switch (source) {
+	case cxm_fm_source:
+		nsettings = saa7115_select_fm.nsettings;
+		settings = saa7115_select_fm.settings;
+		break;
+
+	case cxm_line_in_source_composite:
+		nsettings = saa7115_select_line_in_composite.nsettings;
+		settings = saa7115_select_line_in_composite.settings;
+		break;
+
+	case cxm_line_in_source_svideo:
+		nsettings = saa7115_select_line_in_svideo.nsettings;
+		settings = saa7115_select_line_in_svideo.settings;
+		break;
+
+	case cxm_tuner_source:
+		nsettings = saa7115_select_tuner.nsettings;
+		settings = saa7115_select_tuner.settings;
+		break;
+
+	default:
+		return -1;
+	}
+
+	for (i = 0; i < nsettings; i++)
+		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
+				      settings[i].addr,
+				      settings[i].values, settings[i].nvalues)
+		    != settings[i].nvalues)
+			return -1;
+
+	return 0;
+}
+
+
+int
+cxm_saa7115_configure( struct cxm_softc *sc,
+		       unsigned int width, unsigned int height,
+		       unsigned int fps, unsigned int audio_sample_rate )
+{
+	unsigned char power[1];
+	unsigned char task[1];
+	unsigned int i;
+	unsigned int nsettings;
+	const struct cxm_saa7115_setting *settings;
+
+	for (i = 0; NUM_ELEMENTS(saa7115_scalings); i++)
+		if (saa7115_scalings[i].width == width
+		    && saa7115_scalings[i].height == height
+		    && saa7115_scalings[i].fps == fps)
+			break;
+
+	if (i >= NUM_ELEMENTS(saa7115_scalings))
+		return -1;
+
+	nsettings = saa7115_scalings[i].scaling->nsettings;
+	settings = saa7115_scalings[i].scaling->settings;
+
+	/*
+	 * Reset the scaler.
+	 */
+
+	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x88,
+			     power, sizeof(power)) != sizeof(power))
+		return -1;
+
+	power[0] &= ~0x20;
+
+	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x88,
+			      power, sizeof(power)) != sizeof(power))
+		return -1;
+
+	/*
+	 * Configure the scaler.
+	 */
+
+	for (i = 0; i < nsettings; i++)
+		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
+				      settings[i].addr,
+				      settings[i].values, settings[i].nvalues)
+		    != settings[i].nvalues)
+			return -1;
+
+	/*
+	 * Enable task register set A and B.
+	 */
+
+	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x80,
+			     task, sizeof(task)) != sizeof(task))
+		return -1;
+
+	task[0] |= 0x30;
+
+	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x80,
+			      task, sizeof(task)) != sizeof(task))
+		return -1;
+
+	/*
+	 * Enable the scaler.
+	 */
+
+	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x88,
+			     power, sizeof(power)) != sizeof(power))
+		return -1;
+
+	power[0] |= 0x20;
+
+	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x88,
+			      power, sizeof(power)) != sizeof(power))
+		return -1;
+
+	/*
+	 * Configure the audio clock.
+	 */
+
+	for (i = 0; NUM_ELEMENTS(saa7115_audio_clock); i++)
+		if (saa7115_audio_clock[i].sample_rate == audio_sample_rate
+		    && saa7115_audio_clock[i].fps == fps)
+			break;
+
+	if (i >= NUM_ELEMENTS(saa7115_audio_clock))
+		return -1;
+
+	nsettings = saa7115_audio_clock[i].clock->nsettings;
+	settings = saa7115_audio_clock[i].clock->settings;
+
+	for (i = 0; i < nsettings; i++)
+		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
+				      settings[i].addr,
+				      settings[i].values, settings[i].nvalues)
+		    != settings[i].nvalues)
+			return -1;
+
+	return 0;
+}
+
+
+enum cxm_source_format
+cxm_saa7115_detected_format( struct cxm_softc *sc )
+{
+	unsigned char status[2];
+	enum cxm_source_format source_format;
+
+	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x1e,
+			     status, sizeof(status)) != sizeof(status))
+		return cxm_unknown_source_format;
+
+	if (! (status[1] & 0x01) ) {
+		kprintf("%s: video decoder isn't locked\n", sc->name);
+		return cxm_unknown_source_format;
+	}
+
+	source_format = cxm_unknown_source_format;
+
+	if (! (status[1] & 0x20) )
+		switch (status[0] & 0x03) {
+		case 0:
+			source_format = cxm_bw_50hz_source_format;
+			break;
+
+		case 1:
+			source_format = cxm_ntsc_50hz_source_format;
+			break;
+
+		case 2:
+			source_format = cxm_pal_50hz_source_format;
+			break;
+
+		case 3:
+			source_format = cxm_secam_50hz_source_format;
+			break;
+
+		default:
+			break;
+		}
+	else
+		switch (status[0] & 0x03) {
+		case 0:
+			source_format = cxm_bw_60hz_source_format;
+			break;
+
+		case 1:
+			source_format = cxm_ntsc_60hz_source_format;
+			break;
+
+		case 2:
+			source_format = cxm_pal_60hz_source_format;
+			break;
+
+		default:
+			break;
+		}
+
+	return source_format;
+}
+
+
+int
+cxm_saa7115_detected_fps( struct cxm_softc *sc )
+{
+	unsigned char status[1];
+
+	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x1f,
+			     status, sizeof(status)) != sizeof(status))
+		return -1;
+
+	if (! (status[0] & 0x01) ) {
+		kprintf("%s: video decoder isn't locked\n", sc->name);
+		return -1;
+	}
+
+	return (status[0] & 0x20) ? 30 : 25;
+}
+
+
+int
+cxm_saa7115_get_brightness( struct cxm_softc *sc )
+{
+	unsigned char brightness;
+
+	/*
+	 * Brightness is treated as an unsigned value by the decoder.
+	 * 0 = dark, 128 = ITU level, 255 = bright
+	 */
+	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x0a,
+			     &brightness, sizeof(brightness))
+	    != sizeof(brightness))
+		return -1;
+
+	return brightness;
+}
+
+
+int
+cxm_saa7115_set_brightness( struct cxm_softc *sc, unsigned char brightness )
+{
+
+	/*
+	 * Brightness is treated as an unsigned value by the decoder.
+	 * 0 = dark, 128 = ITU level, 255 = bright
+	 */
+	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x0a,
+			      &brightness, sizeof(brightness))
+	    != sizeof(brightness))
+		return -1;
+
+	return 0;
+}
+
+
+int
+cxm_saa7115_get_chroma_saturation( struct cxm_softc *sc )
+{
+	unsigned char chroma_saturation;
+
+	/*
+	 * Chroma saturation is treated as a signed value by the decoder.
+	 * -128 = -2.0 (inverse chrominance), -64 = 1.0 (inverse chrominance),
+	 * 0 = 0 (color off), 64 = 1.0 (ITU level), 127 = 1.984 (maximum)
+	 */
+	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x0c,
+			     &chroma_saturation, sizeof(chroma_saturation))
+	    != sizeof(chroma_saturation))
+		return -1;
+
+	return chroma_saturation;
+}
+
+
+int
+cxm_saa7115_set_chroma_saturation( struct cxm_softc *sc,
+				   unsigned char chroma_saturation )
+{
+
+	/*
+	 * Chroma saturation is treated as a signed value by the decoder.
+	 * -128 = -2.0 (inverse chrominance), -64 = 1.0 (inverse chrominance),
+	 * 0 = 0 (color off), 64 = 1.0 (ITU level), 127 = 1.984 (maximum)
+	 */
+	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x0c,
+			      &chroma_saturation, sizeof(chroma_saturation))
+	    != sizeof(chroma_saturation))
+		return -1;
+
+	return 0;
+}
+
+
+int
+cxm_saa7115_get_contrast( struct cxm_softc *sc )
+{
+	unsigned char contrast;
+
+	/*
+	 * Contrast is treated as a signed value by the decoder.
+	 * -128 = -2.0 (inverse luminance), -64 = 1.0 (inverse luminance),
+	 * 0 = 0 (luminance off), 64 = 1.0, 68 = 1.063 (ITU level),
+	 * 127 = 1.984 (maximum)
+	 */
+	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x0b,
+			     &contrast, sizeof(contrast)) != sizeof(contrast))
+		return -1;
+
+	return contrast;
+}
+
+
+int
+cxm_saa7115_set_contrast( struct cxm_softc *sc, unsigned char contrast )
+{
+
+	/*
+	 * Contrast is treated as a signed value by the decoder.
+	 * -128 = -2.0 (inverse luminance), -64 = 1.0 (inverse luminance),
+	 * 0 = 0 (luminance off), 64 = 1.0, 68 = 1.063 (ITU level),
+	 * 127 = 1.984 (maximum)
+	 */
+	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x0b,
+			      &contrast, sizeof(contrast)) != sizeof(contrast))
+		return -1;
+
+	return 0;
+}
+
+
+int
+cxm_saa7115_get_hue( struct cxm_softc *sc )
+{
+	unsigned char hue;
+
+	/*
+	 * Hue is treated as a signed value by the decoder.
+	 * -128 = -180.0, 0 = 0.0, 127 = +178.6
+	 */
+	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x0d,
+			     &hue, sizeof(hue))
+	    != sizeof(hue))
+		return -1;
+
+	return hue;
+}
+
+
+int
+cxm_saa7115_set_hue( struct cxm_softc *sc, unsigned char hue )
+{
+
+	/*
+	 * Hue is treated as a signed value by the decoder.
+	 * -128 = -180.0, 0 = 0.0, 127 = +178.6
+	 */
+	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x0d,
+			      &hue, sizeof(hue))
+	    != sizeof(hue))
+		return -1;
+
+	return 0;
+}
+
+
+int
+cxm_saa7115_is_locked( struct cxm_softc *sc )
+{
+	unsigned char status[1];
+
+	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x1f,
+			     status, sizeof(status)) != sizeof(status))
+		return -1;
+
+	return (status[0] & 0x01) ? 1 : 0;
+}
+
+
+int
+cxm_saa7115_wait_for_lock( struct cxm_softc *sc )
+{
+	unsigned int i;
+
+	/*
+	 * Section 2.7 of the data sheet states:
+	 *
+	 *   Ultra-fast frame lock (almost 1 field)
+	 *
+	 * so hopefully 500 ms is enough (the lock
+	 * sometimes takes a long time to occur ...
+	 * possibly due to the time it takes to
+	 * autodetect the format).
+	 */
+
+	for (i = 0; i < 10; i++) {
+
+		/*
+		 * The input may have just changed (prior to
+		 * cxm_saa7115_wait_for_lock) so start with
+		 * the delay to give the video decoder a
+		 * chance to update its status.
+		 */
+
+		tsleep(&sc->iicbus, 0, "video", hz / 20 );
+
+		switch (cxm_saa7115_is_locked(sc)) {
+		case 1:
+			return 1;
+
+		case 0:
+			break;
+
+		default:
+			return -1;
+		}
+	}
+
+	kprintf("%s: video decoder failed to lock\n", sc->name);
+
+	return 0;
+}
diff --git a/sys/dev/video/cxm/cxm_tuner.c b/sys/dev/video/cxm/cxm_tuner.c
new file mode 100644
index 0000000..23b65dd
--- /dev/null
+++ b/sys/dev/video/cxm/cxm_tuner.c
@@ -0,0 +1,998 @@
+/*
+ * Copyright (c) 2003, 2004, 2005
+ *	John Wehle <john@feith.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by John Wehle.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Tuner routines for the Conexant MPEG-2 Codec driver.
+ *
+ * Ideally these routines should be implemented as a separate
+ * driver which has a generic tuner interface so that it's
+ * not necessary for each multimedia driver to re-invent the
+ * wheel.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <sys/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <machine/clock.h>
+
+#include <dev/video/meteor/ioctl_meteor.h>
+#include <dev/video/bktr/ioctl_bt848.h>
+
+#include <dev/video/cxm/cxm.h>
+
+#include <bus/iicbus/iiconf.h>
+#include <bus/iicbus/iicbus.h>
+
+#include "iicbb_if.h"
+
+
+/*
+ * Channel mappings derived from
+ * http://developer.apple.com/technotes/tn/tn1012.html
+ *
+ * B/G Cable mapping based the bktr Western European mapping
+ */
+
+static const struct cxm_tuner_channels
+us_air_channels = {
+	"US Broadcast",
+	CHNLSET_NABCST,
+	CXM_TUNER_TV_SYSTEM_MN,
+	2,
+	69,
+	45750,
+	{ { 14, 471250, 6000 },
+	  { 7, 175250, 6000 },
+	  { 5, 77250, 6000 },
+	  { 2, 55250, 6000 } }
+};
+
+static const struct cxm_tuner_channels
+us_cable_channels = {
+	"US Cable",
+	CHNLSET_CABLEIRC,
+	CXM_TUNER_TV_SYSTEM_MN,
+	1,
+	125,
+	45750,
+	{ { 100, 649250, 6000 },
+	  { 95, 91250, 6000 },
+	  { 23, 217250, 6000 },
+	  { 14, 121250, 6000 },
+	  { 7, 175250, 6000 },
+	  { 5, 77250, 6000 },
+	  { 2, 55250, 6000 },
+	  { 1, 73250, 6000 } }
+};
+
+static const struct cxm_tuner_channels
+bg_air_channels = {
+	"B/G Broadcast",
+	0,
+	CXM_TUNER_TV_SYSTEM_BG,
+	2,
+	89,
+	38900,
+	{ { 82, 175250, 7000 },
+	  { 80, 55250, 7000 },
+	  { 79, 217250, 7000 },
+	  { 77, 209250, 7000 },
+	  { 76, 138250, 9000 },
+	  { 75, 102250, 9000 },
+	  { 73, 86250, 9000 },
+	  { 72, 64250, 7500 },
+	  { 70, 49750, 7500 },
+	  { 21, 471250, 8000 },
+	  { 20, 210250, 8500 },
+	  { 18, 192750, 8500 },
+	  { 16, 175250, 8000 },
+	  { 15, 82250, 8500 },
+	  { 13, 53750, 8500 },
+	  { 5, 175250, 7000 },
+	  { 2, 48250, 7000 } }
+};
+
+static const struct cxm_tuner_channels
+bg_cable_channels = {
+	"B/G Cable",
+	CHNLSET_WEUROPE,
+	CXM_TUNER_TV_SYSTEM_BG,
+	2,
+	120,
+	38900,
+	{ { 100, 303250, 8000 },
+	  { 90, 231250, 7000 },
+	  { 80, 105250, 7000 },
+	  { 79, 0, 0 },
+	  { 78, 0, 0 },
+	  { 77, 0, 0 },
+	  { 74, 69250, 7000 },
+	  { 73, 0, 0 },
+	  { 70, 45750, 8000 },
+	  { 21, 471250, 8000 },
+	  { 20, 210250, 8500 },
+	  { 18, 192750, 8500 },
+	  { 16, 175250, 8000 },
+	  { 15, 82250, 8500 },
+	  { 13, 53750, 8500 },
+	  { 5, 175250, 7000 },
+	  { 2, 48250, 7000 } }
+};
+
+static const struct cxm_tuner_channels
+bg_australia_channels = {
+	"B/G Australia",
+	CHNLSET_AUSTRALIA,
+	CXM_TUNER_TV_SYSTEM_BG,
+	1,
+	83,
+	38900,
+	{ { 28, 527250, 7000 },
+	  { 10, 209250, 7000 },
+	  { 6, 175250, 7000 },
+	  { 4, 95250, 7000 },
+	  { 3, 86250, 7000 },
+	  { 1, 57250, 7000 } }
+};
+
+static const struct cxm_tuner_channels
+i_air_channels = {
+	"I Broadcast",
+	0,
+	CXM_TUNER_TV_SYSTEM_I,
+	1,
+	83,
+	38900,
+	{ { 75, 179750, 5000 },
+	  { 71, 51750, 5000 },
+	  { 70, 45000, 5000 },
+	  { 21, 471250, 8000 },
+	  { 20, 0, 0 },
+	  { 19, 0, 0 },
+	  { 18, 0, 0 },
+	  { 17, 0, 0 },
+	  { 16, 0, 0 },
+	  { 15, 0, 0 },
+	  { 14, 0, 0 },
+	  { 13, 247250, 8000 },
+	  { 12, 0, 0 },
+	  { 4, 175250, 8000 },
+	  { 1, 45750, 8000 } }
+};
+
+static const struct cxm_tuner_channels
+l_air_channels = {
+	"L Broadcast",
+	CHNLSET_FRANCE,
+	CXM_TUNER_TV_SYSTEM_L,
+	1,
+	69,
+	38900,
+	{ { 21, 471250, 8000 },
+	  { 20, 0, 0 },
+	  { 19, 0, 0 },
+	  { 18, 0, 0 },
+	  { 17, 0, 0 },
+	  { 16, 0, 0 },
+	  { 15, 0, 0 },
+	  { 14, 0, 0 },
+	  { 8, 176000, 8000 },
+	  { 5, 57250, 3250 },
+	  { 4, 55750, 1500 },
+	  { 3, 54000, 1750 },
+	  { 2, 49250, 4750 },
+	  { 1, 47750, 1500 } }
+};
+
+static const struct cxm_tuner_channels
+japan_air_channels = {
+	"Japan Broadcast",
+	CHNLSET_JPNBCST,
+	CXM_TUNER_TV_SYSTEM_MN,
+	1,
+	62,
+	45750,
+	{ { 13, 471250, 6000 },
+	  { 8, 193250, 6000 },
+	  { 4, 171250, 6000 },
+	  { 1, 91250, 6000 } }
+};
+
+static const struct cxm_tuner_channels
+japan_cable_channels = {
+	"Japan Cable",
+	CHNLSET_JPNCABLE,
+	CXM_TUNER_TV_SYSTEM_MN,
+	1,
+	63,
+	45750,
+	{ { 23, 223250, 6000 },
+	  { 22, 165250, 6000 },
+	  { 13, 109250, 6000 },
+	  { 8, 193250, 6000 },
+	  { 4, 171250, 6000 },
+	  { 1, 91250, 6000 } }
+};
+
+
+static const struct cxm_tuner_channels
+*channel_sets[] = {
+	&us_air_channels,
+	&us_cable_channels,
+	&bg_air_channels,
+	&bg_cable_channels,
+	&bg_australia_channels,
+	&i_air_channels,
+	&l_air_channels,
+	&japan_air_channels,
+	&japan_cable_channels
+};
+
+
+const struct cxm_tuner
+cxm_tuners[CXM_TUNER_TYPES] = {
+	{ "Philips FI1216 MK2",
+		{ CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
+		48250, 855250,
+		{ { 450000, { 0x8e, 0x30 } },
+		  { 170000, { 0x8e, 0x90 } },
+		  { 48250, { 0x8e, 0xa0 } } },
+		0, 0,
+		{ 0 },
+		&bg_air_channels },
+	{ "Philips FM1216",
+		{ CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
+		48250, 855250,
+		{ { 450000, { 0xce, 0x30 } },
+		  { 170000, { 0xce, 0x90 } },
+		  { 48250, { 0xce, 0xa0 } } },
+		87500, 108000,
+		{ 87500, { 0x88, 0xa5 } },
+		&bg_air_channels },
+	{ "Philips FQ1216ME",
+		{ CXM_TUNER_TV_SYSTEM_BG | CXM_TUNER_TV_SYSTEM_DK
+		  | CXM_TUNER_TV_SYSTEM_I
+		  | CXM_TUNER_TV_SYSTEM_L | CXM_TUNER_TV_SYSTEM_L_PRIME,
+		  cxm_port_system_code_style,
+		  { { CXM_TUNER_TV_SYSTEM_BG,      { 0x09 } },
+		    { CXM_TUNER_TV_SYSTEM_DK,      { 0x09 } },
+		    { CXM_TUNER_TV_SYSTEM_I,       { 0x01 } },
+		    { CXM_TUNER_TV_SYSTEM_L,       { 0x0b } },
+		    { CXM_TUNER_TV_SYSTEM_L_PRIME, { 0x0a } } } },
+		48250, 855250,
+		{ { 450000, { 0x8e, 0x30 } },
+		  { 170000, { 0x8e, 0x90 } },
+		  { 48250, { 0x8e, 0xa0 } } },
+		0, 0,
+		{ 0 },
+		&l_air_channels },
+	{ "Philips FQ1216ME MK3",
+		{ CXM_TUNER_TV_SYSTEM_BG | CXM_TUNER_TV_SYSTEM_DK
+		  | CXM_TUNER_TV_SYSTEM_I
+		  | CXM_TUNER_TV_SYSTEM_L | CXM_TUNER_TV_SYSTEM_L_PRIME,
+		  cxm_if_system_with_aux_code_style,
+		  { { CXM_TUNER_TV_SYSTEM_BG,      { 0x16, 0x70, 0x49, 0x40 }},
+		    { CXM_TUNER_TV_SYSTEM_DK,      { 0x16, 0x70, 0x4b, 0x40 }},
+		    { CXM_TUNER_TV_SYSTEM_I,       { 0x16, 0x70, 0x4a, 0x40 }},
+		    { CXM_TUNER_TV_SYSTEM_L,       { 0x06, 0x50, 0x4b, 0x50 }},
+		    { CXM_TUNER_TV_SYSTEM_L_PRIME, { 0x86, 0x50, 0x53, 0x50 }}
+		    } },
+		48250, 863250,
+		{ { 442000, { 0xce, 0x04 } },
+		  { 160000, { 0xce, 0x02 } },
+		  { 48250, { 0xce, 0x01 } } },
+		0, 0,
+		{ 0 },
+		&l_air_channels },
+	{ "Philips FM1216ME MK3",
+		{ CXM_TUNER_TV_SYSTEM_BG | CXM_TUNER_TV_SYSTEM_DK
+		  | CXM_TUNER_TV_SYSTEM_I
+		  | CXM_TUNER_TV_SYSTEM_L | CXM_TUNER_TV_SYSTEM_L_PRIME,
+		  cxm_if_system_with_aux_code_style,
+		  { { CXM_TUNER_TV_SYSTEM_BG,      { 0x16, 0x70, 0x49, 0x40 }},
+		    { CXM_TUNER_TV_SYSTEM_DK,      { 0x16, 0x70, 0x4b, 0x40 }},
+		    { CXM_TUNER_TV_SYSTEM_I,       { 0x16, 0x70, 0x4a, 0x40 }},
+		    { CXM_TUNER_TV_SYSTEM_L,       { 0x06, 0x50, 0x4b, 0x50 }},
+		    { CXM_TUNER_TV_SYSTEM_L_PRIME, { 0x86, 0x50, 0x53, 0x50 }},
+		    { CXM_TUNER_FM_SYSTEM,         { 0x0a, 0x90, 0x20, 0x40 }}
+		    } },
+		48250, 863250,
+		{ { 442000, { 0xce, 0x04 } },
+		  { 160000, { 0xce, 0x02 } },
+		  { 48250, { 0xce, 0x01 } } },
+		87500, 108000,
+		{ 87500, { 0x88, 0x19 } },
+		&l_air_channels },
+	{ "Philips FI1236 MK2",
+		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
+		55250, 801250,
+		{ { 454000, { 0x8e, 0x30 } },
+		  { 160000, { 0x8e, 0x90 } },
+		  { 55250, { 0x8e, 0xa0 } } },
+		0, 0,
+		{ 0 },
+		&us_cable_channels },
+	{ "Philips FM1236",
+		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
+		55250, 801250,
+		{ { 454000, { 0xce, 0x30 } },
+		  { 160000, { 0xce, 0x90 } },
+		  { 55250, { 0xce, 0xa0 } } },
+		76000, 108000,
+		{ 76000, { 0x88, 0xa5 } },
+		&us_cable_channels },
+	{ "Philips FI1246 MK2",
+		{ CXM_TUNER_TV_SYSTEM_I, cxm_none_system_code_style },
+		45750, 855250,
+		{ { 450000, { 0x8e, 0x30 } },
+		  { 170000, { 0x8e, 0x90 } },
+		  { 45750, { 0x8e, 0xa0 } } },
+		0, 0,
+		{ 0 },
+		&i_air_channels },
+	{ "Philips FM1246",
+		{ CXM_TUNER_TV_SYSTEM_I, cxm_none_system_code_style },
+		45750, 855250,
+		{ { 450000, { 0xce, 0x30 } },
+		  { 170000, { 0xce, 0x90 } },
+		  { 45750, { 0xce, 0xa0 } } },
+		87500, 108000,
+		{ 87500, { 0x88, 0xa5 } },
+		&i_air_channels },
+	{ "Temic 4006 FH5",
+		{ CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
+		48250, 855250,
+		{ { 454000, { 0x8e, 0x30 } },
+		  { 169000, { 0x8e, 0x90 } },
+		  { 48250, { 0x8e, 0xa0 } } },
+		0, 0,
+		{ 0 },
+		&bg_air_channels },
+	{ "Temic 4009 FR5",
+		{ CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
+		48250, 855250,
+		{ { 464000, { 0x8e, 0x30 } },
+		  { 141000, { 0x8e, 0x90 } },
+		  { 48250, { 0x8e, 0xa0 } } },
+		87500, 108100,
+		{ 87500, { 0x88, 0xa5 } },
+		&bg_air_channels },
+	{ "Temic 4036 FY5",
+		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
+		55250, 801250,
+		{ { 453000, { 0x8e, 0x30 } },
+		  { 158000, { 0x8e, 0x90 } },
+		  { 55250, { 0x8e, 0xa0 } } },
+		0, 0,
+		{ 0 },
+		&us_cable_channels },
+	{ "Temic 4039 FR5",
+		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
+		55250, 801250,
+		{ { 453000, { 0x8e, 0x30 } },
+		  { 158000, { 0x8e, 0x90 } },
+		  { 55250, { 0x8e, 0xa0 } } },
+		75900, 108100,
+		{ 75900, { 0x88, 0xa5 } },
+		&us_cable_channels },
+	{ "Temic 4066 FY5",
+		{ CXM_TUNER_TV_SYSTEM_I, cxm_none_system_code_style },
+		45750, 855250,
+		{ { 454000, { 0x8e, 0x30 } },
+		  { 169000, { 0x8e, 0x90 } },
+		  { 45750, { 0x8e, 0xa0 } } },
+		0, 0,
+		{ 0 },
+		&i_air_channels },
+	{ "LG Innotek TPI8PSB11D",
+		{ CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
+		48250, 855250,
+		{ { 450000, { 0x8e, 0x30 } },
+		  { 170000, { 0x8e, 0x90 } },
+		  { 48250, { 0x8e, 0xa0 } } },
+		0, 0,
+		{ 0 },
+		&bg_air_channels },
+	{ "LG Innotek TPI8PSB01N",
+		{ CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
+		48250, 855250,
+		{ { 450000, { 0x8e, 0x30 } },
+		  { 170000, { 0x8e, 0x90 } },
+		  { 48250, { 0x8e, 0xa0 } } },
+		87500, 108000,
+		{ 87500, { 0x88, 0xa5 } },
+		&bg_air_channels },
+	{ "LG Innotek TAPC-H701F",
+		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
+		55250, 801250,
+		{ { 450000, { 0xce, 0x08 } },
+		  { 165000, { 0xce, 0x02 } },
+		  { 55250, { 0xce, 0x01 } } },
+		0, 0,
+		{ 0 },
+		&us_cable_channels },
+	{ "LG Innotek TAPC-H001F",
+		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
+		55250, 801250,
+		{ { 450000, { 0xce, 0x08 } },
+		  { 165000, { 0xce, 0x02 } },
+		  { 55250, { 0xce, 0x01 } } },
+		76000, 108000,
+		{ 76000, { 0x88, 0x04 } },
+		&us_cable_channels },
+	{ "LG Innotek TAPE-H001F",
+		{ CXM_TUNER_TV_SYSTEM_MN,
+		  cxm_if_system_with_aux_code_style,
+		  { { CXM_TUNER_TV_SYSTEM_MN,      { 0x16, 0x30, 0x44, 0x30 }},
+		    { CXM_TUNER_FM_SYSTEM,         { 0x0a, 0x90, 0x20, 0x30 }}
+		    } },
+		48250, 801250,
+		{ { 442000, { 0xce, 0x04 } },
+		  { 160000, { 0xce, 0x02 } },
+		  { 48250, { 0xce, 0x01 } } },
+		88000, 108000,
+		{ 88000, { 0x88, 0x19 } },
+		&us_cable_channels },
+	{ "Microtune 4049 FM5",
+		{ CXM_TUNER_TV_SYSTEM_BG | CXM_TUNER_TV_SYSTEM_DK
+		  | CXM_TUNER_TV_SYSTEM_I
+		  | CXM_TUNER_TV_SYSTEM_L | CXM_TUNER_TV_SYSTEM_L_PRIME,
+		  cxm_if_system_code_style,
+		  { { CXM_TUNER_TV_SYSTEM_BG,      { 0xd4, 0x70, 0x09 } },
+		    { CXM_TUNER_TV_SYSTEM_DK,      { 0xd4, 0x70, 0x0b } },
+		    { CXM_TUNER_TV_SYSTEM_I,       { 0xd4, 0x70, 0x0a } },
+		    { CXM_TUNER_TV_SYSTEM_L,       { 0xc4, 0x10, 0x0b } },
+		    { CXM_TUNER_TV_SYSTEM_L_PRIME, { 0x84, 0x10, 0x13 } },
+		    { CXM_TUNER_FM_SYSTEM,         { 0xdc, 0x10, 0x1d } } } },
+		45750, 855250,
+		{ { 464000, { 0x8e, 0x30 } },
+		  { 141000, { 0x8e, 0x90 } },
+		  { 45750, { 0x8e, 0xa0 } } },
+		87500, 108100,
+		{ 87500, { 0x88, 0xa4 } },
+		&l_air_channels },
+	{ "TCL 2002N-6A",
+		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
+		55250, 801250,
+		{ { 446000, { 0x8e, 0x08 } },
+		  { 170000, { 0x8e, 0x02 } },
+		  { 55250, { 0x8e, 0x01 } } },
+		0, 0,
+		{ 0 },
+		&us_cable_channels },
+#ifdef CXM_PVR150
+	{ "Samsung TCPN 2121P30A",
+		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
+		55250, 801250,
+		{ { 364500, { 0xce, 0x08 } },
+		  { 130000, { 0xce, 0x02 } },
+		  { 55250, { 0xce, 0x01 } } },
+		0, 0,
+		{ 0 },
+		&us_cable_channels }
+#endif
+};
+
+
+/* Read from the tuner registers */
+static int
+cxm_tuner_read( device_t iicbus, int i2c_addr, char *buf, int len )
+{
+	int received;
+
+	if (iicbus_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+	return received;
+
+fail:
+	iicbus_stop(iicbus);
+	return -1;
+}
+
+
+/* Write to the tuner registers */
+static int
+cxm_tuner_write( device_t iicbus, int i2c_addr, const char *buf, int len )
+{
+	int sent;
+
+	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_write(iicbus, buf, len, &sent, CXM_I2C_TIMEOUT) != 0)
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+	return sent;
+
+fail:
+	iicbus_stop(iicbus);
+	return -1;
+}
+
+
+int
+cxm_tuner_init( struct cxm_softc *sc )
+{
+	unsigned char status;
+	int tuner_type;
+
+	if (cxm_eeprom_init(sc) < 0)
+		return -1;
+
+	tuner_type = cxm_eeprom_tuner_type(sc);
+
+	if (tuner_type < 0 || tuner_type >= NUM_ELEMENTS(cxm_tuners))
+		return -1;
+
+	sc->tuner = &cxm_tuners[tuner_type];
+	sc->tuner_channels = sc->tuner->default_channels;
+	sc->tuner_freq = 0;
+
+	if (cxm_tuner_read(sc->iicbus, CXM_I2C_TUNER, &status, sizeof(status))
+	    != sizeof(status))
+		return -1;
+
+#ifdef CXM_PVR150
+	if (cxm_tuner_select_channel(sc, 3) < 0)
+#else
+	if (cxm_tuner_select_channel(sc, 4) < 0)
+#endif
+		return -1;
+
+	kprintf("%s: %s tuner\n", sc->name, sc->tuner->name);
+
+	return 0;
+}
+
+
+int
+cxm_tuner_select_channel_set( struct cxm_softc *sc, unsigned int channel_set )
+{
+	unsigned int i;
+
+	if (! channel_set) {
+		sc->tuner_channels = sc->tuner->default_channels;
+		return 0;
+	}
+
+	for (i = 0; i < NUM_ELEMENTS(channel_sets); i++)
+		if (channel_sets[i]->chnlset == channel_set)
+			break;
+
+	if (i >= NUM_ELEMENTS(channel_sets))
+		return -1;
+
+	if (! (sc->tuner->systems.supported & channel_sets[i]->system) )
+		return -1;
+
+	sc->tuner_channels = channel_sets[i];
+
+	return 0;
+}
+
+
+unsigned int
+cxm_tuner_selected_channel_set( struct cxm_softc *sc )
+{
+	return sc->tuner_channels->chnlset;
+}
+
+
+int
+cxm_tuner_select_frequency( struct cxm_softc *sc,
+			    enum cxm_tuner_freq_type freq_type,
+			    unsigned long freq )
+{
+	unsigned char msg[5];
+	unsigned char aux;
+	unsigned char pb;
+	unsigned int i;
+	unsigned int system;
+	unsigned int tuner_msg_len;
+	unsigned long N;
+	unsigned long osc_freq;
+	const struct cxm_tuner_band_code *band_code;
+	const struct cxm_tuner_system_code *system_code;
+
+	N = 0;
+	aux = 0;
+	pb = 0;
+
+	system_code = NULL;
+
+	tuner_msg_len = 4;
+
+	if (sc->tuner->systems.code_style != cxm_none_system_code_style) {
+		system = freq_type == cxm_tuner_fm_freq_type
+			 ? CXM_TUNER_FM_SYSTEM : sc->tuner_channels->system;
+
+		for (i = 0; i < NUM_ELEMENTS (sc->tuner->systems.codes); i++)
+			if (sc->tuner->systems.codes[i].system & system)
+				break;
+
+		if (i >= NUM_ELEMENTS (sc->tuner->systems.codes))
+			return -1;
+
+		switch (sc->tuner->systems.code_style) {
+		case cxm_port_system_code_style:
+			pb = sc->tuner->systems.codes[i].codes[0];
+			break;
+
+		case cxm_if_system_with_aux_code_style:
+			aux = sc->tuner->systems.codes[i].codes[3];
+			tuner_msg_len = 5;
+
+			/* FALLTHROUGH */
+
+		case cxm_if_system_code_style:
+			system_code = &sc->tuner->systems.codes[i];
+			break;
+
+		default:
+			return -1;
+		}
+	}
+
+	switch (freq_type) {
+	case cxm_tuner_fm_freq_type:
+
+		if (freq < sc->tuner->fm_min_freq
+		    || freq > sc->tuner->fm_max_freq
+		    || ! sc->tuner->fm_band_code.freq)
+			return -1;
+
+		/*
+		 * The Philips FM1216ME MK3 data sheet recommends
+		 * first setting the tuner to TV mode at a high
+		 * frequency (e.g. 150 MHz), then selecting the
+		 * desired FM station in order to ensure that the
+		 * tuning voltage does not stay locked at 0V.
+		 */
+
+		if (cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type,
+					       150000) < 0)
+			return -1;
+
+		/*
+		 * N = { fRF(pc) + fIF(pc) } / step_size
+		 *
+		 * fRF = RF frequency in MHz
+		 * fIF = Intermediate frequency in MHz (FM = 10.70 MHz)
+		 * step_size = Step size in MHz (FM = 50 kHz)
+		 */
+
+		osc_freq = freq + 10700;
+
+		N = (20 * osc_freq) / 1000;
+
+		msg[0] = (unsigned char)(N >> 8);
+		msg[1] = (unsigned char)N;
+		msg[2] = sc->tuner->fm_band_code.codes[0];
+		msg[3] = sc->tuner->fm_band_code.codes[1] | pb;
+		msg[4] = aux;
+		break;
+
+	case cxm_tuner_tv_freq_type:
+
+		if (freq < sc->tuner->min_freq
+		    || freq > sc->tuner->max_freq)
+			return -1;
+
+		/*
+		 * N = 16 * { fRF(pc) + fIF(pc) }
+		 *
+		 * fRF = RF frequency in MHz
+		 * fIF = Intermediate frequency in MHz
+		 *
+		 * The data sheet doesn't state it, however
+		 * this is probably the same equation as
+		 * FM simply with 62.5 kHz as the step size.
+		 */
+
+		osc_freq = freq + sc->tuner_channels->if_freq;
+
+		N = (16 * osc_freq) / 1000;
+
+		for (band_code = sc->tuner->band_codes;
+		     band_code->freq > freq; band_code++)
+			;
+
+		if (freq >= sc->tuner_freq) {
+#ifdef CXM_PVR150
+			msg[0] = (unsigned char)(N >> 8) & 0x7f;
+			msg[1] = (unsigned char)N & 0xff;
+#else
+			msg[0] = (unsigned char)(N >> 8);
+			msg[1] = (unsigned char)N;
+#endif
+			msg[2] = band_code->codes[0];
+			msg[3] = band_code->codes[1] | pb;
+		}
+		else {
+			msg[0] = band_code->codes[0];
+			msg[1] = band_code->codes[1] | pb;
+#ifdef CXM_PVR150
+			msg[2] = (unsigned char)(N >> 8) & 0x7f;
+			msg[3] = (unsigned char)N & 0xff;
+#else
+			msg[2] = (unsigned char)(N >> 8);
+			msg[3] = (unsigned char)N;
+#endif
+		}
+		msg[4] = aux;
+		break;
+
+	default:
+		return -1;
+	}
+
+	if (N > 32767)
+		return -1;
+
+	if (cxm_tuner_write(sc->iicbus, CXM_I2C_TUNER, msg, tuner_msg_len)
+			    != tuner_msg_len)
+		return -1;
+
+	/*
+	 * Program the IF processing after the tuner since some tuners
+	 * use the control byte to set the address of the IF.
+	 */
+
+#ifndef CXM_PVR150
+	if (system_code) {
+		msg[0] = 0x00;
+		msg[1] = system_code->codes[0];
+		msg[2] = system_code->codes[1];
+		msg[3] = system_code->codes[2];
+
+		if (cxm_tuner_write(sc->iicbus, CXM_I2C_TUNER_IF, msg, 4) != 4)
+			return -1;
+	}
+#endif
+
+	sc->tuner_freq = freq;
+
+	return 0;
+}
+
+
+int
+cxm_tuner_select_channel( struct cxm_softc *sc, unsigned int channel )
+{
+	unsigned long freq;
+	const struct cxm_tuner_channel_assignment *assignments;
+	const struct cxm_tuner_channels *channels;
+
+	channels = sc->tuner_channels;
+
+	if (! channels
+	    || channel < channels->min_channel
+	    || channel > channels->max_channel)
+		return -1;
+
+	for (assignments = channels->assignments;
+	     assignments->channel > channel; assignments++)
+		;
+
+	if (! assignments->freq)
+		return -1;
+
+	freq = assignments->freq
+	       + (channel - assignments->channel) * assignments->step;
+
+	return cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type, freq);
+}
+
+
+int
+cxm_tuner_apply_afc( struct cxm_softc *sc )
+{
+	unsigned int i;
+	int status;
+	unsigned long freq;
+	unsigned long max_offset;
+	unsigned long original_freq;
+	unsigned long prev_freq;
+	unsigned long step_size;
+
+	if (cxm_tuner_wait_for_lock(sc) != 1)
+		return -1;
+
+	original_freq = sc->tuner_freq;
+
+	freq = sc->tuner_freq;
+	prev_freq = 0;
+	max_offset = 2000;
+	step_size = 63;
+
+	for (i = 0; i < (max_offset / step_size); i++) {
+		status = cxm_tuner_status(sc);
+
+		if (status == -1)
+			break;
+
+		if (! (status & CXM_TUNER_PHASE_LOCKED) )
+			break;
+
+		switch (status & CXM_TUNER_AFC_MASK) {
+		case CXM_TUNER_AFC_FREQ_CENTERED:
+			return 0;
+
+		case CXM_TUNER_AFC_FREQ_MINUS_125:
+		case CXM_TUNER_AFC_FREQ_MINUS_62:
+			freq -= step_size;
+			break;
+
+		case CXM_TUNER_AFC_FREQ_PLUS_62:
+		case CXM_TUNER_AFC_FREQ_PLUS_125:
+			freq += step_size;
+			break;
+
+		default:
+			goto fail;
+		}
+
+		if (freq == prev_freq)
+			return 0;
+		prev_freq = sc->tuner_freq;
+
+		if (cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type,
+					       freq) < 0)
+			break;
+
+		/*
+		 * Delay long enough for the tuner to update its status.
+		 */
+
+		tsleep(&sc->iicbus, 0, "afc", hz / 10 );
+	}
+
+fail:
+	(void)cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type,
+					 original_freq);
+	return -1;
+}
+
+
+int
+cxm_tuner_is_locked( struct cxm_softc *sc )
+{
+	int status;
+
+	status = cxm_tuner_status(sc);
+
+	if (status == -1)
+		return -1;
+
+	return (status & CXM_TUNER_PHASE_LOCKED) ? 1 : 0;
+}
+
+
+int
+cxm_tuner_wait_for_lock( struct cxm_softc *sc )
+{
+	unsigned int i;
+
+	/*
+	 * The data sheet states the maximum lock-in time
+	 * is 150 ms using fast tuning ... unfortunately
+	 * it doesn't state the maximum lock-in time using
+	 * moderate tuning.  Hopefully 300 ms is enough.
+	 */
+
+	for (i = 0; i < 3; i++) {
+
+		/*
+		 * The frequency may have just changed (prior to
+		 * cxm_tuner_wait_for_lock) so start with the delay
+		 * to give the tuner a chance to update its status.
+		 */
+
+		tsleep(&sc->iicbus, 0, "tuner", hz / 10);
+
+		switch (cxm_tuner_is_locked(sc)) {
+		case 1:
+			return 1;
+
+		case 0:
+			break;
+
+		default:
+			return -1;
+		}
+	}
+
+	kprintf("%s: tuner failed to lock\n", sc->name);
+
+	return 0;
+}
+
+
+static const unsigned char afcmap[16] = {
+	CXM_TUNER_AFC_FREQ_CENTERED,
+	CXM_TUNER_AFC_FREQ_MINUS_62,
+	CXM_TUNER_AFC_FREQ_MINUS_62,
+	CXM_TUNER_AFC_FREQ_MINUS_62,
+	CXM_TUNER_AFC_FREQ_MINUS_125,
+	CXM_TUNER_AFC_FREQ_MINUS_125,
+	CXM_TUNER_AFC_FREQ_MINUS_125,
+	CXM_TUNER_AFC_FREQ_MINUS_125,
+	CXM_TUNER_AFC_FREQ_PLUS_125,
+	CXM_TUNER_AFC_FREQ_PLUS_125,
+	CXM_TUNER_AFC_FREQ_PLUS_125,
+	CXM_TUNER_AFC_FREQ_PLUS_125,
+	CXM_TUNER_AFC_FREQ_PLUS_62,
+	CXM_TUNER_AFC_FREQ_PLUS_62,
+	CXM_TUNER_AFC_FREQ_PLUS_62,
+	CXM_TUNER_AFC_FREQ_CENTERED
+};
+
+int
+cxm_tuner_status( struct cxm_softc *sc )
+{
+	unsigned char status;
+
+	if (cxm_tuner_read(sc->iicbus, CXM_I2C_TUNER, &status, sizeof(status))
+	    != sizeof(status))
+		return -1;
+
+	if (sc->tuner->systems.code_style == cxm_if_system_code_style
+	    || sc->tuner->systems.code_style
+	       == cxm_if_system_with_aux_code_style) {
+		unsigned char if_status;
+
+		if (cxm_tuner_read(sc->iicbus, CXM_I2C_TUNER_IF,
+				   &if_status, sizeof(if_status))
+		    != sizeof(if_status))
+			return -1;
+
+		status &= ~CXM_TUNER_AFC_MASK;
+		status |= afcmap[(if_status >> 1) & 0x0f];
+	}
+
+	return status;
+}
diff --git a/sys/dev/video/cxm/cxm_wm8775.c b/sys/dev/video/cxm/cxm_wm8775.c
new file mode 100644
index 0000000..60c3440
--- /dev/null
+++ b/sys/dev/video/cxm/cxm_wm8775.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2009
+ *	The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Sascha Wildner <swildner@dragonflybsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <sys/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/video/cxm/cxm.h>
+
+#include <bus/iicbus/iiconf.h>
+#include <bus/iicbus/iicbus.h>
+
+#include "iicbb_if.h"
+
+static const struct cxm_wm8775_command wm8775_init = {
+	13,
+	{
+		{ 0x17, { 0x00, 0x00 } },
+		{ 0x07, { 0x00, 0x00 } },
+		{ 0x0b, { 0x00, 0x21 } },
+		{ 0x0c, { 0x01, 0x02 } },
+		{ 0x0d, { 0x00, 0x00 } },
+		{ 0x0e,	{ 0x01, 0xd4 } },
+		{ 0x0f,	{ 0x01, 0xd4 } },
+		{ 0x10, { 0x01, 0xbf } },
+		{ 0x11, { 0x01, 0x85 } },
+		{ 0x12, { 0x00, 0xa2 } },
+		{ 0x13, { 0x00, 0x05 } },
+		{ 0x14, { 0x00, 0x7a } },
+		{ 0x15, { 0x01, 0x02 } },
+	}
+};
+
+static int
+cxm_wm8775_write(device_t iicbus, int i2c_addr, unsigned char addr,
+    const char *buf, int len)
+{
+	unsigned char msg[1];
+	int sent;
+
+	msg[0] = addr;
+
+	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
+		return -1;
+
+	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
+	    || sent != sizeof(msg))
+		goto fail;
+
+	if (iicbus_write(iicbus, buf, len, &sent, CXM_I2C_TIMEOUT) != 0)
+		goto fail;
+
+	iicbus_stop(iicbus);
+
+	return sent;
+
+fail:
+	iicbus_stop(iicbus);
+	return -1;
+}
+
+int
+cxm_wm8775_init(struct cxm_softc *sc)
+{
+	int i;
+
+	for (i = 0; i < wm8775_init.nsettings; i++)
+		if (cxm_wm8775_write(sc->iicbus, CXM_I2C_WM8775,
+			wm8775_init.settings[i].addr,
+			wm8775_init.settings[i].value,
+			sizeof(wm8775_init.settings[i].value))
+		    != sizeof(wm8775_init.settings[i].value))
+			return -1;
+	return 0;
+}
diff --git a/tools/multimedia/cxm/Makefile b/tools/multimedia/cxm/Makefile
new file mode 100644
index 0000000..681e00f
--- /dev/null
+++ b/tools/multimedia/cxm/Makefile
@@ -0,0 +1,3 @@
+SUBDIR=	extract_fw setchannel
+
+.include <bsd.subdir.mk>
diff --git a/tools/multimedia/cxm/extract_fw/Makefile b/tools/multimedia/cxm/extract_fw/Makefile
new file mode 100644
index 0000000..828ced5
--- /dev/null
+++ b/tools/multimedia/cxm/extract_fw/Makefile
@@ -0,0 +1,7 @@
+PROG=	cxm_extract_fw
+BINDIR=	/usr/local/sbin
+MAN=	cxm_extract_fw.8
+MANDIR=	/usr/local/man/man
+WARNS?=	6
+
+.include <bsd.prog.mk>
diff --git a/tools/multimedia/cxm/extract_fw/cxm_extract_fw.8 b/tools/multimedia/cxm/extract_fw/cxm_extract_fw.8
new file mode 100644
index 0000000..250194e
--- /dev/null
+++ b/tools/multimedia/cxm/extract_fw/cxm_extract_fw.8
@@ -0,0 +1,67 @@
+.\"
+.\" Copyright (c) 2009
+.\"	The DragonFly Project.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\" 3. Neither the name of The DragonFly Project nor the names of its
+.\"    contributors may be used to endorse or promote products derived
+.\"    from this software without specific, prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd February 21, 2009
+.Dt CXM_EXTRACT_FW 8
+.Os
+.Sh NAME
+.Nm cxm_extract_fw
+.Nd convert Windows\[rg] firmware for use with
+.Xr cxm 4
+.Sh SYNOPSIS
+.Nm
+.Ar file
+.Op Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility transforms
+.Tn Windows\[rg]
+firmware into C language files which are needed for compiling the
+.Xr cxm 4
+driver.
+.\" blabla
+.Sh AUTHORS
+.An -nosplit
+This program was made by
+.An John Wehle Aq john@feith.com .
+.Pp
+This manual page was written by
+.An Sascha Wildner Aq saw@online.de .
+.Sh SEE ALSO
+.Xr cxm 4
+.Sh HISTORY
+The
+.Nm
+program was ported to
+.Dx 2.3
+by
+.An Sascha Wildner .
diff --git a/tools/multimedia/cxm/extract_fw/cxm_extract_fw.c b/tools/multimedia/cxm/extract_fw/cxm_extract_fw.c
new file mode 100644
index 0000000..982280e
--- /dev/null
+++ b/tools/multimedia/cxm/extract_fw/cxm_extract_fw.c
@@ -0,0 +1,188 @@
+/*-
+ * Copyright (c) 2003
+ *	John Wehle <john@feith.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by John Wehle.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Conexant MPEG-2 Codec firmware extraction program.
+ *
+ * Generates cxm_dec_fw.c and cxm_enc_fw.c from the
+ * Hauppauge PVR-250 / PVR-350 Microsoft Windows driver
+ * (i.e. hcwpvrp2.sys).
+ *
+ * This was written using the invaluable information
+ * compiled by The IvyTV Project (ivtv.sourceforge.net).
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+const uint8_t decoder_magic[] = {
+	0xa7, 0x03, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+};
+const uint8_t encoder_magic[] = {
+	0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+};
+
+static int
+save_firmware(const char *name, const uint8_t *buf, size_t nbytes)
+{
+	FILE *ofp;
+	char outfile[MAXPATHLEN];
+	size_t i;
+
+	if (nbytes > (256 * 1024))
+		nbytes = 256 * 1024;
+
+	if ((size_t)snprintf(outfile, sizeof(outfile), "%s.c", name) >=
+	    sizeof(outfile))
+		errx(1, "save_firmware -- firmware name is too long");
+
+	if (!(ofp = fopen(outfile, "w")))
+		err(1, "save_firmware -- can't open output file <%s>",
+		    outfile);
+
+	fprintf(ofp, "#include <sys/types.h>\n"
+	    "\n"
+	    "const uint8_t %s[] __attribute__ ((aligned(4))) = {",
+	    name);
+
+	for (i = 0; i < nbytes; i++) {
+		if (i)
+			fputc(',', ofp);
+		if ((i % 8) == 0)
+			fputs("\n\t", ofp);
+		else
+			fputc(' ', ofp);
+		fprintf(ofp, "0x%.2x", buf[i]);
+	}
+
+	fprintf(ofp, "\n};\n");
+
+	if (ferror(ofp)) {
+		fclose(ofp);
+		return -1;
+	}
+
+	fclose(ofp);
+	return 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+	uint8_t *end;
+	uint8_t *ptr;
+	uint8_t *start;
+	int decoder_fw_saved = 0;
+	int encoder_fw_saved = 0;
+	int fd, i;
+	struct stat statbuf;
+
+	if (argc < 2 || argc > 3) {
+		fprintf(stderr, "usage: cxm_extract_fw file [file]\n");
+		exit(1);
+	}
+
+	for (i = 1; i <= (argc - 1); i++) {
+		/*
+		 * Open the file.
+		 */
+		if ((fd = open(argv[i], O_RDONLY)) < 0)
+		err(1, "can't open %s for reading\n", argv[i]);
+
+		/*
+		 * Determine how big it is.
+		 */
+		if (fstat(fd, &statbuf) < 0) {
+			close(fd);
+			err(1, "can't fstat %s\n", argv[i]);
+		}
+
+		/*
+		 * Map it into memory.
+		 */
+		if (!(start = (uint8_t *)mmap(NULL, (size_t) statbuf.st_size,
+			    PROT_READ, MAP_SHARED, fd, (off_t) 0))) {
+			close(fd);
+			err(1, "can't mmap %s\n", argv[i]);
+		}
+		end = start + statbuf.st_size;
+
+		close(fd);
+
+		if (statbuf.st_size > 100000) {
+			for (ptr = start; ptr != end; ptr++) {
+				if ((size_t)(end - ptr) >= sizeof(decoder_magic) &&
+				    memcmp(ptr, decoder_magic, sizeof(decoder_magic)) == 0) {
+					if (!decoder_fw_saved) {
+						if (save_firmware("cxm_dec_fw", ptr, end - ptr) < 0)
+							errx(1, "save_firmware failed\n");
+						decoder_fw_saved = 1;
+					} else {
+						errx(1, "multiple decoder images present\n");
+					}
+				}
+				if ((size_t)(end - ptr) >= sizeof(encoder_magic) &&
+				    memcmp(ptr, encoder_magic, sizeof(encoder_magic)) == 0) {
+					if (!encoder_fw_saved) {
+						if (save_firmware("cxm_enc_fw", ptr, end - ptr) < 0)
+							errx(1, "save_firmware failed\n");
+						encoder_fw_saved = 1;
+					} else {
+						errx(1, "multiple encoder images present\n");
+					}
+				}
+			}
+		} else {
+			save_firmware ("cxm_cx2584x_fw", start, statbuf.st_size);
+		}
+
+		munmap((caddr_t)start, (size_t)statbuf.st_size);
+
+		if (!decoder_fw_saved)
+			errx(1, "decoder image not present\n");
+
+		if (!encoder_fw_saved)
+			errx(1, "encoder image not present\n");
+
+		if (!decoder_fw_saved || !encoder_fw_saved)
+			exit(1);
+	}
+
+	exit(0);
+}
diff --git a/tools/multimedia/cxm/setchannel/Makefile b/tools/multimedia/cxm/setchannel/Makefile
new file mode 100644
index 0000000..91faadb
--- /dev/null
+++ b/tools/multimedia/cxm/setchannel/Makefile
@@ -0,0 +1,6 @@
+PROG=	cxm_setchannel
+BINDIR=	/usr/local/bin
+MANDIR=	/usr/local/man/man
+WARNS?=	6
+
+.include <bsd.prog.mk>
diff --git a/tools/multimedia/cxm/setchannel/cxm_setchannel.1 b/tools/multimedia/cxm/setchannel/cxm_setchannel.1
new file mode 100644
index 0000000..30effd9
--- /dev/null
+++ b/tools/multimedia/cxm/setchannel/cxm_setchannel.1
@@ -0,0 +1,83 @@
+.\"
+.\" Copyright (c) 2009
+.\"	The DragonFly Project.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in
+.\"    the documentation and/or other materials provided with the
+.\"    distribution.
+.\" 3. Neither the name of The DragonFly Project nor the names of its
+.\"    contributors may be used to endorse or promote products derived
+.\"    from this software without specific, prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: cxm.4,v 1.1 2004/10/16 00:12:35 mavetju Exp $
+.\"
+.Dd January 13, 2009
+.Dt CXM_SETCHANNEL 1
+.Os
+.Sh NAME
+.Nm cxm_setchannel
+.Nd Hauppauge channel selector
+.Sh SYNOPSIS
+.Nm
+.Op Fl a Ar on | off
+.Oo
+.Fl c | r | s | t
+.Oc
+.Op Fl d Ar unit
+.Op Fl g Ar geom
+.Op Fl m Ar chnl_set
+.Op Ar chnl | freq
+.Sh DESCRIPTION
+.Nm
+provides support for selecting channels on the PVRxxx via
+.Pa /dev/cxm0
+(unit is selectable with
+.Fl d ) .
+.Pp
+Run
+.Nm
+without options to see an overview and
+description of the available command line options.
+.Sh AUTHORS
+.An -nosplit
+This program was made by
+.An John Wehle Aq john@feith.com .
+.Pp
+This manual page was made by
+.An Edwin Groothuis Aq edwin@FreeBSD.org .
+.Sh SEE ALSO
+.Xr bktr 4 ,
+.Xr cxm 4 ,
+.Xr meteor 4
+.Sh HISTORY
+The
+.Nm
+program first appeared in the -multimedia mailing-list in January 2004.
+The
+.Nm
+program first appeared in the FreeBSD Ports collection in October 2004.
+.Pp
+It was ported to
+.Dx 2.3
+by
+.An Sascha Wildner .
diff --git a/tools/multimedia/cxm/setchannel/cxm_setchannel.c b/tools/multimedia/cxm/setchannel/cxm_setchannel.c
new file mode 100644
index 0000000..311b913
--- /dev/null
+++ b/tools/multimedia/cxm/setchannel/cxm_setchannel.c
@@ -0,0 +1,283 @@
+/*-
+ * Copyright (c) 2003, 2004, 2005
+ *	John Wehle <john@feith.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by John Wehle.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Set the channel of the tuner card.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <dev/video/meteor/ioctl_meteor.h>
+#include <dev/video/bktr/ioctl_bt848.h>
+
+static void
+usage(void)
+{
+	fprintf(stderr,
+	    "Usage: setchannel [-a {on|off}] [-c | -r | -s | -t] [-g geom] [-m chnl_set] [chnl | freq]\n"
+	    "  -a    Enable / disable AFC.\n"
+	    "  -c    Select composite input.\n"
+	    "  -d    Select unit ( 0..x ). \n"
+	    "  -r    Select radio input.\n"
+	    "  -s    Select svideo input.\n"
+	    "  -t    Select tuner.\n"
+	    "  -g    Select geometry.\n"
+	    "          352x240 or 352x288 = VCD\n"
+	    "          480x480 or 480x576 = SVCD\n"
+	    "          352x480 or 352x576 = DVD (half D1)\n"
+	    "          720x480 or 720x576 = DVD (full D1)\n"
+	    "  -m    Select channel set / system.\n"
+	    "          0 = Tuner Default\n"
+	    "          %u = US Broadcast / NTSC\n"
+	    "          %u = US Cable / NTSC\n"
+	    "          %u = Western Europe / PAL\n"
+	    "          %u = Japan Broadcast / NTSC\n"
+	    "          %u = Japan Cable / NTSC\n"
+	    "          %u = Australia / PAL\n"
+	    "          %u = France / SECAM\n"
+	    "  chnl  Channel\n"
+	    "  freq  Frequency in MHz (must include decimal point).\n",
+	    CHNLSET_NABCST, CHNLSET_CABLEIRC,
+	    CHNLSET_WEUROPE, CHNLSET_JPNBCST, CHNLSET_JPNCABLE,
+	    CHNLSET_AUSTRALIA, CHNLSET_FRANCE);
+}
+
+int
+main(int argc, char *argv[])
+{
+	char *ptr;
+	char *endptr;
+	char buf[255];
+	int afc;
+	int audio;
+	int c;
+	int channel_set;
+	int i;
+	int status;
+	int tfd;
+	int unit;
+	unsigned int channel;
+	unsigned int fraction;
+	unsigned int freq;
+	unsigned int x_size;
+	unsigned int y_size;
+	unsigned long device;
+	struct bktr_capture_area cap;
+
+	afc = -1;
+	audio = -1;
+	channel = 0;
+	channel_set = -1;
+	device = 0;
+	unit = 0;
+	freq = 0;
+	status = 0;
+	x_size = 0;
+	y_size = 0;
+
+	while ((c = getopt(argc, argv, "a:crstg:m:d:")) != -1)
+		switch (c) {
+		case 'a':
+			if (strcasecmp(optarg, "on") == 0)
+				afc = 1;
+			else if (strcasecmp(optarg, "off") == 0)
+				afc = 0;
+			else {
+				usage();
+				exit(1);
+			}
+			break;
+
+		case 'c':
+			device = METEOR_INPUT_DEV2;
+			audio = -1;
+			break;
+
+		case 'd':
+			unit = atoi(optarg);
+			break;
+
+		case 'r':
+			device = 0;
+			audio = AUDIO_INTERN;
+			break;
+
+		case 's':
+			device = METEOR_INPUT_DEV_SVIDEO;
+			audio = -1;
+			break;
+
+		case 't':
+			device = METEOR_INPUT_DEV1;
+			audio = -1;
+			break;
+
+		case 'g':
+			if (sscanf(optarg, "%ux%u", &x_size, &y_size) != 2 ||
+			    x_size == 0 || y_size == 0) {
+				usage();
+				exit(1);
+			}
+			break;
+
+		case 'm':
+			channel_set = atoi(optarg);
+			if (channel_set < 0 || channel_set > CHNLSET_MAX) {
+				usage();
+				exit(1);
+			}
+			break;
+
+		default:
+			usage();
+			exit(1);
+		}
+
+	if (optind < argc) {
+
+		/*
+		 * A number containing a decimal point is the frequency in
+		 * MHz.
+		 */
+
+		if ((ptr = strchr(argv[optind], '.')) != NULL) {
+			freq = strtol(argv[optind], &endptr, 10) * 1000;
+			if (ptr != endptr) {
+				usage();
+				exit(1);
+			}
+			ptr++;
+
+			fraction = strtol(ptr, &endptr, 10);
+			if (!isdigit(*ptr) || *endptr != '\0') {
+				usage();
+				exit(1);
+			}
+			for (i = endptr - ptr; i > 3; i--)
+				fraction /= 10;
+			for (; i < 3; i++)
+				fraction *= 10;
+
+			freq += fraction;
+		}
+		/*
+		 * An integer is the channel.
+		 */
+
+		else
+			channel = atoi(argv[optind]);
+	}
+	if (afc == -1 && audio == -1 && !device && x_size == 0 &&
+	    y_size == 0 && channel_set == -1 && !channel && !freq) {
+		usage();
+		exit(1);
+	}
+
+	sprintf(buf, "/dev/cxm%d", unit);
+
+	tfd = open(buf, O_RDONLY);
+	if (tfd < 0) {
+		warn("open() of /dev/cxm... failed.");
+		exit(1);
+	}
+
+	if (afc != -1) {
+		if (ioctl(tfd, TVTUNER_SETAFC, &afc) < 0) {
+			warn("ioctl( tfd, TVTUNER_SETAFC ) failed.");
+			status = 1;
+		}
+	}
+	if (device) {
+		if (ioctl(tfd, METEORSINPUT, &device) < 0) {
+			warn("ioctl( tfd, METEORSINPUT ) failed.");
+			status = 1;
+		}
+	}
+	if (audio != -1) {
+		if (ioctl(tfd, BT848_SAUDIO, &audio) < 0) {
+			warn("ioctl( tfd, BT848_SAUDIO ) failed.");
+			status = 1;
+		}
+	}
+	if (ioctl(tfd, BT848_GAUDIO, &audio) < 0) {
+		warn("ioctl( tfd, BT848_GAUDIO ) failed.");
+		status = 1;
+	}
+	if (x_size && y_size) {
+		memset(&cap, 0, sizeof(cap));
+		cap.x_size = x_size;
+		cap.y_size = y_size;
+		if (ioctl(tfd, BT848_SCAPAREA, &cap) < 0) {
+			warn("ioctl( tfd, BT848_SCAPAREA ) failed.");
+			status = 1;
+		}
+	}
+	if (channel_set != -1) {
+		if (ioctl(tfd, TVTUNER_SETTYPE, &channel_set) < 0) {
+			warn("ioctl( tfd, TVTUNER_SETTYPE ) failed.");
+			status = 1;
+		}
+	}
+	if (channel) {
+		if (ioctl(tfd, TVTUNER_SETCHNL, &channel) < 0) {
+			warn("ioctl( tfd, TVTUNER_SETCHNL ) failed.");
+			status = 1;
+		}
+	} else if (freq) {
+		if (audio == AUDIO_INTERN) {
+			/* Convert from kHz to MHz * 100 */
+			freq = freq / 10;
+
+			if (ioctl(tfd, RADIO_SETFREQ, &freq) < 0) {
+				warn("ioctl( tfd, RADIO_SETFREQ ) failed.");
+				status = 1;
+			}
+		} else {
+			/* Convert from kHz to MHz * 16 */
+			freq = (freq * 16) / 1000;
+
+			if (ioctl(tfd, TVTUNER_SETFREQ, &freq) < 0) {
+				warn("ioctl( tfd, TVTUNER_SETFREQ ) failed.");
+				status = 1;
+			}
+		}
+	}
+	close(tfd);
+	exit(status);
+}
